Приветствую.
Коллеги, столкнулся с небольшой задачкой, решение для которой никак не найду. Поучаствуйте в мозговом штурме, может кто-то встречал похожую задачу.
Я использую Symfony2.3. Мы имеем, Entity:
Category:
// src/Acme/DemoBundle/Entity/Category.php<br><br>
namespace Acme\DemoBundle\Entity;<br><br>
use Gedmo\Mapping\Annotation as Gedmo;<br>
use Doctrine\ORM\Mapping as ORM;<br><br>
/**<br>
* @Gedmo\Tree(type="nested")<br>
* @ORM\Table(name="categories")<br>
* use repository for handy tree functions<br>
* @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")<br>
*/<br>
class Category<br>
{<br>
/**<br>
* @ORM\Column(name="id", type="integer")<br>
* @ORM\Id<br>
* @ORM\GeneratedValue<br>
*/<br>
private $id;<br><br>
/**<br>
* @Gedmo\Translatable<br>
* @ORM\Column(name="name", type="string", length=64)<br>
*/<br>
private $name;<br><br>
/**<br>
* @Gedmo\TreeLeft<br>
* @ORM\Column(name="lft", type="integer")<br>
*/<br>
private $lft;<br><br>
/**<br>
* @Gedmo\TreeLevel<br>
* @ORM\Column(name="lvl", type="integer")<br>
*/<br>
private $lvl;<br><br>
/**<br>
* @Gedmo\TreeRight<br>
* @ORM\Column(name="rgt", type="integer")<br>
*/<br>
private $rgt;<br><br>
/**<br>
* @Gedmo\TreeRoot<br>
* @ORM\Column(name="root", type="integer", nullable=true)<br>
*/<br>
private $root;<br><br>
/**<br>
* @Gedmo\TreeParent<br>
* @ORM\ManyToOne(targetEntity="Category", inversedBy="children")<br>
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")<br>
*/<br>
private $parent;<br><br>
/**<br>
* @ORM\OneToMany(targetEntity="Category", mappedBy="parent")<br>
* @ORM\OrderBy({"lft" = "ASC"})<br>
*/<br>
private $children;<br>
Item:
// src/Acme/DemoBundle/Entity/Item.php<br>
namespace Acme\DemoBundle\Entity;<br><br>
use DoctrineExtensions\Taggable\Taggable;<br>
use Doctrine\Common\Collections\ArrayCollection;<br>
use Doctrine\ORM\Mapping as ORM;<br>
use Doctrine\Common\Collections\Collection;<br><br>
/**<br>
* Item entity<br>
*<br>
* @ORM\Table(name="items")<br>
* @ORM\HasLifecycleCallbacks<br>
* @ORM\Entity<br>
*/<br>
class Item implements Taggable<br>
{<br>
/**<br>
* @ORM\Id<br>
* @ORM\Column(type="integer")<br>
* @ORM\GeneratedValue(strategy="AUTO")<br>
*/<br>
protected $id;<br><br>
/**<br>
* @ORM\Column(name="name", type="string", length=64, nullable=true)<br>
*/<br>
protected $name;<br><br>
/**<br>
* @ORM\ManyToMany(targetEntity="Category", inversedBy="items")<br>
* @ORM\JoinColumn(name="category_id", referencedColumnName="id"),<br>
* inverseJoinColumn=(name="item_id", referencedColumnName="id")<br>
*<br>
**/<br>
protected $categories;<br><br>
public function getCategories(){<br>
return $this->categories;<br>
}<br><br>
public function setCategories($categories){<br>
$this->categories = $categories;<br><br>
return $this->categories;<br>
}<br>
Кроме этого, у нас есть тип формы:
AddItemForm:
// src/Acme/DemoBundle/Form/AddItemForm.php<br>
namespace Acme\DemoBundle\Form;<br><br>
use Symfony\Component\Form\AbstractType;<br>
use Symfony\Component\Form\FormBuilderInterface;<br>
use Symfony\Component\OptionsResolver\OptionsResolverInterface;<br>
use Doctrine\ORM\EntityRepository;<br>
use Symfony\Component\Validator\Constraints\Collection;<br><br>
/**<br>
* Add item form<br>
*<br>
*/<br>
class AddModelForm extends AbstractType<br>
{ public function buildForm(FormBuilderInterface $builder, array $options) {<br>
$builder->add('categories', 'collection', array(<br>
'type' => 'entity',<br>
'allow_add' => true,<br>
'allow_delete' => true,<br>
'prototype' => true,<br>
'show_legend' => false,<br>
'data' => array(''),<br>
'widget_add_btn' => array('label' => _('Добавить категорию')),<br>
'options' => array(<br>
'widget_control_group' => false,<br>
'label_render' => false,<br>
'class' => 'AcmeDemoBundle:Category',<br>
'query_builder' => function(EntityRepository $er) {<br>
return $er->createQueryBuilder('c')<br>
->where('c.lvl = 0')<br>
->orderBy('c.id', 'ASC');<br>
},<br>
'property' => 'name',<br>
'empty_value' => _('Choose category'),<br>
),<br><br>
)<br>
);<br>
}<br>
}<br>
И контроллер ItemController:
// src/Acme/DemoBundle/Controller/ItemController.php<br>
...<br>
public function editAction($itemId) {<br>
$item= $em->getRepository('AcmeDemoBundle:Item')<br>
->findOneById($itemId);<br><br>
$form = $this->createForm(new AddItemForm(), $item);<br>
}<br><br>
public function addAction() {<br>
$item = new Item();<br><br>
$form = $this->createForm(new AddItemForm(), $item);<br>
}<br>
...<br>
Категории содержат подкатегории, которые, в свою очередь, тоже содержат подкатегории и т.д. Реализация nested tree из doctrine extensions.
Что я хочу получить?:
1) Когда я добавляю новый предмет и перехожу к выбору категорий, к которым он принадлежит, я хочу получить следующее повидение (скриншот мой, на английском):
вид выбора категорий.
Т.е. я выбираю категорию, подкатегорию и т.д. до момента, когда у категории не будет потомков (например: Оружие-Магическое-Посохи). 1-я категория, к которой принадлежит предмет — Посохи. Можно добавить до N категорий. Нажминая «добавить категорию», появляется выпадающий список нулевого уровня и динамическое появление подкатегорий происходит как было описано ранее.
2) Когда я редактирую предмет, я ожидаю увидеть уже раскрытые категории до последней, с возможностью редактировать их.
Конечно, я могу реализовать это с помощью javascript, в контроллере получить список категорий, путь до корня каждой и в twig шаблоне рендерить это дело. Но хотелось бы найти best practice решение.
Скоре всего я что-то упустил, т.к. компонент в Form в Symfony2 достаточно обширный. Может, нужное поведение в моем случае может помочь организовать какой-либо Bundle? Но опять же, пока, я ничего не нашел.