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 拟通过pc下指令到安卓系统,如果追求响应速度,尽可能无延迟,是不是用安卓模拟器会优于实体的安卓手机?如果是,可以快多少毫秒?
  • ¥20 神经网络Sequential name=sequential, built=False
  • ¥16 Qphython 用xlrd读取excel报错
  • ¥15 单片机学习顺序问题!!
  • ¥15 ikuai客户端多拨vpn,重启总是有个别重拨不上
  • ¥20 关于#anlogic#sdram#的问题,如何解决?(关键词-performance)
  • ¥15 相敏解调 matlab
  • ¥15 求lingo代码和思路
  • ¥15 公交车和无人机协同运输
  • ¥15 stm32代码移植没反应