Поле динамического выбора формы Symfony2 с возвращаемыми значениями EAV
Я создаю пакет электронной коммерции с Symfony2 и Doctrine2. Я применяю подход EAV для функций продукта и значений продукта для неограниченных возможностей. Для этого у меня есть три основных объекта: Product, FeatureKind и FeatureValues.
- FeatureKind связан с FeatureValues с однонаправленным OneToMany
отношения.
- Продукт подключен к FeatureKind с отношением ManyToMany.
Проблема в том, что мне нужен FeatureType в качестве меток, а в виде продукта - различные поля в качестве поля выбора. Мне удалось получить свойство и связанные значения в форме продукта, но я не знаю, как превратить их в поля выбора.
Ниже перечислены все три сущности, контроллер и код формы и результат моего кода.
Примечание. Я удалил лишние вещи из кода, чтобы он был коротким.
product.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';
}
}
![This is my form result.]()
EDIT:
Через несколько дней работы я застрял с простым набором функций и их несколькими значениями:
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- > choice содержит массив.
$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, оно все равно создает только одно поле формы для последней итерации цикла.
У меня нет вариантов.
Ответы
Ответ 1
То, что вы хотите сделать, не может быть выполнено с вашей текущей структурой. Позвольте мне попытаться объяснить: FeatureKind имеет отношение один к многим с FeatureValue. Это означает, что вы можете иметь "цветной" вид, который может иметь значения "красный", "розовый" и т.д. Это нормально. Но ваш объект продукта имеет коллекцию объектов FeatureKind, поэтому у него может быть список "Цвет", "Размер" и т.д. НО (это самая важная часть), он не имеет никакого отношения к определенному значению для любого этих видов: нет свойства, которое имеет конкретное значение для каждого вида. Надеюсь, вы могли бы разобраться в этом, его немного сложно объяснить.
Что вам нужно сделать:
Определите классы FeatureValue и FeatureKind так, как они есть.
Определите NEW объект, который обрабатывает связь между видом и значением для продукта:
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;
}
Этот объект обрабатывает пары вида: значение, например цвет: красный
Наконец, ваш объект продукта имеет свойство этого нового типа:
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;
}
Затем, чтобы представить форму по вашему желанию, сделайте что-то похожее на инструкции, приведенные в ответе на этот stackoverflow question
Ответ 2
Этот тип материала может быть сложным, но вы можете использовать этот подход в своем классе FeatureKindType
:
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
.
Я использую этот подход в моем проекте, и он работает для меня.