У меня есть форма для вставки лица категория. У этого объекта есть два других связанных с ним объекта.
Одна связанная сущность является другой отдельной сущностью группа. Другая сущность сама ссылается на себя категория это коллекция массивов, которая представляет предварительные условия. Пока все хорошо, я могу сохранить основной объект с отношениями с правильными аннотациями ORM.
Грубая схема категория
Я создал класс типов для создания формы, как описано в рекомендациях.
$form = $this->createForm(new CategoryType($em));
Прежде чем сохранить объект, я должен его инициализировать и установить для него опубликованные данные. Размещенные связанные объекты нельзя просто установить для сохраняющейся сущности, поскольку они имеют неправильный тип данных. (Например, самоссылающаяся коллекция публикуется только в виде массива с идентификаторами, а не в виде массива выбранных элементов.)
Так что я ловлю эти необработанные данные и отдельно получаю связанные сущности от менеджера сущностей.
Вставляемый объект должен автоматически заполняться связанными объектами, без получения их отдельно через менеджер объектов
Разве это означает компонент формы, что эти связанные объекты не размещены и не доступны полностью? Или что мне не хватает в моей реализации?
Есть ли способ сделать это более автоматизированным?
В классе формы для свойства «предварительные условия» я должен был сделать mapped => false
в противном случае я получаю исключение, что был передан неправильный тип. Но в конце я хочу, чтобы форма соответствовала всем автоматически через сопоставление, без пропуска сопоставления и без получения связанных сущностей отдельно от менеджера сущностей.
class CategoryType extends AbstractType
{
public function __construct($em)
{
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$qb = $this->em->createQueryBuilder();
$categories = $qb->select('e.id, e.title')
->from('MyvendorCoreBundle:Category', 'e')
->indexBy('e', 'e.id')
->orderBy('e.title')
->getQuery()
->getResult();
$categories_choice = array_map(function ($value) {
return $value['title'];
}, $categories);
$builder->add('title')
->add('group_Id', new GroupType($this->em))
->add('preconditions', 'choice', array(
'choices' => $categories_choice,
'multiple' => true,
'mapped' => false
))
->add('save', 'submit');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Myvendor\CoreBundle\Entity\Category'
));
}
public function getName()
{
return 'category';
}
}
Контроллер метод
public function newAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$form = $this->createForm(new CategoryType($em));
// Repopulating the form after submission
$form->handleRequest($request);
// Prepare a new empty Category
$category = new Category();
if ($form->isValid()) {
/* Catch some raw datas posted from the form */
// Posted precondition category ids to get its entities more later
$precondition_category_ids = $form->get('preconditions')->getData();
// Posted group entity that have only filled the group id in the object
$group_raw = $form->get('group_Id')->getData();
// Get the explicit filled group entity throuth the posted id.
$group = $em->find('MyvendorCoreBundle:Group', $group_raw->getGroupid());
// Fill the prepaired group with the posted datas
$category->setTitle($form->get('title')->getData());
$category->setGroupId($group);
// Adding preconditions
try {
for ($i = 0; count($precondition_category_ids) > $i; $i ++) {
$precondition_category_id = $precondition_category_ids[$i];
if (0 >= $precondition_category_id) { // Retrieving id must be greater than 0
throw new \Exception('Error retrieving precondition id');
}
$precondition_category = $em->find('MyvendorCoreBundle:Category', $precondition_category_id);
if ($precondition_category instanceof Category) {
$category->addPrecondition($precondition_category);
} else {
throw new \Exception('Error retrieving precondition as Myvendor\CoreBundle\Entity\Category');
}
}
$em->persist($category); // Insert the group item with its relations
$em->flush();
echo '<h1 style="color:green">persisted</h1>';
} catch (\Exception $e) {
echo '<h1 style="color:red">' . $e->getMessage() . '</h1>';
}
}
return $this->render('MyvendorCoreBundle:fbm:new.html.twig', array(
'form' => $form->createView()
));
}
GroupType
class GroupType extends AbstractType
{
public function __construct($em){
$this->em = $em;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$groups = $this->em->createQuery("SELECT o.groupid, o.descr
FROM MyvendorCoreBundle:Group o
INDEX BY o.groupid
ORDER BY o.descr
")->getResult();
$groups_dropdown = array();
$groups_dropdown = array_map(function($value) { return $value['descr']; }, $groups);
$builder->add('groupid', 'choice', array(
'label' => false,
'choices' => $groups_dropdown,
'attr' => array('style' => 'width: 300px')
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Myvendor\CoreBundle\Entity\Group',
));
}
public function getName()
{
return 'group';
}
}
/**
* @ORM\Entity
* @ORM\Table(name="category")
*/
class Category
{
public function __construct()
{
$this->preconditions = new ArrayCollection();
}
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @var \Myvendor\CoreBundle\Entity\Group
*
* @Assert\Type(type="Myvendor\CoreBundle\Entity\Group")
* @Assert\Valid()
* @ORM\ManyToOne(targetEntity="Myvendor\CoreBundle\Entity\Group", inversedBy="Category")
* @ORM\JoinColumn(name="group_id", nullable=false, referencedColumnName="groupid")
*/
private $group_Id;
/**
* @var string
* @Assert\NotBlank()
* @ORM\Column(type="string", length=255, nullable=false)
*/
private $title;
/**
* Preconditions are Categorys referencing to an Category.
* For a single Category its empty (which have no subelements).
* A join table holds the references of a main Category to its sub-Categorys (preconditions)
*
* @ORM\ManyToMany(targetEntity="Category")
* @ORM\JoinTable(name="category_precondition",
* joinColumns={@JoinColumn(name="category_id", referencedColumnName="id")},
* inverseJoinColumns={@JoinColumn(name="category_precondition_id", referencedColumnName="id")}
* )
*/
private $preconditions;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* @param string $title
*
* @return Category
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set groupId
*
* @param \Myvendor\CoreBundle\Entity\Group $groupId
*
* @return Category
*/
public function setGroupId(\Myvendor\CoreBundle\Entity\Group $groupId)
{
$this->group_Id = $groupId;
return $this;
}
/**
* Get groupId
*
* @return \Myvendor\CoreBundle\Entity\Group
*/
public function getGroupId()
{
return $this->group_Id;
}
/**
* Add precondition
*
* @param \Myvendor\CoreBundle\Entity\Category $precondition
*
* @return $this
*/
public function addPrecondition(\Myvendor\CoreBundle\Entity\Category $precondition)
{
$this->preconditions[] = $precondition;
return $this;
}
/**
* Get preconditions
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getPreconditions()
{
return $this->preconditions;
}
/**
* Group
*
* @ORM\Table(name="group", indexes={@ORM\Index(name="homepage", columns={"homepage"}), @ORM\Index(name="theme", columns={"theme"})})
* @ORM\Entity
*/
class Group
{
/**
* @var string
*
* @ORM\Column(name="descr", type="string", length=60, nullable=true)
*/
private $descr;
/**
* @var integer
*
* @Assert\NotBlank()
* @ORM\Column(name="groupid", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
public $groupid;
/**
* Set descr
*
* @param string $descr
* @return Group
*/
public function setDescr($descr)
{
$this->descr = $descr;
return $this;
}
/**
* Get descr
*
* @return string
*/
public function getDescr()
{
return $this->descr;
}
/**
* Get groupid
*
* @return integer
*/
public function getGroupid()
{
return $this->groupid;
}
}
Решение состояло в том, что тип объектов выбора должен быть не списком выбора, а типом коллекции.
Так что используйте что-то вроде этого
->add('preconditions', 'collection', array(
'entry_type' => 'entity',
'entry_options' => array(
'class' => 'MyVendorCoreBundle:EduStructItem',
'choice_label' => 'title'
),
'allow_add' => true,
'allow_delete' => true
))
вместо
->add('preconditions', 'choice', array(
'choices' => $categories_choice,
'multiple' => true,
'mapped' => false
))
Других решений пока нет …