1

我有一个复杂的表格,它映射到一个集合Entity,它允许以给定的价格购买一个活动的门票,对于大多数活动它都有效,但是对于其中一个活动,我们以可重复的方式得到了错误Could not determine access type for property "id"

在这种情况下,我知道这Could not determine access type for property X是因为缺少 setter。确实没有setId()方法和idis protected,但我认为 symfony 不应该首先尝试设置 id (因为它适用于其他形式,即购买门票,并正确显示与活动的链接等)

所以我的问题是为什么在某些情况下 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”特性没有设置器(但它是故意的),或者至少直到现在它从来都不是问题

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,我可以看到 2Pricing

array:2 [▼
  0 => Pricing {#1375 ▶}   # id  "d82cafb8-432b-4e20-ac9f-66e48dc55458"
  1 => & Pricing {#1375 ▶} # id  "d82cafb8-432b-4e20-ac9f-66e48dc55458"
]

因此,symfony 似乎正试图用另一个有效/现有的定价来覆盖这个定价的 id(这是一个有效/现有的),我认为这就是它试图通过尝试调用来“替换”它的原因setter 失败了,但为什么会这样呢?

编辑:

经过更多调试后,我发现了一个令人不安的巧合:

  • d82cafb8-432b-4e20-ac9f-66e48dc55458addQuantitiesByPricing是我在方法中添加一些调试时添加的第一个 id
  • 80424145-ca68-4dce-b4f0-644a423d3aad是提交表单中索引为 0 的定价 ID

Edit2:我的控制器创建表单的方式(并且是调用addQuantitiesByPricing制作d82cafb8-432b-4e20-ac9f-66e48dc55458首先出现的表单),使我们首先从数据库中检索这些 Id,然后再将 Ids POSTed

4

1 回答 1

0

addQuantitiesByPricing问题是在创建表单的主要实体(即)时添加元素的顺序OrderFront与在 HTML 中添加元素的顺序不同(sort在树枝文件中隐藏了一个)

所以 POSTed 数组的QuantityByPricing顺序与QuantityByPricing数据库创建的数组的顺序不同,所以当 Symfony 神奇地查看需要更新的字段时,它发现我的数组的第一个元素id与POSTed 数组的第一个元素,然后尝试通过首先搜索它的 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)

PRE_SUBMIT注意2:另一个可能有效的解决方案是在使用我的事件提交之前重新排序数组OrderType

        ->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);
            }
        )
于 2017-06-18T19:36:15.967 回答