@maximrabotaet

Как в symfony 3.4 вывести и отправить форму для сущности в списке сущностей?

Здравствуйте, не так долго изучаю symfony и застрял на следующем моменте.
  • создал сущность с полями для бд, контроллер и форму;
  • в контроллере в методе который выводит список экземпляров этой сущности создаю форму для каждой сущности в цикле экземпляров;
  • присваиваю созданную форму в свойство каждого экземпляра;
  • передаю массив сущностей в шаблон, где в цикле выводится список с данными и формой для редактирования каждого экземпляра.

Так же поставил в контроллере условия которые срабатывают при отправке формы и сохраняют изменения:
if($form->isSubmitted() && $form->isValid()){...}
Но при отправке формы изменения сохраняются всегда только для самого первого экземпляра, даже если я менял данные у другого экземпляра. Если после отправки не возвращать в контроллере редирект (return $this->redirectToRoute(...);) то данные измененные в любой форме применяются ко всем экземплярам.

Вопросы:
В чем моя ошибка?
Как правильно сделать вывод списка сущностей с формами редактирования для каждой?

Спасибо!

Код сущности:
// src/AppBundle/Entity/Account.php

namespace AppBundle\Entity;

class Account
{
//...
    /**
     * @var FormInterface
     */
    private $form;
    /**
     * @var FormView
     */
    private $formView;
//...
}


Код метода контроллера:
// src/AppBundle/Controller/AccountController.php

//...
/**
     * @Route("/accounts/", name="account_list")
     * @Template()
     */
    public function indexAction(Request $request)
    {
        $accounts = $this
            ->getDoctrine()
            ->getRepository('AppBundle:Account')
            ->findActive();

        foreach ($accounts as $key => $account){

            $form = $this->createForm(AccountType::class, $account);

            $form->add('submit', SubmitType::class);

            $formView = $form->createView();

            $form->handleRequest($request);

            $account->setForm($form);

            $account->setFormView($formView);

            if($form->isSubmitted() && $form->isValid()){

                $data = $form->getData();

                $this->addFlash('success', 'Saved');

                $em = $this->getDoctrine()->getManager();
                $em->persist($data);
                $em->flush();

                return $this->redirectToRoute('account_list');
            }
        }

        return [
            'accounts' => $accounts,
        ];
    }
//...


Код шаблона:
// src/AppBundle/Resources/views/account/index.html.twig
//...
{% block body %}
    <ul class="list-group">
        {% for account in accounts %}

            <li class="list-group-item flex-column align-items-start">
                <div class="d-flex w-100 justify-content-between">
                    <h5 class="mb-1">{{ account.login }}</h5>
                    <small>{{ account.id }}</small>
                </div>

                {{ form_row(account.formView) }}

            </li>

        {% endfor %}
    </ul>
{% endblock %}
//...
  • Вопрос задан
  • 227 просмотров
Решения вопроса 1
BoShurik
@BoShurik Куратор тега Symfony
Symfony developer
$form = $this->container->get('form.factory')
    ->createNamed(
        'account_'. $account->getId(), 
        AccountType::class, 
        $account
    )
;


По поводу кода
public function indexAction(Request $request)
{
    $accounts = $this
        ->getDoctrine()
        ->getRepository('AppBundle:Account')
        ->findActive();

    $forms = [];
    foreach ($accounts as $key => $account){

        $form = $this->container->get('form.factory')->createNamed('account_'. $account->getId(), AccountType::class, $account);

        // Не надо добавлять кнопки сабмита, их рекомендуется добавлять непосредственно в шаблоне обычным html
        // $form->add('submit', SubmitType::class);

        $form->handleRequest($request);

        // Не надо инжектить форму в сущность, легче просто передать формы в шаблон
        // $account->setForm($form);
        // $account->setFormView($formView);

        if($form->isSubmitted() && $form->isValid()){

            // Объекты передаются по ссылке, нет смысла в получение $account повторно
            // $data = $form->getData();

            $this->addFlash('success', 'Saved');

            $em = $this->getDoctrine()->getManager();
            // Это актуально только для создания сущности, при редактировании - это не нужно
            // $em->persist($data);
            $em->flush();

            return $this->redirectToRoute('account_list');
        } else {
            $forms[$account->getId()] = $form->createView();
        }
    }

    return [
        'forms' => $forms,
        'accounts' => $accounts,
    ];
}


{# {{ form_row(account.formView) }} #}
{{ form_start(forms[account.id]) }}
    {{ form_widget(forms[account.id]) }}
    <button>Submit</button>
{{ form_end(forms[account.id]) }}


P.S. В идеале бы разделить обновление и список сущностей по разным экшенам
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
mad_maximus
@mad_maximus
Какой ужас. Вы ничего хуже не придумали? По поводу вопроса: посмотрите в вёрстку и на названия форм, которые у вас получились. Если что, все формы, которые вы отрендерили, с одинаковыми именами, поэтому отправляется только одна из них. Чтобы было понятно, какая форма обрабатывается, если таких форм на странице много, у FormType перегружается метод getBlockPrefix(), где просто возвращается строка с названием формы. Это первое. Второе. Поля сущности не равны полям в форме. Не надо использовать сущность для маппинга на форму. И добавлять в сущность поле FormInterface - это как вообще? Вы просто выводите в цикле данные о сущностях, у каждой из которых делаете блок с действиями. При любом действии отправляете id этой сущности. Таким образом, форма одна, а редактировать можно любую сущность.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы