Ответ 1
Не могли бы вы попытаться реализовать код из этого URL?
Сначала попробуйте изменить отображаемые/обратные стороны и удалите $service->addTag($this);
из метода Tag::addService
.
Я пытаюсь вставить коллекцию форм Tag
в форму Service
, согласно этот учебник. Tag
и Service
объекты имеют отношения "многие ко многим".
Форма выполняется корректно. Но когда я отправляю форму, я получаю
Не удалось определить тип доступа для свойства tagList
ошибка. Я не понимаю, почему новый объект Tag
не добавляется в класс Service
, вызывая метод addTag()
.
ServiceType
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?
Сначала попробуйте изменить отображаемые/обратные стороны и удалите $service->addTag($this);
из метода Tag::addService
.
Может быть, проблема в том, что Symfony не может получить доступ к этому свойству?
Если вы посмотрите, где это исключение выбрано (метод writeProperty в классе PropertyAccessor), он говорит, что он может быть брошен:
Если свойство не существует или не является общедоступным.
В учебнике вы упомянули, что у него есть теги свойств $и метод addTag. Я просто догадываюсь здесь, но, возможно, существует соглашение, в котором он пытается вызвать имена методов add($singularForm)
, и это не работает для вас, потому что свойство tagList
, а метод addTag
.
Я не уверен на 100%, но вы можете попробовать отладки, установив точку останова в этом методе Symfony, чтобы понять, почему она была выбрана.
Краткая версия:
Я просто столкнулся с этой проблемой и решил ее, добавив сеттер для затронутого свойства:
Не удалось определить тип доступа для свойства "tagList"
public function setTagList(Array $tagList)
{
$this->tagList = $tagList;
}
Длинная версия:
Сообщение об ошибке сигнализирует о том, что Symfony пытается изменить состояние объекта, но не может понять, как на самом деле внести изменения из-за того, как настроен его класс.
Взглянув на внутренности Symfony, мы видим, что Symfony дает вам 5 шансов дать ему доступ и выбрать лучший в этом порядок сверху вниз:
setProperty()
с одним аргументом:Это первое, что Symfony проверяет и является наиболее явным способом достижения этого. Насколько мне известно, это лучшая практика:
class Entity {
protected $tagList;
//...
public function getTagList()
{
return $this->tagList;
}
//...
}
Важно понимать, что этот метод также будет доступен 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
}
A __set
магический метод:
Это повлияет на все свойства, а не только на то, что вы намеревались.
class Entity {
public $tagList;
//...
public function __set($name, $value){
$this->$name = $value;
}
//...
}
__call
магический метод (в некоторых случаях):Я не смог подтвердить это, но внутренний код подсказывает, что это возможно, когда magic
включен в конструкцию PropertyAccessor.
Требуется использовать только одну из вышеуказанных стратегий.
Возможно, вы забыли в __construct()
класса Service и Tag для инициализации $tagList и $serviceList, как это?
$this->tagList = new ArrayCollection();
$this->serviceList = new ArrayCollection();
Это похоже на ошибку с вашим конструктором. Попробуйте следующее:
public function __construct()
{
$this-> tagList = new \Doctrine\Common\Collections\ArrayCollection();
}
Это длинный выстрел, но, глядя на ваши аннотации, я думаю, что проблема может быть связана с вашими многочисленными отношениями. Попытайтесь изменить сторону владельца и обратную сторону (замените связь), если вам не требуется обновление с обоих концов (в этом случае я думаю, что единственным решением является добавление объектов вручную или использование отношений oneToMany).
Изменения, внесенные только в обратную сторону ассоциации, игнорируются. Обязательно обновите обе стороны двунаправленной связи (или наименее владеющая сторона, с точки зрения Doctrines)
Это проблема, связанная с доктриной, которую я перенес прежде, см. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/unitofwork-associations.html
Основываясь на Symfony 3.3.10
Я действительно сталкивался с этой проблемой много и много раз, наконец, однажды, когда я обнаружил, откуда эта проблема, в зависимости от имени, которое вы передаете своей сущности, может случиться, что сумматор и remover для вашего свойства коллекции не совсем то, что вы ожидаете.
Пример. Имя вашей сущности является "foo", и вы ожидаете, что сумматор будет называться "addFoo" и удалите "removeFoo", но затем неожиданно "Не удалось определить тип доступа для свойства" .
Итак, вы начинаете испытывать страх в поисках проблем с w/e в своем коде, вместо этого вам просто нужно посмотреть этот файл внутри основных файлов Symfony:
поставщик/Symfony/Symfony/SRC/Symfony/Компонент/PropertyAccess/PropertyAccessor.php
Внутри этого файла есть метод findAdderAndRemover. Идите туда с вашим отладчиком, и вы в конце концов узнаете, что symfony ищет странное имя для вашего сумматора/удаления, они могут на самом деле заканчиваться "um" или "on" или "нами" в зависимости от языка (человеческий язык), который вы использовали для их обозначения. Поскольку я итальянский, это случается довольно часто.
Следите за этим, поскольку исправление может быть таким же простым, как изменение имени, используемого для вашего метода add/remove внутри вашего объекта, чтобы они соответствовали тому, что ищет ядро Symfony.
Это происходит со мной, когда я использую bin/console doctrine: generate: entities, чтобы автоматически создавать методы для меня