如何在 Symfony2 中创建一个包含一个实体的多行的表单 [英] How to create a form with multiple rows of one entity in Symfony2

查看:25
本文介绍了如何在 Symfony2 中创建一个包含一个实体的多行的表单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我阅读了 Collection Field Type如何嵌入表单集合 ...示例是关于一个实体(Task)与另一个实体(Tag)有一对多关系,我理解,但我无法适应我想要的!

为了简单起见,假设我有一个任务实体,这个任务实体与其他对象有一些关系,比如用户和项目(每个任务可以有一个用户和一个项目)

我想制作一个表单,在这个表单中列出一个任务列表,每个任务在一个表格的一行中,显示诸如task.titletask.startdate之类的信息>、task.user.nametask.user.company.nametask.project.name,它有 2 个可编辑的字段,文本框说明"复选框活动".您可以编辑多个任务并使用主表单中表格底部的一个按钮提交表单,因此基本上您应该能够在一次事务中更新多条记录(而不是每行制作一个表单和一个提交按钮,因此每次提交更新一条记录).

我对这个复杂的设计有很多问题:

首先,我想按照示例将一组表单嵌入到主表单中,因此我为我的任务创建了一个表单类型,它应该像每行一个表单.我制作了这些文件:

任务的表单类型:

//src/Acme/TaskBundle/Form/Type/TaskType.php命名空间 AcmeTaskBundleFormType;使用 SymfonyComponentFormAbstractType;使用 SymfonyComponentFormFormBuilderInterface;使用 SymfonyComponentOptionsResolverOptionsResolverInterface;类 TaskType 扩展 AbstractType{公共函数 buildForm(FormBuilderInterface $builder, array $options){$builder->add('description', 'text', ['label' => false, 'required' => false, 'attr' => ['placeholder' => 'description']]);$builder->add('active', 'checkbox', ['label' => false, 'required' => false, 'data' => true]);}公共函数 setDefaultOptions(OptionsResolverInterface $resolver){$resolver->setDefaults(array('data_class' =>'AcmeTaskBundleEntityTask',));}公共函数 getName(){返回任务类型";}}

主表单的表单类型:

//src/Acme/TaskBundle/Form/Type/SaveTasksType.php命名空间 AcmeTaskBundleFormType;使用 SymfonyComponentFormAbstractType;使用 SymfonyComponentFormFormBuilderInterface;使用 SymfonyComponentOptionsResolverOptionsResolverInterface;使用 AcmeTaskBundleFormTypeTaskType.php;类 SaveTasksType 扩展 AbstractType{公共函数 buildForm(FormBuilderInterface $builder, array $options){$builder->add('tasksCollection', 'collection', ['type' => new TaskType()]);$builder->add('tasksSubmit', 'submit', ['label' => 'Save']);}公共函数 setDefaultOptions(OptionsResolverInterface $resolver){$resolver->setDefaults(['属性' =>['类' =>'形式水平'],'方法' =>'邮政']);}公共函数 getName(){返回 '​​saveTasksType';}}

任务表单控制器:

//src/Acme/TaskBundle/Controller/ManageTasksController.php命名空间 AcmeTaskBundleController;使用 AcmeTaskBundleEntityTask;使用 AcmeTaskBundleFormTypeSaveTaskType;使用 SymfonyComponentHttpFoundationRequest;使用 SymfonyBundleFrameworkBundleControllerController;类 ManageTasksController 扩展控制器{公共函数 showListAction(Request $request){$repository = $this->getDoctrine()->getRepository('ExampleBundle:Task');$tasks = $repository->findAll();$taskSaveForm = $this->createForm(new SaveTasksType(['tasks' => $tasks]));return $this->render('AcmeTaskBundle:Task:list.html.twig', array('taskSaveForm' =>$taskSaveForm->createView(),));}}

Task Form Twig Template(只是相关部分):

{{ form_start(taskSaveForm) }}{{ form_errors(taskSaveForm) }}<table class="table table-bordered table-striped table-primary list-table"><头><tr><th>任务ID</th><th>Title</th><th>开始日期</th><th>用户</th><th>公司</th><th>项目</th><第>描述<th>Active</th></tr></thead>{% 用于 taskSaveForm.tasksCollection 中的任务 %}<tr><td>{{ task.id }}</td><td><a href="https://localhost/taskid={{ task.id }}">{{ task.title }}</a></td><td>{{ task.startDate }}</td><td>{{ task.userName }}</td><td>{{ task.companyName }}</td><td>{{ task.projectName }}</td><td>{{ form_widget(task.description) }}</td><td>{{ form_widget(task.active) }}</td><td></td></tr>{% 结束为 %}</tbody><div>{{ form_row(taskSaveForm.tasksSubmit) }}</div>{{ form_end(taskSaveForm) }}

但是这里有一个问题,当我从查询生成器得到结果时,它是一堆包含对象的数组,我收到一个关于

的错误<块引用>

表单的视图数据应该是 AcmeTaskBundleEntityTask 类的一个实例,但它是一个 (n) 数组.您可以通过将data_class"选项设置为 null 或通过添加将 (n) 数组转换为 AcmeTaskBundleEntityTask 实例的视图转换器来避免此错误.

这是查询:

createQueryBuilder()->选择("任务编号,任务标题,任务开始日期,任务描述,用户名作为用户名,company.name 作为公司名称,项目名称作为项目名称,")->from('AcmeTaskBundleEntityTask', 'task')->innerJoin('task.project', 'project')->innerJoin('task.user', 'user')->innerJoin('AcmeTaskBundleEntityCompany', 'company', 'with', 'store.company = company')-> where('task.active = :isActive')->setParameter('isActive', true);

Soooo,我使用了部分对象指南看看它是否有帮助,它有助于在查询结果中创建任务对象,我可以提取它并将其发送到表单,但似乎表单的其余部分仍然不知道其余的对象......

好的,所以也许我选择了错误的方法,我不确定!如果您对我该怎么做有任何建议,请在此处留言...我为此苦苦挣扎了一个多星期!在此先感谢您的时间!即使你不做任何笔记,我也很感激你花时间阅读我很长的问题!谢谢!:)

解决方案

这是一个可能的解决方案的开始.下面的示例采用单个实体技能并在单个页面上显示所有这些.我不知道的是这种技术是否可以用于持久化子对象.我希望可以遍历返回的数据并根据需要保留.

下面的代码会生成一个页面,其中包含所有可能的技能列表和一个用于声明每个技能已启用或已启用的复选框.

在控制器中:

 $skills = $em->getRepository("TruckeeMatchingBundle:Skill")->getSkills();$formSkills = $this->createForm(new SkillsType(), array('skills' => $skills));...if ($request->getMethod() == 'POST') {$formSkills->handleRequest($request);foreach ($skills as $existingSkill) {$em->persist($existingSkill);}}...返回 ['formSkills' =>$formSkills->createView(),...]

在模板中:

{% for Skills in formSkills.skills %}{{ 技能.vars.value.skill }}<input type="hidden" name="skills[skills][{{ loop.index0 }}][skill]" value="{{ Skill.vars.value.skill }}"><input type="checkbox" name="skills[skills][{{ loop.index0 }}][enabled]"{%if Skill.vars.value.enabled %}checked="checked"{%endif%}{% 结束为 %}

First I've read documents for both Collection Field Type and How to Embed a Collection of Forms ... The example is about one entity (Task) that has one-to-many relation with another entity (Tag), and I understand it, but I can not adapt it to what I want!

To make it simpler, let say I have a Task entity, this task entity has some relations with other objects like user and project (each task can have one user and one project)

I want to make one form, inside this form a list of Tasks, each task in one row of a table that shows information like task.title, task.startdate, task.user.name, task.user.company.name, task.project.name, And it has 2 fields editable, textbox "Description" and checkbox "active". You can edit multiple tasks and submit the form using one button at the bottom of the table in the main form, so basically you should be able to update multiple records in one transaction (instead of making one form and one submit button per row and therefor one record update per submit).

I have many issues with this complicated design:

First I wanted to follow the sample to embed a collection of forms inside the main form, So I made a Form Type for my Task that should be like one form per row. I made these files:

Form Type for Task:

// src/Acme/TaskBundle/Form/Type/TaskType.php
namespace AcmeTaskBundleFormType;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;

class TaskType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
         $builder->add('description', 'text', ['label' => false, 'required' => false, 'attr' => ['placeholder' => 'description']]);
         $builder->add('active', 'checkbox', ['label' => false, 'required' => false, 'data' => true]);
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AcmeTaskBundleEntityTask',
        ));
    }

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

Form Type for main form:

// src/Acme/TaskBundle/Form/Type/SaveTasksType.php
namespace AcmeTaskBundleFormType;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;
use AcmeTaskBundleFormTypeTaskType.php;

class SaveTasksType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('tasksCollection', 'collection', ['type' => new TaskType()]);
        $builder->add('tasksSubmit', 'submit', ['label' => 'Save']);
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults([
            'attr' => ['class' => 'form-horizontal'],
            'method' => 'POST'
        ]);
    }

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

Tasks Form Controller:

// src/Acme/TaskBundle/Controller/ManageTasksController.php
namespace AcmeTaskBundleController;

use AcmeTaskBundleEntityTask;
use AcmeTaskBundleFormTypeSaveTaskType;
use SymfonyComponentHttpFoundationRequest;
use SymfonyBundleFrameworkBundleControllerController;

class ManageTasksController extends Controller
{
    public function showListAction(Request $request)
    {
        $repository = $this->getDoctrine()->getRepository('ExampleBundle:Task');
        $tasks = $repository->findAll();

        $taskSaveForm = $this->createForm(new SaveTasksType(['tasks' => $tasks]));

        return $this->render('AcmeTaskBundle:Task:list.html.twig', array(
            'taskSaveForm' => $taskSaveForm->createView(),
        ));
    }
}

Task Form Twig Template (just related part):

<div class="innerAll">
    {{ form_start(taskSaveForm) }}
    {{ form_errors(taskSaveForm) }}

    <table class="table table-bordered table-striped table-primary list-table">
        <thead>
            <tr>
                <th>Task ID</th>
                <th>Title</th>
                <th>Start Date</th>
                <th>User</th>
                <th>Company</th>
                <th>Project</th>
                <th>Description</th>
                <th>Active</th>
            </tr>
        </thead>
        <tbody>
            {% for task in taskSaveForm.tasksCollection %}

                <tr>
                    <td>{{ task.id }}</td>
                    <td><a href="https://localhost/taskid={{ task.id }}">{{ task.title }}</a></td>
                    <td>{{ task.startDate }}</td>
                    <td>{{ task.userName }}</td>
                    <td>{{ task.companyName }}</td>
                    <td>{{ task.projectName }}</td>
                    <td>{{ form_widget(task.description) }}</td>
                    <td>{{ form_widget(task.active) }}</td>
                    <td></td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
    <div>{{ form_row(taskSaveForm.tasksSubmit) }}</div>
    {{ form_end(taskSaveForm) }}
</div>

BUT there is an issue here, when I get the result from query builder it is a mess of arrays containing objects in them, I get an error about

The form's view data is expected to be an instance of class AcmeTaskBundleEntityTask, but is a(n) array. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) array to an instance of AcmeTaskBundleEntityTask.

This is the query:

createQueryBuilder()
    ->select(
            "
            task.id,
            task.title,
            task.startDate,
            task.description,
            user.name as userName,
            company.name as companyName,
            project.name as projectName,
            "
    )
        ->from('AcmeTaskBundleEntityTask', 'task')
        ->innerJoin('task.project', 'project')
        ->innerJoin('task.user', 'user')
        ->innerJoin('AcmeTaskBundleEntityCompany', 'company', 'with', 'store.company = company')
        ->where('task.active = :isActive')->setParameter('isActive', true);

Soooo, I used Partial Objects guide to see if it can help, it helps to make the task object in the query result and I could extract it and send it to form, but still it seems the rest of form is unaware of the rest of objects...

Ok, so maybe I'm choosing the wrong approach, I'm not sure! please if you have any suggestions about what should I do, put a note here... I'm struggling with this for more than a week! Thanks in advance for your time! Even if you don't put any note, I appreciate that you spend time reading my very long question! Thanks! :)

解决方案

Here's a start on a possible solution. The example below takes a single entity Skills and presents all of them on a single page. What I don't know is whether this technique can be used to persist children objects. I would expect one could loop through the returned data and persist as required.

The code below results in a page with a list of all possible Skills and a checkbox for declaring each enabled or enabled.

In a controller:

    $skills = $em->getRepository("TruckeeMatchingBundle:Skill")->getSkills();
    $formSkills = $this->createForm(new SkillsType(), array('skills' => $skills));
    ...
        if ($request->getMethod() == 'POST') {
            $formSkills->handleRequest($request);
            foreach ($skills as $existingSkill) {
                $em->persist($existingSkill);
            }
        }
    ...
    return ['formSkills' => $formSkills->createView(),...]

In a template:

{% for skill in formSkills.skills %}
    {{ skill.vars.value.skill }}
            <input type="hidden" name="skills[skills][{{ loop.index0 }}][skill]" value="{{ skill.vars.value.skill }}">
            <input type="checkbox" name="skills[skills][{{ loop.index0 }}][enabled]" 
            {%if skill.vars.value.enabled %}checked="checked"{%endif%}
{% endfor %}

这篇关于如何在 Symfony2 中创建一个包含一个实体的多行的表单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆