Вставить коллекцию ошибок форм: не удалось определить тип доступа для свойства

Я пытаюсь вставить коллекцию форм 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(),
    ));
}

Ответы

Ответ 2

Может быть, проблема в том, что Symfony не может получить доступ к этому свойству?

Если вы посмотрите, где это исключение выбрано (метод writeProperty в классе PropertyAccessor), он говорит, что он может быть брошен:

Если свойство не существует или не является общедоступным.

В учебнике вы упомянули, что у него есть теги свойств $и метод addTag. Я просто догадываюсь здесь, но, возможно, существует соглашение, в котором он пытается вызвать имена методов add($singularForm), и это не работает для вас, потому что свойство tagList, а метод addTag.

Я не уверен на 100%, но вы можете попробовать отладки, установив точку останова в этом методе Symfony, чтобы понять, почему она была выбрана.

Ответ 3

Краткая версия:

Я просто столкнулся с этой проблемой и решил ее, добавив сеттер для затронутого свойства:

Не удалось определить тип доступа для свойства "tagList"

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

Длинная версия:

Сообщение об ошибке сигнализирует о том, что Symfony пытается изменить состояние объекта, но не может понять, как на самом деле внести изменения из-за того, как настроен его класс.

Взглянув на внутренности Symfony, мы видим, что Symfony дает вам 5 шансов дать ему доступ и выбрать лучший в этом порядок сверху вниз:

  • Метод setter с именем setProperty() с одним аргументом:

Это первое, что 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. Искаженное свойство объявляется публичным:

    class Entity {
    
        public $tagList;
        //... other properties here
    }
    
  2. A __set магический метод:

Это повлияет на все свойства, а не только на то, что вы намеревались.

class Entity {

    public $tagList;

    //...

    public function __set($name, $value){
        $this->$name = $value;
    }
    //...
}
  1. A __call магический метод (в некоторых случаях):

Я не смог подтвердить это, но внутренний код подсказывает, что это возможно, когда magic включен в конструкцию PropertyAccessor.


Требуется использовать только одну из вышеуказанных стратегий.

Ответ 4

Возможно, вы забыли в __construct() класса Service и Tag для инициализации $tagList и $serviceList, как это?

$this->tagList = new ArrayCollection();

$this->serviceList = new ArrayCollection();

Ответ 5

Это похоже на ошибку с вашим конструктором. Попробуйте следующее:

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

Ответ 6

Это длинный выстрел, но, глядя на ваши аннотации, я думаю, что проблема может быть связана с вашими многочисленными отношениями. Попытайтесь изменить сторону владельца и обратную сторону (замените связь), если вам не требуется обновление с обоих концов (в этом случае я думаю, что единственным решением является добавление объектов вручную или использование отношений oneToMany).

Изменения, внесенные только в обратную сторону ассоциации, игнорируются. Обязательно обновите обе стороны двунаправленной связи (или наименее владеющая сторона, с точки зрения Doctrines)

Это проблема, связанная с доктриной, которую я перенес прежде, см. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/unitofwork-associations.html

Ответ 7

Основываясь на 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, чтобы автоматически создавать методы для меня