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);
                }
            )
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 AT89C51控制8位八段数码管显示时钟。
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 下图接收小电路,谁知道原理
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度
  • ¥30 关于#r语言#的问题:如何对R语言中mfgarch包中构建的garch-midas模型进行样本内长期波动率预测和样本外长期波动率预测
  • ¥15 ETLCloud 处理json多层级问题