Symfony2-如何定义窗体和控制器以一次动态创建实体的多个对象? [英] Symfony2 - How do I define Form and Controllers to dynamically create multiple objects of a entity at once?

查看:75
本文介绍了Symfony2-如何定义窗体和控制器以一次动态创建实体的多个对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Symfony2 2.7

Symfony2 2.7

我环顾四周,但是找不到一种简洁,标准的定义方式:

I have looked around, but I do not find a neat, standard way to define:

(*)表单(formType)和所需的任何控制器,让用户可以动态创建并且可以随心所欲地创建任意数量的对象.

(*) a form (formType) and whatever controller you need to let a user to create dynamically and at once as many objects as she wishes.

我在这里这里,但是我无法重现他们的解决方案.

I see here and here, but I am not able to reproduce their solutions.

问题是:有没有一种标准的方法(*)?

Question is: is there a standard way to do (*)?

注意:似乎无济于事,即使它只允许用户(动态)创建多个Tag对象,也只能创建一个唯一的Task对象.

Note: This does not seem to help, in the sense that it creates only a unique Task object, even if it let a user to create (dynamically) several Tag ones.

解决方案 可以很好地执行以下操作:用户可以根据需要添加任意数量的对象.

PARTIAL SOLUTION The following works fine: a user may add as many objects as she wishes.

我需要检查是否可以某种方式清除代码. 如果您认为有改进的方法,请发表评论.

I need to check whether I may clean the code somehow. If you see a way to improve it, please comment.

最终,我希望有一个Job 1:M Tasks 1:M Tags的情况,该表单具有一次创建多个Job,每个Job多个任务,每个Task多个标签的表单.我需要检查是否可以嵌入相关的1:M实体的表单.敬请期待!

Eventually, I wish to have a Job 1:M Tasks 1:M Tags situation, with a form to create at once many Jobs, many Tasks for each Job, many Tags for each Task. I need to check that I may embed forms for the related 1:M entities. Stay tuned!

控制器

/**
 * Creates a new Job entity.
 *
 * @Route("/", name="Myname_Job_create")
 * @Method("POST")
 * @Template("MynameBlogBundle:Job:new.html.twig")
 */
public function createAction(Request $request)
{
    ////var_dump($request); die('here'); // this helped a lot to understand
    $postData = $request->request->get('form');
    $entity = array();
    foreach($postData['jobs'] as $key => $obj){$entity[$key]= new Job();}
    $form = $this->createCreateForm($entity);
    $form->handleRequest($request);

    if ($form->isValid()) {
       try {

            $em = $this->getDoctrine()->getEntityManager();
            foreach($entity as $ent){ $em->persist($ent); }
            $em->flush();  

        } catch (\Doctrine\DBAL\DBALException $e) {
            die($e->getMessage());
    }
        return $this->redirect($this->generateUrl('Myname_Job'));
    }
    return array(
        'info' =>   $postData,
        'entity' => $entity,
        'form'   => $form->createView(),
    );
}

/**
 * Creates a form to create a Job entity.
 *
 * @param Job $entity The entity
 *
 * @return \Symfony\Component\Form\Form The form
 */
private function createCreateForm(Array $jobs)
{
   $form = $this->createFormBuilder(array('jobs'=>$jobs))
            ->setAction($this->generateUrl('Myname_Job_create'))
            ->add('jobs','collection',array(
                'required'       => true,
                'allow_add'      => true,
                'allow_delete'  => true,
                'type'           => new JobType(),
           ))
            ->add('submit', 'submit',array('label' => 'Create'))
            ->getForm()
        ;

    return $form;
}

/**
 * Displays a form to create a new Job entity.
 *
 * @Route("/new", name="Myname_Job_new")
 * @Method("GET")
 * @Template()
 */
public function newAction()
{


    $jobs = array(0 => new Job());
    $form = $this->createCreateForm($jobs);

    return array(
        'entity' => $jobs,
        'form'   => $form->createView(),
    );
}

JobType

class JobType extends AbstractType
{
  /**
  * @param FormBuilderInterface $builder
  * @param array $options
  */

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('title', 'text', array(
            'label' => 'Write Title   ',
            'required' => true
        ))
        ->add('description','text',array(
            'label'     => 'Write Descr.  ',

        )) 
    ;
}
....
}

新的.twig模板

{% extends '::base.html.twig' %}

{% block javascripts %}
     {{ parent() }}

        <script src="{{ asset('bundles/mynameblog/js/JQuery/jquery-2.1.0.js') }}" type="text/javascript">
</script>
<script>
var $collectionHolder;

// setup an "add aJobs" link
var $addJobLink = $('<a href="#" class="add_Job_link">Add Jobs</a>');
var $newLinkLi = $('<li></li>').append($addJobLink);

      function addJobForm($collectionHolder, $newLinkLi) {
// Get the data-prototype explained earlier
var prototype = $collectionHolder.data('prototype');

// get the new index
var index = $collectionHolder.data('index');

// Replace '__name__' in the prototype's HTML to
// instead be a number based on how many items we have
var newForm = prototype.replace(/__name__/g, index);

// increase the index with one for the next item
$collectionHolder.data('index', index + 1);

// Display the form in the page in an li, before the "Add a Jobs" link li
var $newFormLi = $('<li></li>').append(newForm);
$newLinkLi.before($newFormLi);
    }

jQuery(document).ready(function() {
// Get the ul that holds the collection of Jobs
$collectionHolder = $('ul.jobs');

// add the "add Jobs" anchor and li to the Jobs ul
$collectionHolder.append($newLinkLi);

// count the current form inputs we have (e.g. 2), use that as the new
// index when inserting a new item (e.g. 2)
$collectionHolder.data('index', $collectionHolder.find(':input').length);

$addJobLink.on('click', function(e) {
    // prevent the link from creating a "#" on the URL
    e.preventDefault();

    // add a newJobs form (see next code block)
    addJobForm($collectionHolder, $newLinkLi);
});
});
</script>
{% endblock %}
{% block main -%}  
 <h1>Job creation</h1>

{{ form_start(form) }}

<ul class="jobs" data-prototype="{{ form_widget(form.jobs.vars.prototype)|e }}">

{# render the job-s only two fields: title description #}

{% for f in form.jobs %}
           <li>   {{ form_row(f) }} </li>
            {% endfor %}
 </ul>
 {{ form_end(form) }}
 {% endblock %}

推荐答案

建议:通过一个客户端操作添加多个对象的最佳方法:

  • 为每个对象创建一个表单(只需使用JS复制即可)
  • 使用JS,侦听提交"按钮,并使用不同的ajax请求发送每个表单.

所以这很简单... 无需复杂的表单即可在接收已发布请求的控制器中管理服务器实体.

So it is very simple ... No need to have a complicated form por to manage serveral entities in the controller receiving the posted request.

但是,要做您想做的事情:

这是一种执行所需操作的方法. 我只强调了如何在服务器端修改表格. 我无法测试您的JS,在这里我假设它在您的表单中添加了[title_1,description_1,title_2,description_2,...]之类的字段.

Here is a way to do what you want. I have only highlighted how to adapt the form on the server side. I cannot test your JS, and here I'm assuming it add fields like [title_1,description_1,title_2,description_2,...] in your form.

您还应该调整:

  • 如何创建实体
  • 您如何验证它们

我建议您使用"objects_count"选项来修改表格: (请注意,您应该处理不带类的表单(data_class不应在configureOptions中定义))

I suggest you to adapt your form with an option "objects_count" : (notice that you should deal with a form without class (data_class should not be defined in configureOptions))

class JobType extends AbstractType
{
  /**
  * @param FormBuilderInterface $builder
  * @param array $options
  */

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // This is called once you posted your form to recreate the JS generated
        // fiels on the server side. Otherwise, these fiels are ignorated
        if($options['objects_count']>0){
            for($i=0;$i<$options['objects_count'];$i++){
                $builder
                    ->add('title_'.$i, TextType::class)
                    ->add('description_'.$i, TextType::class) 
                ;
            }
        }

        // This is called to render the first form
        else{
            $builder
                ->add('title', TextType::class)
                ->add('description', TextType::class) 
            ;
        }
    }

    /**
    * @param OptionsResolver $resolver
    */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array('objects_count'=>0));
    }
}

然后,要处理此表单,您必须获取已发布字段的数量:

Then, to deal with this form you have to get the count of posted fields :

您的控制器:

/**
 * Creates a new Job entity.
 *
 * @Route("/", name="Myname_Job_create")
 * @Method("POST")
 * @Template("MynameBlogBundle:Job:new.html.twig")
 */
public function createAction(Request $request)
{
    $i = 1;
    $defaults = array();
    while($request->request->has('title_'.$i)){
        $defaults += array('title_'.$i=>"",'description_'.$i=>"");
        $i++;
    }


    $objects_count = $i-1;
    if($objects_count === 0){
        $defaults = array('title'=>"",'description'=>"");
    }


    $form = $this->$defaults(JobType::class,$defaults,array('objects_count'=>$objects_count));
    $form->handleRequest($request);

    // ...
}

这篇关于Symfony2-如何定义窗体和控制器以一次动态创建实体的多个对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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