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
表格中。 Tag
和 Service
实体具有多对多关系。
表单呈现正确。但是当我提交表格时,我得到
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 中的代码?
首先,请尝试改变 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 次访问权限的机会,并按照从上到下的顺序选择最好的一个:
- 一个名为
setProperty()
的 setter 方法,带有一个参数:
这是 Symfony 检查的第一件事,也是实现它的最明确的方法。据我所知,这是最佳做法:
class Entity {
protected $tagList;
//...
public function getTagList()
{
return $this->tagList;
}
//...
}
- 将 getter 和 setter 组合在一个方法中,带有一个参数:
重要的是要意识到 Symfony 也会访问此方法以获取对象的状态。由于这些方法调用不包含参数,因此此方法中的参数必须是可选的。
class Entity {
protected $tagList;
//...
public function tagList($tags = null)
{
if($reps){
$this->tagList = $tags;
} else {
return $this->tagList;
}
}
//...
}
受影响的 属性 被声明为 public:
class Entity {
public $tagList;
//... other properties here
}
一个__set
法术:
这将影响所有属性,而不仅仅是您想要的属性。
class Entity {
public $tagList;
//...
public function __set($name, $value){
$this->$name = $value;
}
//...
}
- 一个
__call
魔术方法(在某些情况下):
我无法确认这一点,但内部代码表明,当在 PropertyAccessor 的构造中启用 magic
时,这是可能的。
只需要使用上述策略之一。
基于 Symfony 3.3.10
我实际上遇到过这个问题很多很多次,最后我发现这个问题是从哪里来的,这取决于你给你的实体起的名字 属性 collection 属性 的 adder 和 remover 并不完全符合您的预期。
示例:您的实体属性名称是 "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
而不是使用方法 addTagList
和 removeTagList
.
根据 this tutorial,我正在尝试将 Tag
表格的集合嵌入到 Service
表格中。 Tag
和 Service
实体具有多对多关系。
表单呈现正确。但是当我提交表格时,我得到
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 中的代码?
首先,请尝试改变 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 次访问权限的机会,并按照从上到下的顺序选择最好的一个:
- 一个名为
setProperty()
的 setter 方法,带有一个参数:
这是 Symfony 检查的第一件事,也是实现它的最明确的方法。据我所知,这是最佳做法:
class Entity {
protected $tagList;
//...
public function getTagList()
{
return $this->tagList;
}
//...
}
- 将 getter 和 setter 组合在一个方法中,带有一个参数:
重要的是要意识到 Symfony 也会访问此方法以获取对象的状态。由于这些方法调用不包含参数,因此此方法中的参数必须是可选的。
class Entity {
protected $tagList;
//...
public function tagList($tags = null)
{
if($reps){
$this->tagList = $tags;
} else {
return $this->tagList;
}
}
//...
}
受影响的 属性 被声明为 public:
class Entity { public $tagList; //... other properties here }
一个
__set
法术:
这将影响所有属性,而不仅仅是您想要的属性。
class Entity {
public $tagList;
//...
public function __set($name, $value){
$this->$name = $value;
}
//...
}
- 一个
__call
魔术方法(在某些情况下):
我无法确认这一点,但内部代码表明,当在 PropertyAccessor 的构造中启用 magic
时,这是可能的。
只需要使用上述策略之一。
基于 Symfony 3.3.10
我实际上遇到过这个问题很多很多次,最后我发现这个问题是从哪里来的,这取决于你给你的实体起的名字 属性 collection 属性 的 adder 和 remover 并不完全符合您的预期。
示例:您的实体属性名称是 "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
而不是使用方法 addTagList
和 removeTagList
.