Embed a Collection of Forms Error: Could not determine access type for property

Embed a Collection of Forms Error: Could not determine access type for property

根据 this tutorial,我正在尝试将 Tag 表格的集合嵌入到 Service 表格中。 TagService 实体具有多对多关系。

表单呈现正确。但是当我提交表格时,我得到

Could not determine access type for property "tagList"

错误。我不明白为什么没有通过调用 addTag() 方法将新的 Tag 对象添加到 Service class。

服务类型

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('title', TextType::class, array(
            'label' => 'Title'
        ))
    ;

    $builder->add('tagList', CollectionType::class, array(
        'entry_type' => TagType::class,
        'allow_add' => true,
        'allow_delete' => true,
        'by_reference' => false
    )));
}

服务class

{
....
 /**
 * @ORM\ManyToMany(targetEntity="Tag", mappedBy="serviceList",cascade={"persist"})
 */ 
private $tagList;

/**
 * @return ArrayCollection
 */
public function getTagList()
{
    return $this->tagList;
}

/**
 * @param Tag $tag
 * @return Service
 */
public function addTag(Tag $tag)
{
    if ($this->tagList->contains($tag) == false) {
        $this->tagList->add($tag);
        $tag->addService($this);
    }
}

/**
 * @param Tag $tag
 * @return Service
 */
public function removeTag(Tag $tag)
{
    if ($this->tagList->contains($tag)) {
        $this->tagList->removeElement($tag);
        $tag->removeService($this);
    }
    return $this;
}
}

标签class

 {
  /**
 * @ORM\ManyToMany(targetEntity="Service", inversedBy="tagList")
 * @ORM\JoinTable(name="tags_services")
 */
private $serviceList;
 /**
 * @param Service $service
 * @return Tag
 */
public function addService(Service $service)
{
    if ($this->serviceList->contains($service) == false) {
        $this->serviceList->add($service);
        $service->addTag($this);
    }
    return $this;
}

/**
 * @param Service $service
 * @return Tag
 */
public function removeService(Service $service)
{
    if ($this->serviceList->contains($service)) {
        $this->serviceList->removeElement($service);
        $service->removeTag($this);
    }
    return $this;
}
 }

服务控制器

  public function newAction(Request $request)
{
    $service = new Service();
    $form = $this->createForm('AppBundle\Form\ServiceType', $service);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {

        $em = $this->getDoctrine()->getManager();
        $em->persist($service);
        $em->flush();

        return $this->redirectToRoute('service_show', array('id' => $service->getId()));
    }

    return $this->render('AppBundle:Service:new.html.twig', array(
        'service' => $service,
        'form' => $form->createView(),
    ));
}

也许问题在于 Symfony 无法访问 属性?

如果您查看抛出异常的位置(PropertyAccessor class 中的 writeProperty 方法),它表示可以抛出:

If the property does not exist or is not public.

在您提到的教程中,它有 属性 $tags 和方法 addTag。我只是在这里猜测,但也许有一个约定,它试图调用方法名称 add($singularForm) 而这对你来说是失败的,因为 属性 是 tagList 并且方法是 addTag

我不是 100% 确定,但您可以通过在该 Symfony 方法中设置停止点来尝试调试,看看它被抛出的原因。

也许你忘记在 Service class 和 Tag class 的 __construct() 中像这样初始化 $tagList 和 $serviceList 了?

$this->tagList = new ArrayCollection();

$this->serviceList = new ArrayCollection();

这似乎是您的构造函数出错。试试这个:

public function __construct()
{
    $this-> tagList = new \Doctrine\Common\Collections\ArrayCollection();
}

能否请您尝试实现此 URL 中的代码?

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#owning-and-inverse-side-on-a-manytomany-association

首先,请尝试改变 mapped/inverse 面,并从 Tag::addService 方法中删除 $service->addTag($this);

不太可能,但看你的注释我认为问题可能与你的多对多关系有关。尝试更改拥有方和反方(交换关系),除非您特别需要从两端更新(在这种情况下,我认为唯一的解决方案是手动添加对象或使用 oneToMany 关系)。

Changes made only to the inverse side of an association are ignored. Make sure to update both sides of a bidirectional association (or at least the owning side, from Doctrine’s point of view)

这是我之前遇到过的与Doctrine相关的问题,参见: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/unitofwork-associations.html

短版:

我刚刚 运行 解决了这个问题,并通过为受影响的 属性 添加 setter 解决了它:

Could not determine access type for property "tagList"

public function setTagList(Array $tagList)
{
    $this->tagList = $tagList;
}

长版:

错误消息表示 Symfony 正在尝试修改对象的状态,但由于 class 的设置方式,无法弄清楚如何实际进行更改。

Taking a look at Symfony's internals,我们可以看到 Symfony 给了你 5 次访问权限的机会,并按照从上到下的顺序选择最好的一个:

  1. 一个名为 setProperty() 的 setter 方法,带有一个参数:

这是 Symfony 检查的第一件事,也是实现它的最明确的方法。据我所知,这是最佳做法:

class Entity {

    protected $tagList;

    //...

    public function getTagList()
    {
        return $this->tagList;
    }

    //...
}
  1. 将 getter 和 setter 组合在一个方法中,带有一个参数:

重要的是要意识到 Symfony 也会访问此方法以获取对象的状态。由于这些方法调用不包含参数,因此此方法中的参数必须是可选的。

class Entity {

    protected $tagList;

    //...

    public function tagList($tags = null)
    {
        if($reps){
            $this->tagList = $tags;
        } else {
            return $this->tagList;
        }
    }

    //...
}
  1. 受影响的 属性 被声明为 public:

    class Entity {
    
        public $tagList;
        //... other properties here
    }
    
  2. 一个__set法术:

这将影响所有属性,而不仅仅是您想要的属性。

class Entity {

    public $tagList;

    //...

    public function __set($name, $value){
        $this->$name = $value;
    }
    //...
}
  1. 一个__call魔术方法(在某些情况下):

我无法确认这一点,但内部代码表明,当在 PropertyAccessor 的构造中启用 magic 时,这是可能的。


只需要使用上述策略之一。

基于 Symfony 3.3.10

我实际上遇到过这个问题很多很多次,最后我发现这个问题是从哪里来的,这取决于你给你的实体起的名字 属性 collection 属性 的 adderremover 并不完全符合您的预期。

示例:您的实体属性名称是 "foo",您希望加法器被称为 "addFoo",去除器被称为 "removeFoo",但突然 "Could not determine access type for property"出现。

所以你开始害怕在你的代码中寻找 w/e 问题,而你只需要在 Symfony 核心文件中查看这个文件:

vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php

在这个文件中有一个名为 findAdderAndRemover 的方法。 带着你的调试器去那里,你最终会发现 symfony 为你的 adder/remover 搜索奇怪的名字,它们实际上可能以 "um" 或 "on" 或 "us" 取决于你用来命名它们的语言(人类语言)。因为我是意大利人,这种情况经常发生。

注意这一点,因为修复可能很简单,只需更改实体内用于 add/remove 方法的名称,使它们与 Symfony 核心正在寻找的名称相匹配。

当我使用 bin/console doctrine:generate:entities 自动为我创建方法时,我会遇到这种情况

如果您使用的是 symfony,并且使用 EntityRepository 而不是 CollectionType,请确保在您的表单构建中使用 'multiple' => true,,否则输入将针对一个实体而不是多个实体,因此它会调用setTagList 而不是使用方法 addTagListremoveTagList.