【问题标题】:symfony2: multiple entities one formsymfony2:多个实体一种形式
【发布时间】:2014-09-24 22:55:15
【问题描述】:

我有 2 个实体:

ADS\LinkBundle\Entity\Link:
type: entity
table: null
repositoryClass: ADS\LinkBundle\Entity\LinkRepository
id:
    id:
        type: integer
        id: true
        generator:
            strategy: AUTO
fields:
    dateAdded:
        type: datetime
    expirationDate:
        type: datetime
        nullable: true
    designator:
        type: string
        length: 255
        nullable: false
        unique: true
    slug:
        type: string
        length: 255
        nullable: true
        unique: true
manyToOne:
    company:
        targetEntity: ADS\UserBundle\Entity\Company
        inversedBy: link
        joinColumn:
            name: company_id
            referencedColumnName: id
        nullable: true
    createdBy:
        targetEntity: ADS\UserBundle\Entity\User
        inversedBy: link
        joinColumn:
            name: createdBy_id
            referencedColumnName: id
    domain:
        targetEntity: ADS\DomainBundle\Entity\Domain
        inversedBy: link
        joinColumn:
            name: domain_id
            referencedColumnNames: id
oneToMany:
        paths:
            targetEntity: ADS\LinkBundle\Entity\Path
            mappedBy: link
            cascade: [persist]
lifecycleCallbacks: {  }

ADS\LinkBundle\Entity\Path:
type: entity
table: null
repositoryClass: ADS\LinkBundle\Entity\PathRepository
id:
    id:
        type: integer
        id: true
        generator:
            strategy: AUTO
fields:
    pathAddress:
        type: string
        length: 255
    pathWeight:
        type: string
        length: 255
manyToOne:
    link:
        targetEntity: ADS\LinkBundle\Entity\Link
        inversedBy: paths
        joinColumn:
            name: link_id
            referencedColumnName: id
lifecycleCallbacks: {  }

除了实体的路径部分外,我已经弄清楚了一切。这是用于 A/B 拆分测试,因此每个链接可以有 2 条路径。每个路径将包含一个网址和一个数字(0 - 100)

这是我当前状态的表单:

<?php
namespace ADS\LinkBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class PathType extends AbstractType {

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder
        ->add('pathAddress')
        ->add('pathWeight')
    ;
}

public function setDefaultOptions(OptionsResolverInterface $resolver) {
    $resolver->setDefaults(array('data_class' => 'ADS\LinkBundle\Entity\Path'));
}
public function getName() { return 'ads_linkbundle_link'; }
}

<?php
namespace ADS\LinkBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

    class LinkType extends AbstractType {

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder
        ->add('designator')
        ->add('domain', 'entity', array(
            'class' => 'ADS\DomainBundle\Entity\Domain',
            'property' => 'domainAddress'
        ))
        ->add('paths', 'collection', array('type' => new PathType(), 'allow_add' => true))
        ->add('Submit', 'submit')
    ;
}

public function setDefaultOptions(OptionsResolverInterface $resolver) {
    $resolver->setDefaults(array('data_class' => 'ADS\LinkBundle\Entity\Link'));
}
public function getName() { return 'ads_linkbundle_link'; }
}

我需要弄清楚的是,在创建链接时,我还需要能够创建正确的路径和权重来配合它。在创建链接之前,路径不会在数据库中。

这是我的控制器:

 public function newAction(Request $request) {
    $entity = new Link();
    $form = $this->createForm(new LinkType(), $entity);
    if ($request->isMethod('POST')) {
        $form->handleRequest($request);
        if ($form->isValid()) {
            $code = $this->get('ads.default');
            $em = $this->getDoctrine()->getManager();
            $user = $this->getUser();
            $entity->setDateAdded(new \DateTime("now"));
            $entity->setCreatedBy($user);
            $entity->setSlug($code->generateToken(5));
            $entity->setCompany($user->getParentCompany());
            $em->persist($entity);
            $em->flush();
            return new Response(json_encode(array('error' => '0', 'success' => '1')));
        }
        return new Response(json_encode(array('error' => count($form->getErrors()), 'success' => '0')));
    }

    return $this->render('ADSLinkBundle:Default:form.html.twig', array(
        'entity' => $entity,
        'saction' => $this->generateUrl('ads.link.new'),
        'form' => $form->createView()
    ));
}

【问题讨论】:

  • 我不确定这个问题是否很清楚。您需要知道如何创建路径和权重,但您并没有告诉我们信息来自何处。如果此信息来自请求,并且表单和实体设置正确,您应该能够使用表单创建或编辑集合(相关实体)。查看 Symfony 文档How to embed a collection of forms 了解更多信息。
  • 我告诉你信息实际上来自哪里。它将来自表格,但因为在表格不完整之前我还没有这样做。寻找有关如何拥有我的链接表单的指导,包括创建多个“路径”的能力。
  • 看看我在第一条评论上发布的链接。文档将引导您了解如何向任务添加多个标签。你的问题也是类似的,你需要给一个链接添加多个路径,对吧?您可能需要为路径添加额外的表单类型并将其作为集合添加到链接中。
  • 这帮了很多忙...我现在唯一的问题是关系。链接是单对路径,路径是多对链接。当我提交表单时,它会插入所有内容,但链接 ID 未存储在路径表中。也许我的关系错了。我编辑了问题,以适应新的变化。
  • 想通了。在我的表单上,我必须添加'by_reference' =&gt; false 并在我的Link Entity 中修改addPath() 方法

标签: symfony symfony-forms


【解决方案1】:

感谢@Onema(阅读上面的 cmets),我已经弄清楚了。通过阅读http://symfony.com/doc/current/cookbook/form/form_collections.html 的文档,它为我提供了完成这项工作所需的信息。

做我需要做的第一步是创建一个名为PathsType.php 的新form type,其中包含与Paths Entity 关联的字段

<?php
namespace ADS\LinkBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class PathType extends AbstractType {

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder
        ->add('pathAddress')
        ->add('pathWeight')
    ;
}

public function setDefaultOptions(OptionsResolverInterface $resolver) {
    $resolver->setDefaults(array('data_class' => 'ADS\LinkBundle\Entity\Path'));
}
public function getName() { return 'ads_linkbundle_path'; }
}

然后修改LinkType.php 以使用这个新表单

<?php
namespace ADS\LinkBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class LinkType extends AbstractType {

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder
        ->add('designator')
        ->add('domain', 'entity', array(
            'class' => 'ADS\DomainBundle\Entity\Domain',
            'property' => 'domainAddress'
        ))
        ->add('paths', 'collection', array(
                'type' => new PathType(), 
                 'allow_add' => true,))
        ->add('Submit', 'submit')
    ;
}

public function setDefaultOptions(OptionsResolverInterface $resolver) {
    $resolver->setDefaults(array('data_class' => 'ADS\LinkBundle\Entity\Link'));
}
public function getName() { return 'ads_linkbundle_link'; }
}

添加allow_add 使得您可以添加该表单的多个实例。

在视图中,我现在使用data-prototype 属性。在文档中,它有一个使用列表项的示例 - 这就是我开始的地方。

&lt;ul class="tags" data-prototype="{{ form_widget(form.paths.vars.prototype)|e }}"&gt;&lt;/ul&gt;

然后是 jQuery 函数(在上面的文档链接中列出,简单的复制/粘贴就可以了)

这使系统正常工作,有 1 个小问题,在我的 paths entity 中,我与 Link entity 有关系,但它没有注意到这种关系,并且 link_id 字段为 null

为了解决这个问题,我们再次编辑LinkType.php,并将by_reference = false 添加到collection 定义中。然后我们编辑实体内部的addPath 方法,如下所示:

public function addPath(\ADS\LinkBundle\Entity\Path $paths)
{
    $paths->setLink($this);
    $this->paths->add($paths);
}

这会将当前链接对象设置为与路径关联的链接。

此时,系统运行正常。它创造了它需要的一切,只需要稍微调整一下显示。我个人选择使用twig macro 来修改data-prototype 中包含的html 输出

我的宏当前所在的位置(不完整 - 但有效),我将其添加到 form.html.twig 的开头

{% macro path_prototype(paths) %}
    <div class="form-group col-md-10">
        <div class="col-md-3">
            <label class="control-label">Address</label>
        </div>
        <div class="col-md-9">
            {{ form_widget(paths.pathAddress, { 'attr' : { 'class' : 'form-control required' }}) }}
        </div>
    </div>
{% endmacro %}

在表单本身的HTML 中,我删除了list 创建,并将其替换为:

<div class="form-group">
        {{ form_label(form.paths,'Destination(s)', { 'label_attr' : {'class' : 'col-md-12 control-label align-left text-left' }}) }}
        <div class="tags" data-prototype="{{ _self.path_prototype(form.paths.vars.prototype)|e }}">
        </div>
    </div>

然后我修改了我的 javascript 以使用 div 作为起点,而不是示例中的 ul

<script type="text/javascript">
    var $collectionHolder;

    // setup an "add a tag" link
    var $addTagLink = $('<a href="#" class="add_tag_link btn btn-xs btn-success">Add Another Destination</a>');
    var $newLinkLi = $('<div></div>').append($addTagLink);

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

        // add the "add a tag" anchor and li to the tags 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);
        addTagForm($collectionHolder, $newLinkLi);

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

            // add a new tag form (see next code block)
            addTagForm($collectionHolder, $newLinkLi);
        });
    });

    function addTagForm($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);
        console.log(index);
        if (index == 1) {
            console.log('something');
            $('a.add_tag_link').remove();
        }
        // Display the form in the page in an li, before the "Add a tag" link li
        var $newFormLi = newForm;
        $newLinkLi.before($newFormLi);
    }
</script>

由于这些 paths 是我的营销应用程序中 A/B 拆分测试的目标地址,因此我选择将路径限制为每个链接 2 个。有了这个,我已经成功地设置了一个表单来使用 collections 类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多