简体   繁体   English

如何为数据库中的每个活动用户创建一个具有嵌套表单的Symfony表单?

[英]How to create a Symfony form that has a nested form for each active user in the database?

I am trying to create an admin page for a sports club website so that each month, the admin user can generate new invoices ( Invoice entity) for all active members ( Member entity) of the club. 我正在尝试为体育俱乐部网站创建一个管理页面,以便每个月管理员用户都可以为俱乐部的所有活动成员( 成员实体)生成新的发票( 发票实体)。

I'm trying to create the form so that I have one row for each member pre-populated with their standard monthly fee and the current date (both of which can be changed for individual member entries if needed): 我正在尝试创建表单,以便为每个成员预填一个行,其中包含他们的标准月费和当前日期(如果需要,可以为单个成员条目更改两者):

所需表格图

I have tried just about everything I can think of to get this working in a form but so far I've had no success. 我已经尽我所能想到的一切,以某种形式使它起作用,但是到目前为止,我还没有成功。 Below is the code as it currently stands but this gives me an individual form for just the last member....any advice on what I'm doing wrong would be very welcome - thanks in advance! 下面是目前的代码,但是这给了我最后一个成员一个单独的表格。...关于我做错事情的任何建议都将非常受欢迎-预先感谢!

Entities (Member and Invoice): 实体(会员和发票):

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: 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();
    }
}

The Controller: 控制器:

/** 
 * @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(),
    ));
}

The reason you are only getting the last entry in the form is that you are continually overwriting your own variables. 之所以只获得表格中的最后一个条目,是因为您不断覆盖自己的变量。 Take a look at this code specifically: 看一下这段代码:

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

You are continually overwriting the $form value every single time through your loop, so not only can you only handle the last entry, it is the only one that will show up. 您每次在循环中每次都会不断覆盖$form值,因此不仅可以处理最后一个条目,而且它是唯一会显示的条目。

I have come across this situation before but it was usually just to delete a single record. 我之前遇到过这种情况,但通常只是删除一条记录。 If you are fine with displaying all the forms at once but only updating one member at a time, you can generate a single form for each member (like you are doing now), and then add it to a forms array that you pass to your template. 如果可以一次显示所有表单,但一次只能更新一个成员,则可以为每个成员生成一个表单(就像您现在所做的一样),然后将其添加到传递给表单的数组中模板。 So your code would now look like: 因此您的代码现在看起来像:

$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,
));

Notice that I am calling ->createView() which is what is acceptable for the Twig template. 注意,我正在调用->createView() ,这对于Twig模板是可以接受的。 Then your twig template is going to look something like this: 然后,您的树枝模板将如下所示:

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

Obviously you could change the templating to your liking, or even put the individual form definition in its own template and include that like so: 显然,您可以根据自己的喜好更改模板,甚至可以将单个表单定义放在其自己的模板中,并包括如下内容:

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

Keep in mind that doing this would only allow you to update one record at a time, so if you didn't want to refresh the page you would want to create an AJAX request that posts to a separate controller action and handles the modification of results and sending a success/fail response back. 请记住,这样做只能一次更新一条记录,因此,如果您不想刷新页面,则需要创建一个AJAX请求,该请求发布到单独的控制器操作并处理结果修改并发送成功/失败响应。 The benefit of doing it this way is that you're not posting heaps of data for all members when you're only modifying a small amount. 这样做的好处是,当您只修改少量数据时,不会为所有成员发布数据堆。

It could be simpler to just display all member information and then have an Edit button that takes you to a separate form just for updating that member that would then redirect back to your list after submitting - unless I need on-the-fly or bulk updates I always go for this route. 仅显示所有成员信息,然后具有一个“编辑”按钮,将您带到一个单独的表单以更新该成员,然后在提交后将其重定向回您的列表,可能会更简单-除非我需要即时或批量更新我总是走这条路线。

If you want to update all records at once you will have to use the CollectionType as Onema said. 如果要一次更新所有记录,则必须使用Onema所说的CollectionType

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM