Symfony2 使用 EAV 返回值形成动态选择字段

Posted

技术标签:

【中文标题】Symfony2 使用 EAV 返回值形成动态选择字段【英文标题】:Symfony2 Form Dynamic Choice Field With EAV Returned Values 【发布时间】:2012-09-09 16:14:01 【问题描述】:

我正在使用 Symfony2 和 Doctrine2 创建一个电子商务捆绑包。我正在将 EAV 方法应用于产品功能和无限功能的产品价值。为此,我有三个基本实体:Product、FeatureKind 和 FeatureValues。

FeatureKind 通过 OneToMany 单向与 FeatureValues 连接 关系。 产品通过多对多关系连接到 FeatureKind。

问题是我需要 FeatureType 作为标签,它的各种值作为产品表单中的选择字段。我已经设法在产品表单中获得了特征种类和相关值,但我不知道如何将它们变成选择字段。

以下是所有三个实体,控制器和表单代码以及我的代码的结果。

注意:我已经从代码中删除了多余的东西以保持简短。

产品.php

namespace Webmuch\ProductBundle\Entity;

/**
 * @ORM\Table()
 * @ORM\Entity
 */
class Product

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

    /**
     * @ORM\Column(name="title", type="string", length=255)
     */
    private $title;

    /**
     * @ORM\ManyToMany(targetEntity="FeatureKind", inversedBy="product", cascade="persist")
     * @ORM\JoinTable(name="product_featurekind")
     **/
    private $featurekind;

FeatureKind.php

namespace Webmuch\ProductBundle\Entity;

/**
 * @ORM\Table(name="feature_kind")
 * @ORM\Entity
 */
class FeatureKind

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

    /**
     * @ORM\Column(name="name", type="string", length=50)
     */
    protected $name;

    /**
     * @ORM\ManyToMany(targetEntity="FeatureValue")
     * @ORM\JoinTable(name="feature_kind_value",
     *      joinColumns=@ORM\JoinColumn(name="kind_id", referencedColumnName="id"),
     *      inverseJoinColumns=@ORM\JoinColumn(name="value_id", referencedColumnName="id", unique=true)
     *      )
     **/
    protected $values;   

FeatureValue.php

namespace Webmuch\ProductBundle\Entity;

/**
 * @ORM\Table()
 * @ORM\Entity
 */
class FeatureValue

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

    /**
     * @ORM\Column(name="value", type="string", length=100)
     */
    protected $value;

ProductController.php

public function newAction(Request $request)

    $entity = new Product();
    $em = $this->getDoctrine()->getEntityManager();
    $features = $em->getRepository('ProductBundle:FeatureKind')->findAll();

    foreach($features as $feature)
    
        $featurekind = new FeatureKind();
        $featurekind->setTitle($feature->getTitle());
        foreach($feature->getValue() as $value )
            $featurekind->getValue()->add($value);
        
        $entity->getFeaturekind()->add($featurekind);   
    

    $form = $this->createForm(new ProductType(), $entity);

     if ('POST' === $request->getMethod()) 
        $form->bindRequest($request);
        if ($form->isValid()) 
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('product_show', array(
                'id' => $entity->getId()
            )));
        
    
    return $this->render('ProductBundle:Product:new.html.twig', array(
       'form'   => $form->createView()
    ));

ProductType.php

namespace Webmuch\ProductBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class ProductType extends AbstractType

    public function buildForm(FormBuilder $builder, array $options)
    
        $builder
            ->add('featurekind', 'collection', array('type' => new FeatureKindType()))
            ->getForm();
        

    public function getDefaultOptions(array $options)
    
        return array(
            'data_class' => 'Webmuch\ProductBundle\Entity\Product',
            'required' => true
        );
    

    public function getName()
    
        return 'product';
    

FeatureKindType.php

namespace Webmuch\ProductBundle\Form;

class FeatureKindType extends AbstractType

    public function buildForm(FormBuilder $builder, array $options)
    
        $builder
            ->add('title')
            ->add('value','collection', array(
                                               'type' => new FeatureValueType(),
                                               'allow_add'=>true))
            ->getForm();
    

    public function getDefaultOptions(array $options)
    
        return array(
            'data_class' => 'Webmuch\ProductBundle\Entity\FeatureKind',
        );
    

    public function getName()
    
        return 'featurekind';
    

编辑:

经过几天的工作,我现在陷入了一系列简单的特征及其各自的多个值:

Array
(
    [Color] => Array
        (
            [Red] => Red
            [Green] => Green
        )

    [Size] => Array
        (
            [Large] => Large
            [Medium] => Medium
            [Small] => Small
        )

    [Sleeve Style] => Array
        (
            [Half Sleeved] => Half Sleeved
            [Full Sleeved] => Full Sleeved
            [Cut Sleeves] => Cut Sleeves
        )

)

我尝试按如下方式创建表单:$this->choices 包含数组。

$builder
    ->add('name')
    ->add('slug')
    ->add('active')
;

foreach ($this->choices as $choice) 
    $builder->add('featurekind', 'choice', array(
        'required' => 'false',
        'choices' => $choice,
        'empty_value' => 'Choose an option',
        'empty_data'  => null
    ));


$builder->getForm();

上述方法不适用于 $featurekind 属性。我得到了错误:

Notice: Object of class Doctrine\Common\Collections\ArrayCollection could not be converted to int in /vagrant/project/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php line 457

尽管如果表单域附加到任何非关联属性,例如:$name,它仍然只为循环的最后一次迭代创建一个表单域。

我没有选择。

【问题讨论】:

你没有显示你的 FeatureKindType 我的错。我刚刚添加了它。谢谢! 现在你没有显示 FeatureValueType。你想得到什么?一个带有 FeatureKind 名称的标签,然后是一个包含所有可能特征值的下拉菜单? @carlos-granados 这正是我想要的。 FeatureValueType 只有一个属性$builder->add('value') 的简单文本字段。这就是我没有发布它的原因。 我现在得走了。我会在几个小时后回来。如果你还没有找到解决方案,我会试试看 【参考方案1】:

你想做的事不能用你目前的结构来完成。让我试着解释一下:FeatureKind 与 FeatureValue 是一对多的关系。这意味着您可以拥有一种“颜色”类型,其值可以是“红色”、“粉红色”等。这很好。但是你的产品实体有一个 FeatureKind 对象的集合,所以它可以有一个像“颜色”、“尺寸”等的列表......但是(这是最重要的部分)它无法将特定的值分配给任何这些种类中的:没有任何属性具有每种种类的特定值。我希望你能理解这一点,这有点难以解释。

你需要做什么:

按原样定义您的 FeatureValue 和 FeatureKind 类。

定义一个新实体来处理产品的种类和价值之间的关联:

namespace Webmuch\ProductBundle\Entity;

/**
 * @ORM\Table()
 * @ORM\Entity
 */
class FeatureKindValue

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

    /**
     * @ManyToOne(targetEntity="Product", inversedBy="features")
     **/
    private $product;

    /**
     * @ORM\ManyToOne(targetEntity="FeatureKind")
     **/
    protected $kind;   

    /**
     * @ORM\ManyToOne(targetEntity="FeatureValue")
     **/
    protected $value;   

该实体处理成对的 kind:value,例如 color:red

最后,您的产品实体具有这种新类型的属性:

namespace Webmuch\ProductBundle\Entity;

/**
 * @ORM\Table()
 * @ORM\Entity
 */
class Product

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

    /**
     * @ORM\Column(name="title", type="string", length=255)
     */
    private $title;

    /**
     * @ORM\OneToMany(targetEntity="FeatureKindValue", mappedBy="product")
     **/
    private $features;

然后,为了根据需要显示表单,请执行类似于此 *** question 答案中给出的说明的操作

【讨论】:

感谢您的回答,我已经过去并尝试过您的方法,但它不起作用。如果您看到图像,我的表单中最前面的所有正确值已经连接到正确的类型。我所需要的只是将种类变成具有所有值的选择字段的标签。这将使产品具有尺寸:大和颜色:例如绿色。 然后我将创建多个这些产品并将它们连接到 MasterProduct 并再次从多个连接的产品实体的所有值中选择一个字段。这个问题只询问已经具有表单中所有值但作为文本字段的单个子产品表单,我希望它们作为选择字段。 我可能无法得到答案,也没有得到完整的概念。请让我知道。再次感谢。 不知道如何得到你想要的,但我相信你的原始结构无法完成 这简直太完美了。非常感谢。【参考方案2】:

这种东西可能很棘手,但您可以在 FeatureKindType 类中使用 this approach:

public function buildForm(FormBuilderInterface $builder, array $options)

    $builder->addEventListener(
        FormEvents::PRE_SET_DATA,
        function (DataEvent $event) use ($builder) 
            /* @var FormBuilderInterface $builder */
            $form = $event->getForm();
            /* @var FeatureKind $data */
            $data = $event->getData();
            if ($data !== null) 
                $form->add(
                    $builder->getFormFactory()->createNamed(
                        'values',
                        'entity',
                        null,
                        array(
                            'label' => $data->getName(),
                            'class' => 'WebmuchProductBundle:FeatureValue',
                        )
                    )
                );
            
        
    );

请注意,我没有尝试在您的情况下发布表单并保存实体,但该表单现在将 FeatureKind 的名称显示为标签和包含相应 FeatureKindValues 的下拉选择。

我在我的一个项目中使用这种方法,它对我有用。

【讨论】:

以上是关于Symfony2 使用 EAV 返回值形成动态选择字段的主要内容,如果未能解决你的问题,请参考以下文章

选择 EAV 方案中价格的所有产品

实体-属性-值 (EAV) 的替代方案?

如何将动态数量的输入值传递给 Symfony2.1 中的控制器

Symfony2 禁用选择

雄辩的 ORM 和 EAV

动态表单(切换实体)symfony2