我正在用Symfony3构建一个REST-API。
作为示例,以下是使用FormBuilderInterface制作的表单中Price的API字段。下面的代码示例为ApiBundle/Form/PriceType.php
class PriceType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, array(
'description' => 'Name',
))
->add('price_category', EntityPublicKeyTextType::class, array(
'class' => 'MyCustomBundle:PriceCategory',
'property_path' => 'priceCategory',
))
问题是关于具有以下内容的字段的良好响应消息:验证错误。对于默认的symfony类型(例如IntegerType,TextType),它可以自动找到property_path并向我发出有用的错误消息。这是API响应,但有两个错误:
name
可以很好地解决(因为我看到了它涉及的领域,
price_category
,它无法解决(第二条消息)。 {
"name": [
"This value is too long. It should have 50 characters or less."
],
"0": "This value should not be null."
}
解决问题。我为price_category字段添加了'property_path' => 'priceCategory'
。 property_path的值与其中定义了变量protected $priceCategory;
的BaseBundle / Entity / Price.php匹配。
添加property_path
后,错误消息看起来还不错。
{
"name": [
"This value is too long. It should have 50 characters or less."
],
"price_category": [
"This value should not be null."
]
}
price_category的类是EntityPublicKeyTextType
,它是从TextType中抽象出来的(它可以执行错误操作)。
因此,我有以下问题:为避免手动为所有字段添加property_path,我必须向继承的类EntityPublicKeyTextType中添加什么?
任何解决此问题的提示都非常欢迎
最佳内线
编辑:
EntityPublicKeyTextType:
class EntityPublicKeyTextType extends AbstractType
{
/**
* @var ObjectManager
*/
private $om;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new ObjectToPublicKeyTransformer(
$this->om,
$options['class'],
$options['public_key'],
$options['remove_whitespaces'],
$options['multiple'],
$options['string_separator'],
$options['extra_find_by']
);
$builder->addModelTransformer($transformer);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setRequired(array(
'class',
'public_key'
))
->setDefaults(array(
'multiple' => false,
'string_separator' => false,
'extra_find_by' => array(),
'remove_whitespaces' => true,
));
}
public function getParent()
{
return TextType::class;
}
public function getBlockPrefix()
{
return 'entity_public_key_text';
}
}
ObjectToPublicKeyTransformer:
class ObjectToPublicKeyTransformer implements DataTransformerInterface
{
/**
* @var PropertyAccessorInterface
*/
private $propertyAccessor;
/**
* @var ObjectManager
*/
private $om;
/**
* @var string
*/
private $class;
/**
* @var string|string[]
*/
private $publicKey;
/**
* @var bool
*/
private $removeWhitespaces;
/**
* @var boolean
*/
private $multiple;
/**
* @var boolean|string
*/
private $stringSeparator;
/**
* @var array
*/
private $extraFindBy;
public function __construct(
ObjectManager $om,
string $class,
$publicKey,
bool $removeWhitespaces,
bool $multiple = false,
$stringSeparator = false,
array $extraFindBy = array(),
PropertyAccessorInterface $propertyAccessor = null
) {
$this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
$this->om = $om;
$classMetadata = $om->getClassMetadata($class);
$this->class = $classMetadata->getName();
$this->publicKey = $publicKey;
$this->stringSeparator = $stringSeparator;
$this->multiple = $multiple;
$this->extraFindBy = $extraFindBy;
$this->removeWhitespaces = $removeWhitespaces;
}
/**
* Transforms an object / Collection of objects to a publicKey string / array of publicKey strings.
*
* @param object|Collection $object
* @return string|array
*/
public function transform($object)
{
if (null == $object) {
return null;
}
if (is_array($this->publicKey)) {
$publicKey = $this->publicKey[0];
} else {
$publicKey = $this->publicKey;
}
if ($this->multiple) {
if ($object instanceof Collection) {
$values = array();
foreach ($object as $objectItem) {
$values[] = (string)$this->propertyAccessor->getValue($objectItem, $publicKey);
}
if ($this->stringSeparator) {
return implode($this->stringSeparator, $values);
}
return $values;
}
} else {
return (string)$this->propertyAccessor->getValue($object, $publicKey);
}
}
/**
* Transforms an publicKey string / array of public key strings to an object / Collection of objects.
*
* @param string|array $value
* @return object|Collection
*
* @throws TransformationFailedException if object is not found.
*/
public function reverseTransform($value)
{
if (null === $value) {
return $this->multiple ? new ArrayCollection() : null;
}
if (is_array($this->publicKey)) {
$publicKeys = $this->publicKey;
} else {
$publicKeys = array($this->publicKey);
}
if ($this->multiple) {
if ($this->stringSeparator) {
$value = explode($this->stringSeparator, $value);
}
if (is_array($value)) {
$objects = new ArrayCollection();
foreach ($value as $valueItem) {
foreach ($publicKeys as $publicKey) {
$object = $this->findObject($valueItem, $publicKey);
if ($object instanceof $this->class) {
$objects->add($object);
break;
}
}
}
return $objects;
}
}
foreach ($publicKeys as $publicKey) {
$object = $this->findObject($value, $publicKey);
if ($object instanceof $this->class) {
return $object;
}
}
return $this->multiple ? new ArrayCollection() : null;
}
private function findObject($value, $publicKey)
{
if ($this->removeWhitespaces) {
$value = str_replace(' ', '', $value);
}
$findBy = array_merge([$publicKey => $value], $this->extraFindBy);
$object = $this->om->getRepository($this->class)->findOneBy($findBy);
return $object;
}
}
答案 0 :(得分:0)
如果您还提供Price
模型/实体类,这将很有用。似乎您在模型(priceCategory
)中使用驼峰大小写作为属性名称,然后在表单(price_category
)中使用蛇形大小写。
如果您对模型和表单使用相同的约定,则验证错误将自动映射到正确的属性。
解释是,Symfony的映射器仍然可以通过将蛇转换为驼峰式大小写,反之亦然来映射您的字段,这就是为什么即使不使用property_path
选项,表单仍然可以工作并提交值的原因。但是问题是验证器不执行此映射,并且不能匹配正确的属性(price_category-> priceCategory)。