このチュートリアル に従って、Tag
フォームのコレクションをService
フォームに埋め込もうとしています。 Tag
エンティティとService
エンティティには多対多の関係があります。
フォームは正しくレンダリングされています。しかし、フォームを送信すると、
プロパティ「tagList」のアクセスタイプを判別できませんでした
エラー。 addTag()
メソッドを呼び出しても新しいTag
オブジェクトがService
クラスに追加されない理由がわかりません。
サービスの種類
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
)));
}
サービスクラス
{
....
/**
* @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;
}
}
タグクラス
{
/**
* @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;
}
}
ServiceController
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(),
));
}
このURLからコードを実装してみてください。
まず、マップされた/逆の側面を変更して、_Tag::addService
_メソッドから$service->addTag($this);
を削除してください。
おそらく問題は、Symfonyがそのプロパティにアクセスできないことですか?
その例外がスローされる場所(PropertyAccessorクラスのwritePropertyメソッド)を見ると、スローされる可能性があることがわかります。
プロパティが存在しないか、公開されていない場合。
チュートリアルで、プロパティ$ tagsとメソッドaddTagがあると述べました。ここで推測しているだけですが、メソッド名add($singularForm)
を呼び出そうとする規則があり、プロパティがtagList
で、メソッドがaddTag
。
100%確信はありませんが、そのSymfonyメソッドにストップポイントを設定してデバッグを試み、なぜスローされるのかを確認することができます。
ショートバージョン:
私はこの問題に遭遇し、影響を受けるプロパティのセッターを追加することで解決しました。
プロパティのアクセスタイプを判別できませんでした "tagList"
_public function setTagList(Array $tagList)
{
$this->tagList = $tagList;
}
_
ロングバージョン:
エラーメッセージは、Symfonyがオブジェクトの状態を変更しようとしていることを示していますが、クラスの設定方法が原因で、実際に変更を加える方法を理解できません。
Symfonyの内部を見てください 、Symfonyはアクセスを許可する5つのチャンスを与え、上から下の順序で最良のものを選択することがわかります:
setProperty()
という名前のセッターメソッドと1つの引数:これはSymfonyが最初にチェックすることであり、これを達成するための最も明確な方法です。私の知る限り、これがベストプラクティスです。
_class Entity {
protected $tagList;
//...
public function getTagList()
{
return $this->tagList;
}
//...
}
_
オブジェクトの状態をgetするために、このメソッドもSymfonyによってアクセスされることを理解することが重要です。これらのメソッド呼び出しには引数が含まれていないため、このメソッドの引数はオプションである必要があります。
_class Entity {
protected $tagList;
//...
public function tagList($tags = null)
{
if($reps){
$this->tagList = $tags;
} else {
return $this->tagList;
}
}
//...
}
_
パブリックとして宣言されている影響を受けるプロパティ:
_class Entity {
public $tagList;
//... other properties here
}
_
___set
_マジックメソッド:
これは、意図したプロパティだけでなく、すべてのプロパティに影響します。
_class Entity {
public $tagList;
//...
public function __set($name, $value){
$this->$name = $value;
}
//...
}
_
__call
_マジックメソッド(場合によっては):これを確認することはできませんでしたが、内部コードは、PropertyAccessorの構築でmagic
が有効になっている場合にこれが可能であることを示しています。
上記の戦略のいずれかを使用するだけで済みます。
ロングショットですが、アノテーションを見ると、問題はmanyToManyの関係に関連している可能性があると思います。特に両端から更新する必要がない限り、所有側と逆側を変更してみてください(関係を交換してください)(その場合、唯一の解決策はオブジェクトを手動で追加するか、oneToMany関係を使用することだと思います)。
アソシエーションの逆側にのみ加えられた変更は無視されます。双方向アソシエーションの両側(または、Doctrineの観点からは少なくとも所有側)を必ず更新してください。
これは、Doctrine以前に苦しんだことがある、参照: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference /unitofwork-associations.html
たぶん、このように$ tagListと$ serviceListを初期化するためにServiceクラスとTagクラスの__construct()
を忘れましたか?
$this->tagList = new ArrayCollection();
$this->serviceList = new ArrayCollection();
これはコンストラクターのエラーのようです。これを試して :
public function __construct()
{
$this-> tagList = new \Doctrine\Common\Collections\ArrayCollection();
}
Symfonyを使用していて、CollectionTypeの代わりにEntityRepositoryを使用している場合は、フォームビルドで'multiple' => true,
を使用してください。そうしないと、入力が1つのエンティティに対して行われ、多くのエンティティに対して行われないため、setTagList
メソッドaddTagList
およびremoveTagList
を使用する代わりに。
Symfony 3.3.10に基づく
私は実際に何度もこの問題に直面しましたが、最終的にこの問題がどこから来ているのかを発見すると、エンティティプロパティに付けた名前によっては、adderと( removerコレクションプロパティは、期待どおりのものではありません。
例:エンティティのプロパティ名は「foo」で、加算器は「addFoo」、リムーバーは「removeFoo」と呼ばれると予想されますが、突然"プロパティのアクセスタイプを判別できませんでした" =表示されます。
そのため、コード内の問題を検索することを恐れ始めます。代わりに、Symfonyコアファイル内でこのファイルを調べる必要があります。
vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php
このファイルの中にはfindAdderAndRemoverというメソッドがあります。デバッガーを使ってそこに行くと、symfonyが加算器/リムーバーの奇妙な名前を検索していることがわかります。言語によっては実際には"um"または "on"または "us"で終わる場合があります。 (人間の言語)あなたはそれらに名前を付けていました。私はイタリア人なので、これはかなり頻繁に起こります。
修正は、エンティティ内のadd/removeメソッドに使用される名前を変更して、Symfonyコアが探しているものと一致させるのと同じくらい簡単な場合があるため、注意してください。
これは、bin/console doctrine:generate:entitiesを使用してメソッドを自動的に作成すると発生します