Как создать форму Symfony, которая имеет вложенную форму для каждого активного пользователя в базе данных?

1

Я пытаюсь создать страницу администратора для сайта спортивного клуба, чтобы каждый месяц пользователь-администратор мог создавать новые счета-фактуры (объект Invoice) для всех активных членов (Member) клуба.

Я пытаюсь создать форму, чтобы у меня была одна строка для каждого члена, предварительно заполненного их стандартной ежемесячной абонентской платой и текущей датой (оба из которых могут быть изменены для отдельных записей участников, если это необходимо):

Изображение 174551

Я пробовал практически все, что я могу придумать, чтобы заставить это работать в форме, но до сих пор я не имел успеха. Ниже приведен код, который в настоящее время стоит, но это дает мне индивидуальную форму только для последнего участника... любой совет о том, что я делаю неправильно, был бы очень желанным - заблаговременно!

Сущности (член и счет-фактура):

class Member
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\OneToMany(targetEntity="Invoice", mappedBy="member_id")
     */
    protected $invoice_ids;

    /**
     * @ORM\COLUMN(type="string", length=100)
     * @Assert\NotBlank()
     */
    protected $firstname;

    /**
     * @ORM\COLUMN(type="string", length=100)
     */
    protected $familyname;

    /**
     * @ORM\COLUMN(type="boolean")
     */
    protected $active = true;

    /**
     * @ORM\COLUMN(type="decimal", precision=7, scale=2)
     * @Assert\Regex(
     *     pattern="/^\s*-?[1-9]\d*(\.\d{1,2})?\s*$/",
     *     match=true,
     *     message="Error")
     */
    protected $defaultinvoiceamount;

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

    public function FullName()
    {
        return $this->firstname . ' ' . $this->familyname;
    }
}


class Invoice
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
    /**
     * @ORM\ManyToOne(targetEntity="Member", inversedBy="invoice_ids")
     * @ORM\JoinColumn(name="member_id",     referencedColumnName="id")
     */
    protected $member_id;

    /**
     * @ORM\COLUMN(type="decimal", precision=7, scale=2)
     * @Assert\Regex(
     *     pattern="/^\s*-?[1-9]\d*(\.\d{1,2})?\s*$/",
     *     match=true,
     *     message="Error")
     */
    protected $amount;

    /**
     * @ORM\COLUMN(type="datetime")
     * @Assert\DateTime()
     */
    protected $invoicedate;


    /**
     * @ORM\COLUMN(type="datetime")
     * @Assert\DateTime()
     */
    protected $createdate;

    /**
     * @ORM\COLUMN(type="text", nullable=True)
     */
    protected $comments;

}

FormType:

class InvoiceType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('member_id', EntityType::class, array(
                    'class' => 'AppBundle:Member',
                    'choice_label' => 'FullName',
                    'attr' => array(
                         'readonly' => 'readonly'
                    )
                )
            )

            ->add('invoicedate', DateType::class, array(
                'widget' => 'single_text',
                'data' => new \DateTime('now'),
                'format' => 'dd/MMM/yyyy',
                'label' => 'Date of invoice',
            ))

            ->add('createdate', DateType::class, array(
                'widget' => 'single_text',
                'data' => new \DateTime('now'),
                'format' => 'dd/MMM/yyyy',
                'label' => 'Date invoice recorded in database',
                'disabled' => 'true'
            ))

            ->add('amount', MoneyType::class, array(
                'label' => 'Amount',
            ))

            ->add('comments')
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Invoice',
        ));
    }

    public function getName()
    {
        return 'invoice';
    }
}

Repository:

class MemberRepository extends EntityRepository
{

    /**
     * @return \Doctrine\ORM\QueryBuilder
     */
    public function findAllActiveMembers()
    {

        return $this->getEntityManager()
            ->createQuery
            (
                'SELECT m
                 FROM AppBundle:Member m
                 WHERE m.active= :active
                 ORDER BY m.surname, m.firstname'
            )
            ->setParameter('active' ,true)
            ->getResult();
    }
}

Контроллер:

/** 
 * @Route("/batchinvoices"  ,name="batchinvoices")
 */
public function newBatchInvoicesAction(Request $request)
{
    $members = $this->getDoctrine()->getRepository('AppBundle:Members')->findAllActiveMembers(); 

    foreach ($members as $member) {
        $invoices = new Invoice();
        $invoices->setMemberId($member);
        $form=$this->createForm(InvoiceType::class, $invoices);
    }

    $form->handleRequest($request);

    if ($form->isSubmitted() && ($form->isValid())) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($invoices);
        $em->flush();
        return $this->redirectToRoute('invoices_added');
    }

    return $this->render('admin/batchinvoices.html.twig', array(
        'form' => $form->createView(),
    ));
}
  • 0
    Чтобы использовать fullName, создайте в вашей сущности публичную функцию с именем fullName и дайте ей "rsturn $ this-> firstname. ''. $ This-> surname;". Вы можете в своем коде ветки вызвать {{entity.getFullname ()}}
  • 0
    Вы смотрели на записи документации, как встроить коллекцию форм ? Я считаю, что это то, что вы ищете.
Показать ещё 2 комментария
Теги:

1 ответ

0
Лучший ответ

Причина, по которой вы получаете только последнюю запись в форме, заключается в том, что вы постоянно переписываете свои собственные переменные. Взгляните на этот код:

foreach ($members as $member) {
    $invoices = new Invoice();
    $invoices->setMemberId($member);
    $form=$this->createForm(InvoiceType::class, $invoices);
}

Вы постоянно переписываете значение $form каждый раз через свой цикл, поэтому вы можете не только обрабатывать последнюю запись, но и единственную, которая появится.

Раньше я сталкивался с этой ситуацией, но обычно просто удалялась одна запись. Если вам хорошо отображать все формы одновременно, но только обновлять один член за раз, вы можете создать единую форму для каждого члена (например, вы сейчас делаете), а затем добавить его в массив форм, который вы передаете на свой шаблон. Таким образом, ваш код теперь будет выглядеть так:

$forms = array();

foreach ($members as $member) {
    $invoices = new Invoice();
    $invoices->setMemberId($member);
    $forms[] = $this->createForm(InvoiceType::class, $invoices)->createView();
}

//...

return $this->render('admin/batchinvoices.html.twig', array(
    'forms' => $forms,
));

Обратите внимание, что я ->createView() что является приемлемым для шаблона Twig. Тогда ваш шаблон ветки будет выглядеть примерно так:

{% for form in forms %}
    {{ form_start(form) }}
    {{ form_widget(form) }}
    {{ form_end(form) }}
{% endfor %}

Очевидно, вы можете изменить шаблон по своему вкусу или даже поместить индивидуальное определение формы в свой собственный шаблон и включить его так:

{% for form in forms %}
    {{ include('@AppBundle/YourController/batchInvoiceForm.html.twig', {'form': form}) }}
{% endfor %}

Имейте в виду, что выполнение этого позволит вам обновлять только одну запись за раз, поэтому, если вы не хотите обновлять страницу, вам нужно создать запрос AJAX, который отправляет отдельное действие контроллера и обрабатывает изменение результатов и отправку ответа об успешном завершении/отказе. Преимущество этого в том, что вы не публикуете кучи данных для всех участников, когда вы только изменяете небольшую сумму.

Было бы проще просто отобразить всю информацию о членах, а затем нажать кнопку "Редактировать", которая приведет вас к отдельной форме только для обновления этого члена, который затем перенаправит обратно в ваш список после отправки - если мне не нужны "на лету" или "массовые" обновления Я всегда езжу по этому маршруту.

Если вы хотите обновить все записи сразу, вам придется использовать CollectionType, как сказал Онема.

  • 0
    Большое спасибо! :) Это очень похоже на то, что я закончил делать после того, как придерживался collectionType (в итоге я создал сущность BatchInvoice ). Один вопрос, однако, я использовал $form=$this->createForm(InvoiceBatchType::class, $batch); (т.е. нет ->createView(); ) ... так что я прав, думая, что createview() означает, что вы можете циклически создавать html для каждого $ member без необходимости в сущности InvoiceBatchType ?
  • 0
    Все, что createView() , - это принимает форму Symfony и преобразует ее в формат, который Twig поймет, когда вы попытаетесь вызвать функции, которые обращаются к ней (например, form_start(form) . Все, что в моем примере выше, это то, что вы делаете для одиночная форма (создайте ее, затем подготовьте для Twig) и сделайте это в массиве из цикла.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню