对于某些(但不是全部)表单提交得到 "Could not determine access type for property "id"。"
for some, but not all, form submission got "Could not determine access type for property "id"."
我有一个映射到 Entity
集合的复杂表单,它允许以给定的价格为一个活动购买门票,对于它适用的大多数活动,但对于其中一个,我们以可重复的方式得到错误 Could not determine access type for property "id"
在这种情况下,我知道 Could not determine access type for property X
是因为缺少 setter。确实没有 setId()
方法并且 id
是 protected
,但我认为 symfony 不应该首先尝试设置 id(因为它适用于其他形式,即门票是购买,并正确出现在活动中 link 等)
所以我的问题是 为什么在某些情况下 symfony 需要 setId()
我有以下实体
class OrderFront
{
use Traits\HasId;
/**
* List of pricings set on this event
*
* @Assert\Valid()
* @ORM\OneToMany(
* targetEntity="QuantityByPricing",
* mappedBy="orderFront",
* cascade={"persist", "remove"}
* )
*/
private $quantitiesByPricing;
/**
* @Assert\NotBlank()
* @Assert\NotNull()
*/
public $occurenceId;
public function getQuantitiesByPricing(): Collection
{
return $this->quantitiesByPricing;
}
public function addQuantitiesByPricing(QuantityByPricing $quantityByPricing)
{
$quantityByPricing->setOrderFront($this);
$this->quantitiesByPricing[] = $quantityByPricing;
return $this;
}
}
class QuantityByPricing
{
use Traits\HasId;
/**
* @var int
* @ORM\Column(type="integer")
*/
public $quantity = 0;
/**
* The pricing of this ticket
*
* @var Pricing
*
* @ORM\ManyToOne(targetEntity="Pricing")
* @ORM\JoinColumn(
* name="pricing_id",
* nullable=false,
* referencedColumnName="id"
* )
*/
public $pricing;
}
确实 "HasId" 特征没有 setter(但这是故意的)或者至少到现在为止它从来都不是问题
trait HasId
{
/**
*
* @ORM\GeneratedValue(strategy="UUID")
* @ORM\Column(name="id", type="guid") }
* @ORM\Id
*
* @Assert\Uuid()
*/
private $id;
/**
* Get id
*
* @return guid
*/
public function getId()
{
return $this->id;
}
}
和表格
class OrderType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'quantitiesByPricing',
CollectionType::class,
['entry_type' => QuantityByPricingType::class]
)
->add('occurenceId', HiddenType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => 'AppBundle\Entity\OrderFront']);
}
}
/**
* sub-form to buy X tickets of a given pricing
*/
class QuantityByPricingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('quantity')
->add('pricing',HiddenPricingType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => 'AppBundle\Entity\QuantityByPricing']);
}
}
/**
*
*/
class HiddenPricingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('id', HiddenType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => 'AppBundle\Entity\Pricing']);
}
}
class Pricing
{
use Traits\HasId;
}
创建表单的控制器如下所示
// call the DB to set the possible pricings
// inside it calls addQuantityByPricing
$orderFront = $this->_createOrderFront();
$form = $this->createForm(OrderType::class, $orderFront);
$form->handleRequest($request);
异常回溯如下
Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException:
Could not determine access type for property "id".
at vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php:652
at Symfony\Component\PropertyAccess\PropertyAccessor->writeProperty(array(object(Pricing), object(Pricing)), 'id', '80424145-ca68-4dce-b4f0-644a423d3aad')
当添加一些 debug
时,我可以看到 2 Pricing
是
array:2 [▼
0 => Pricing {#1375 ▶} # id "d82cafb8-432b-4e20-ac9f-66e48dc55458"
1 => & Pricing {#1375 ▶} # id "d82cafb8-432b-4e20-ac9f-66e48dc55458"
]
所以 symfony 似乎试图用另一个 valid/existing 覆盖此定价的 ID(这是一个 valid/existing 一个),我认为这就是它尝试的原因"replace" 它,通过尝试调用 setter 但失败了,但为什么要这样做呢?
编辑:
经过更多的调试,我发现了一个令人不安的巧合:
d82cafb8-432b-4e20-ac9f-66e48dc55458
是我在 addQuantitiesByPricing
方法中添加一些调试时添加的第一个 id
80424145-ca68-4dce-b4f0-644a423d3aad
是提交表单中索引为 0 的定价 ID
Edit2:我的控制器创建表单的方式(调用 addQuantitiesByPricing
使 d82cafb8-432b-4e20-ac9f-66e48dc55458
先出现的方式),使我们首先从数据库中检索这些 ID,然后再获取发布的 ID
问题是在创建表单的主要实体(即 OrderFront
)时使用 addQuantitiesByPricing
添加元素的顺序与在HTML(有一个 sort
隐藏在 twig 文件中)
所以 QuantityByPricing
的 POSTed 数组与数据库创建的 QuantityByPricing
数组的顺序不同,所以当 Symfony 施展魔法来查看哪个字段需要更新时,它看到我的数组的第一个元素与 POSTed 数组的第一个元素有不同的 id
,然后尝试通过首先搜索它的 setter 来替换它,所以 setId()
因此异常
我之前没有看到这个错误的原因是因为一些(不)幸运的原因来自数据库的数组已经排序,所以树枝排序什么都不做
注意:与此特定上下文没有直接关系,但如果发布的数组的大小小于数据库数组的大小,您将获得相同的堆栈跟踪(除了最后一个参数将是 null
)
Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException:
Could not determine access type for property "id".
at vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php:652
at Symfony\Component\PropertyAccess\PropertyAccessor->writeProperty(array(object(Pricing), object(Pricing)), 'id', null)
note2:另一个可行的解决方案是在提交数组之前使用我的 OrderType
中的 PRE_SUBMIT
事件重新排序数组
->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) {
$submittedData = $event->getData();
if (!array_key_exists('quantitiesByPricing', $submittedData)) {
return;
}
$form = $event->getForm();
dump($submittedData['quantitiesByPricing']);
//Re-index the array to ensure the forms
// stay in the submitted order.
$submittedData['quantitiesByPricing'] = /* some code to reorder the submitted form in the same order as the data from the database */
$event->setData($submittedData);
}
)
我有一个映射到 Entity
集合的复杂表单,它允许以给定的价格为一个活动购买门票,对于它适用的大多数活动,但对于其中一个,我们以可重复的方式得到错误 Could not determine access type for property "id"
在这种情况下,我知道 Could not determine access type for property X
是因为缺少 setter。确实没有 setId()
方法并且 id
是 protected
,但我认为 symfony 不应该首先尝试设置 id(因为它适用于其他形式,即门票是购买,并正确出现在活动中 link 等)
所以我的问题是 为什么在某些情况下 symfony 需要 setId()
我有以下实体
class OrderFront
{
use Traits\HasId;
/**
* List of pricings set on this event
*
* @Assert\Valid()
* @ORM\OneToMany(
* targetEntity="QuantityByPricing",
* mappedBy="orderFront",
* cascade={"persist", "remove"}
* )
*/
private $quantitiesByPricing;
/**
* @Assert\NotBlank()
* @Assert\NotNull()
*/
public $occurenceId;
public function getQuantitiesByPricing(): Collection
{
return $this->quantitiesByPricing;
}
public function addQuantitiesByPricing(QuantityByPricing $quantityByPricing)
{
$quantityByPricing->setOrderFront($this);
$this->quantitiesByPricing[] = $quantityByPricing;
return $this;
}
}
class QuantityByPricing
{
use Traits\HasId;
/**
* @var int
* @ORM\Column(type="integer")
*/
public $quantity = 0;
/**
* The pricing of this ticket
*
* @var Pricing
*
* @ORM\ManyToOne(targetEntity="Pricing")
* @ORM\JoinColumn(
* name="pricing_id",
* nullable=false,
* referencedColumnName="id"
* )
*/
public $pricing;
}
确实 "HasId" 特征没有 setter(但这是故意的)或者至少到现在为止它从来都不是问题
trait HasId
{
/**
*
* @ORM\GeneratedValue(strategy="UUID")
* @ORM\Column(name="id", type="guid") }
* @ORM\Id
*
* @Assert\Uuid()
*/
private $id;
/**
* Get id
*
* @return guid
*/
public function getId()
{
return $this->id;
}
}
和表格
class OrderType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'quantitiesByPricing',
CollectionType::class,
['entry_type' => QuantityByPricingType::class]
)
->add('occurenceId', HiddenType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => 'AppBundle\Entity\OrderFront']);
}
}
/**
* sub-form to buy X tickets of a given pricing
*/
class QuantityByPricingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('quantity')
->add('pricing',HiddenPricingType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => 'AppBundle\Entity\QuantityByPricing']);
}
}
/**
*
*/
class HiddenPricingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('id', HiddenType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['data_class' => 'AppBundle\Entity\Pricing']);
}
}
class Pricing
{
use Traits\HasId;
}
创建表单的控制器如下所示
// call the DB to set the possible pricings
// inside it calls addQuantityByPricing
$orderFront = $this->_createOrderFront();
$form = $this->createForm(OrderType::class, $orderFront);
$form->handleRequest($request);
异常回溯如下
Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException:
Could not determine access type for property "id".
at vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php:652
at Symfony\Component\PropertyAccess\PropertyAccessor->writeProperty(array(object(Pricing), object(Pricing)), 'id', '80424145-ca68-4dce-b4f0-644a423d3aad')
当添加一些 debug
时,我可以看到 2 Pricing
是
array:2 [▼
0 => Pricing {#1375 ▶} # id "d82cafb8-432b-4e20-ac9f-66e48dc55458"
1 => & Pricing {#1375 ▶} # id "d82cafb8-432b-4e20-ac9f-66e48dc55458"
]
所以 symfony 似乎试图用另一个 valid/existing 覆盖此定价的 ID(这是一个 valid/existing 一个),我认为这就是它尝试的原因"replace" 它,通过尝试调用 setter 但失败了,但为什么要这样做呢?
编辑:
经过更多的调试,我发现了一个令人不安的巧合:
d82cafb8-432b-4e20-ac9f-66e48dc55458
是我在addQuantitiesByPricing
方法中添加一些调试时添加的第一个 id80424145-ca68-4dce-b4f0-644a423d3aad
是提交表单中索引为 0 的定价 ID
Edit2:我的控制器创建表单的方式(调用 addQuantitiesByPricing
使 d82cafb8-432b-4e20-ac9f-66e48dc55458
先出现的方式),使我们首先从数据库中检索这些 ID,然后再获取发布的 ID
问题是在创建表单的主要实体(即 OrderFront
)时使用 addQuantitiesByPricing
添加元素的顺序与在HTML(有一个 sort
隐藏在 twig 文件中)
所以 QuantityByPricing
的 POSTed 数组与数据库创建的 QuantityByPricing
数组的顺序不同,所以当 Symfony 施展魔法来查看哪个字段需要更新时,它看到我的数组的第一个元素与 POSTed 数组的第一个元素有不同的 id
,然后尝试通过首先搜索它的 setter 来替换它,所以 setId()
因此异常
我之前没有看到这个错误的原因是因为一些(不)幸运的原因来自数据库的数组已经排序,所以树枝排序什么都不做
注意:与此特定上下文没有直接关系,但如果发布的数组的大小小于数据库数组的大小,您将获得相同的堆栈跟踪(除了最后一个参数将是 null
)
Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException:
Could not determine access type for property "id".
at vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php:652
at Symfony\Component\PropertyAccess\PropertyAccessor->writeProperty(array(object(Pricing), object(Pricing)), 'id', null)
note2:另一个可行的解决方案是在提交数组之前使用我的 OrderType
PRE_SUBMIT
事件重新排序数组
->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) {
$submittedData = $event->getData();
if (!array_key_exists('quantitiesByPricing', $submittedData)) {
return;
}
$form = $event->getForm();
dump($submittedData['quantitiesByPricing']);
//Re-index the array to ensure the forms
// stay in the submitted order.
$submittedData['quantitiesByPricing'] = /* some code to reorder the submitted form in the same order as the data from the database */
$event->setData($submittedData);
}
)