donglian1523 2017-06-18 10:36
浏览 32
已采纳

对于某些,但不是全部,表单提交得到“无法确定属性的访问类型”id“。”

I have a complex form that maps to a collection of Entity, that permits to buy ticket at a given pricing for an event, for most of the events it works, but for one of them, we got in a repeatable fashion the error Could not determine access type for property "id"

In such a case I know that Could not determine access type for property X is because a setter is missing. There's indeed no setId() method and id is protected, but I think symfony should not try to set the id in the first place (because it works for the other forms, i.e the tickets are bought, and appears correctly link to the event etc.)

So my question is why in some case symfony REQUIRES the setId()

I have the following entities

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;
}

Indeed the trait "HasId" has no setter (but it's on purpose) or at least until now it has never been a problem

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;
    }
}

And the forms

    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;
}

The controller creating the form looks like this

      // call the DB to set the possible pricings
      // inside it calls addQuantityByPricing
      $orderFront = $this->_createOrderFront(); 
      $form = $this->createForm(OrderType::class, $orderFront);
      $form->handleRequest($request);

The exception traceback is the following

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')

And when adding some debug I can see that the 2 Pricing are

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

So it seems that symfony is trying to override the id of this pricing (which is a valid/existing one) by an other valid/existing one, which is I think the reason why it try to "replace" it, by trying to call the setter and fails, but why does it do so ?

Edit:

After some more debugging I found a troubling coincidence:

  • d82cafb8-432b-4e20-ac9f-66e48dc55458 is the first id to add when i added some debug in the addQuantitiesByPricing method
  • 80424145-ca68-4dce-b4f0-644a423d3aad is the pricing id with the index 0 in the submitted form

Edit2: the way my controller create the form (and which is the one calling the addQuantitiesByPricing making d82cafb8-432b-4e20-ac9f-66e48dc55458 to appear first ), make that we first retrieve these Ids from the database BEFORE taking the Ids POSTed

  • 写回答

1条回答 默认 最新

  • doujiang1939 2017-06-18 19:36
    关注

    The problem was that the order in which the elements were added using addQuantitiesByPricing when creating the main entity of the form (i.e OrderFront ) was not the same in which they were added in the HTML (there was a sort hidden in the twig file )

    So the POSTed array of QuantityByPricing was not in the same order as the array of QuantityByPricing created by the database, so when Symfony was doing its magic to see which field needed to be updated, it saw that the first element of my array had a different id than the first element of the POSTed array, and then tried to replace it by searching first its setter so setId() hence the exception

    The reason why I didn't see this bug before was because for some (un)lucky reason the array coming from the database was already sorted, so the twig sort was doing nothing

    Note: not related directly to this specific context, but if the size of the posted array is less than the size of the database array you will get the same stacktrace (except the last argument will be 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: an other solution that could had work would have been to reorder the array before submiting it using the PRE_SUBMIT event in my 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);
                }
            )
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 做个有关计算的小程序
  • ¥15 MPI读取tif文件无法正常给各进程分配路径
  • ¥15 如何用MATLAB实现以下三个公式(有相互嵌套)
  • ¥30 关于#算法#的问题:运用EViews第九版本进行一系列计量经济学的时间数列数据回归分析预测问题 求各位帮我解答一下
  • ¥15 setInterval 页面闪烁,怎么解决
  • ¥15 如何让企业微信机器人实现消息汇总整合
  • ¥50 关于#ui#的问题:做yolov8的ui界面出现的问题
  • ¥15 如何用Python爬取各高校教师公开的教育和工作经历
  • ¥15 TLE9879QXA40 电机驱动
  • ¥20 对于工程问题的非线性数学模型进行线性化