Symfony2 表格:坚持相关 objects 的自动化
Symfony2 Form: AUTOMATION on persisting with related objects
我有一个用于插入实体 类别 的表单。该实体有两个与之相关的其他实体。
一个相关实体是另一个单独的实体 Group。另一个实体本身是 self-referenced Category,它是一个表示先决条件的数组 collection。到目前为止一切顺利,我可以使用正确的 ORM 注释来保存主要实体和关系。
类别
的粗略方案
- id : 整数
- 标题:字符串
- 组:组对象
- 先决条件:[类别对象,类别对象,...]
我创建了一个类型 class 用于创建文档中描述的 best-practice 表单。
$form = $this->createForm(new CategoryType($em));
情况
在持久化实体之前,我必须对其进行初始化并将发布的数据设置给它。已发布的相关 objects 不能简单地设置为持久实体,因为它们的数据类型错误。 (例如 self-referencing collection 仅作为带有 id 的数组发布,而不是所选项目的数组 collection。)
所以我捕获了这个原始数据并从实体管理器中单独获取相关实体。
目标
插入实体应自动填充相关实体,无需通过实体管理器单独获取这些实体
问题
这就是相关objects的表单组件没有完全发布和提供的意思吗?或者我在实施中缺少什么?
有没有办法更自动化地做到这一点?
在表格 class 的“先决条件”属性 上,我必须做 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()
));
}
群组类型
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
))
我有一个用于插入实体 类别 的表单。该实体有两个与之相关的其他实体。 一个相关实体是另一个单独的实体 Group。另一个实体本身是 self-referenced Category,它是一个表示先决条件的数组 collection。到目前为止一切顺利,我可以使用正确的 ORM 注释来保存主要实体和关系。
类别
的粗略方案- id : 整数
- 标题:字符串
- 组:组对象
- 先决条件:[类别对象,类别对象,...]
我创建了一个类型 class 用于创建文档中描述的 best-practice 表单。
$form = $this->createForm(new CategoryType($em));
情况
在持久化实体之前,我必须对其进行初始化并将发布的数据设置给它。已发布的相关 objects 不能简单地设置为持久实体,因为它们的数据类型错误。 (例如 self-referencing collection 仅作为带有 id 的数组发布,而不是所选项目的数组 collection。) 所以我捕获了这个原始数据并从实体管理器中单独获取相关实体。
目标
插入实体应自动填充相关实体,无需通过实体管理器单独获取这些实体
问题
这就是相关objects的表单组件没有完全发布和提供的意思吗?或者我在实施中缺少什么? 有没有办法更自动化地做到这一点?
在表格 class 的“先决条件”属性 上,我必须做 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()
));
}
群组类型
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
))