Как получить данные в форме события внутри CollectionType? - PullRequest
0 голосов
/ 05 февраля 2019

У меня проблема с Symfony 4 по проблеме, уже идентифицированной и описанной на Github (здесь: https://github.com/symfony/symfony/issues/5694#issuecomment-110398953), но я не могу найти способ применить этот ответ.

Когда я пытаюсьчтобы использовать событие формы POST_SET_DATA в форме ChildType, функция getData () дает мне нулевое значение, потому что для опции "allow_add" установлено значение true в форме ParentType, которая является CollectionType.

У меня есть 3 коллекции: Страница, Модуль и Модуль. Документ Модуль используется для встраивания коллекции форм модуля. Цель состоит в том, чтобы иметь возможность добавить несколько форм в коллекцию страниц одним запросом, следуя этой статье Symfony: https://symfony.com/doc/current/form/form_collections.html.

У меня есть 2 различных встроенных документа: тег и задача. Оба они встроены в документ модуля (EmbedOne). Я хочу настроить поле ModuleType с помощью прослушивателя события формы, так что мне просто нужночтобы установить заголовок Модуля в контроллере, и тогда Symfony знает, что ему нужно использовать TaskType или TagType в ModuleType.

Итак, во-первых, вот мой контроллер

class TaskingController extends Controller
{
    /**
     * The controller from which I set the module title, "task" here
     *
     * @Route("/{slug}/task/create", name="tasking_create")
     * 
     * @ParamConverter("page", options={"mapping": {"slug": "slug"}})
     * 
     * @return Response
     */
    public function createTasking(DocumentManager $dm, $id, Module $module, Moduling $moduling)
    {
        $page = $dm->find(Page::class, $id);

        $module->setTitle('task');

        $moduling->addModule($module);
        $page->addModuling($moduling);

        $form = $this->createForm(ModulingType, $moduling);
        $form->handleRequest($request);

        if ($form->isValid() && $form->isSubmitted() {
            // Form validation then redirection
        }
        // Render form template}
    }
}

Теперь, вот мои три коллекции: страницы, модули и модули

/**  
 * My page document
 *
 * @MongoDB\Document(collection="pages")
 */
class Page
{
    /** 
     * @MongoDB\Id(strategy="AUTO")
     */
    protected $id;

    /**
     * @MongoDB\ReferenceMany(targetDocument="App\Document\Moduling")
     * 
     * @var Moduling
     */
    protected $moduling = array();

    public function __construct()
    {
        $this->moduling = new ArrayCollection();
    }

    /**
     * Get the value of id
     */ 
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return Collection $moduling
     */    
    public function getModuling()
    {
        return $this->moduling;
    }

    /**
     * @param Moduling $moduling
     */
    public function addModuling(Moduling $moduling)
    {
        $this->moduling[] = $moduling;
    }

    /**
     * @param Moduling $moduling
     */
    public function removeModuling(Moduling $moduling)
    {
        $this->moduling->removeElement($moduling);
    }
}

/**
 * @MongoDB\Document(collection="moduling")
 */
class Moduling
{
    /**
     * @MongoDB\Id(strategy="AUTO")
     */
    protected $id;

    /**
     * @MongoDB\ReferenceOne(targetDocument="App\Document\Page", storeAs="id")
     * 
     * @var Page
     */
    protected $parentPage;

    /**
     * @MongoDB\ReferenceMany(targetDocument="App\Document\Module", mappedBy="moduling")
     */
    protected $module = array();

    public function __construct()
    {
        $this->module = new ArrayCollection();
    }

    /**
     * Get the value of id
     */
    public function getId()
    {
        return $this->id;
    }

    public function getModule()
    {
        return $this->module;
    }

    public function addModule(Module $module): self
    {   
        $this->module[] = $module;
    }

    public function removeModule(Module $module)
    {
        $this->module->removeElement($module);
    }

    /**
     * Get the value of parentPage
     *
     * @return  Page
     */ 
    public function getParentPage()
    {
        return $this->parentPage;
    }

    /**
     * Set the value of parentPage
     *
     * @param  Page  $parentPage
     *
     * @return  self
     */ 
    public function setParentPage(Page $parentPage)
    {
        $this->parentPage = $parentPage;

        return $this;
    }
}

/**  
 * @MongoDB\Document(collection="modules")
 */
class Module
{
    /**
     * @MongoDB\Id(strategy="AUTO")
     */
    public $id;

    /**
     * @MongoDB\Field(type="string")
     */
    public $title;

    /**
     * @MongoDB\ReferenceOne(targetDocument="App\Document\Moduling", inversedBy="module", storeAs="id")
     */
    public $moduling;

    /**
     * @MongoDB\EmbedOne(targetDocument="App\Document\Task", strategy="set")
     * @Assert\Valid
     */
    public $task;

    public function getTitle()
    {
        return $this->title;
    }

    /**
     * @return  self
     */ 
    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    public function getTask()
    {
        return $this->task;
    }

    public function setTask(Task $task = null)
    {
        $this->task = $task;
    }
}

Мой встроенный документ Задача.Документ тега имеет ту же структуру.

/**  
 * @MongoDB\EmbeddedDocument
 */
class Task
{
    /**
     * @MongoDB\Id(strategy="AUTO")
     */
    protected $id;

    public function getId()
    {
        return $this->id;
    }
}

My ModulingType, который является коллекцией ModuleType

class ModulingType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('module', CollectionType::class, [
                'entry_type' => ModuleType::class,
                'entry_options' => [
                    'label' => false,
                ],
                'by_reference' => false,
                'allow_add' => true,
                'allow_delete' => true
                ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Moduling::class
        ]);
    }
}

class ModuleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {
            $module = $event->getData();
            $form = $event->getForm();

            if ('task' == $module->getTitle()) {
                $form->add('task', TaskType::class);
            }
        });
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Module::class
        ]);
    }
}

Итак, я обнаружил проблему.Когда я пытаюсь заставить это работать, Symfony посылает мне это сообщение об ошибке: «Вызов функции-члена getTitle () on null»).Кажется, getData () ничего не получает.

На самом деле, прочитав несколько постов на Github, я понял, что источником этой проблемы была опция "allow_add", установленная на "true".И действительно, когда я устанавливаю его на «ложь», у меня нет сообщений об ошибках.Но следствием этого является то, что мой JQuery не позволяет мне дублировать форму, если я хочу, для этого необходима опция allow_add.

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

$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {
    if (null != $event->getData()) {
    }
}

Это то, что я сделал, но это ничего не меняет.Я написал это, а затем код, написанный в ModuleType, но у меня все еще есть то же сообщение об ошибке ... Возможно, я не знаю, как правильно вставить его в ModuleType.

Я надеюсь, что у кого-то есть решение,Я знаю, что все еще могу добавлять типы Tag и Task непосредственно в ModulingType, но у меня будет больше коллекций.

Большое спасибо за помощь, надеюсь, я достаточно ясен!

Приветствия

Ответы [ 2 ]

0 голосов
/ 11 февраля 2019

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

class ModuleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
            $module = $event->getData();
            $form = $event->getForm();

            if (null != $event->getData()) {
                if ('task' == $module->getTitle()) {
                    $form->add('task', TaskType::class);
                }
            }
        });
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Module::class
        ]);
    }
}

Это было решение, так что, как вы можете видеть, это не было так сложно, НО фактиспользование события формы в ModuleType создает новую проблему.

В моем ModulingType я добавляю опцию

'allow_add' => true,

Этот действительно полезный инструмент позволяет автоматически добавлять «data-prototype»в моей форме, чтобы я мог скопировать / пропустить некоторые доступные здесь строки jQuery (https://symfony.com/doc/current/form/form_collections.html)), а затем иметь возможность дублировать или удалить мою форму. Однако при использовании события формы прототип данных ничего не регистрируеткак он был создан до моего TaskType.

Поэтому, потратив часы на чтение дискуссий на Github и пытаясь найти решение, я пришел к выводу, что должен был создать TaskingType и TagingType, которые выглядят так:

class TaskingType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('task', TaskType::class);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Module::class
        ]);
    }
}

Итак, это решение не идеально, и у меня есть некоторое дублирование кода. Но, по крайней мере, оно позволяет мне иметь только 3 коллекции: Page, Moduling и Module.

Если кто-то найдет элегантный способ управлять всем с помощью одной формы без удаления контента, доступного в data-prototype, пожалуйста, держите меня в курсе:)

0 голосов
/ 06 февраля 2019

Вы пробовали это:

if (!is_null($module) && 'task' == $module->getTitle()) {
    $form->add('task', TaskType::class);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...