åé¡ã¯åºç¯ã§ãéèŠãããããããã«äœ¿ãããæ¢è£œã®ãœãªã¥ãŒã·ã§ã³ãåŸãããšãæåŸ ãããŠããŸããã ããããããã¯èµ·ãããŸããã§ããã ããã ãã§ãªããSonataã®éçºè ã¯ã誰ããèªåã®ãã³ãã«ãä»ããŠããªãŒãã管çããããšããèãã«ã€ããŠèããããšããªãã£ãããã§ãã
ããªãŒèªäœããå§ããŸãããã ãŸãã«ãããã°ã©ããŒãšããŠã®åäŸæ代ãããã決ããŠè»èŒªãåçºæããããšãæããããŸããã§ããã ãããŠãæã ã蹎ã£ãŠãã¬ãã«ãããŸããããæ°ããçºæãããèªè»¢è»ã¯æšªåãã§ã¯ãªããåã«ä¹ãããããªããŸããã ããŒãžã®ããªãŒæ§é ã«ã¯ãDoctrine Extensionsã®ãã¹ããããããªãŒã䜿çšããããšã決å®ãããŸããã
Doctrine Extensions Treeã䜿çšããããªãŒã¢ãã«ã®äœæã¯ç°¡åã§ãããã¥ã¢ã«ã§èª¬æãããŠããŸãã Symfony2å ã§Doctrineæ¡åŒµæ©èœã䟿å©ã«äœ¿çšããããã«ã¯ã StofDoctrineExtensionsBundleãæ¥ç¶ããå¿ èŠããããŸãããã®ã€ã³ã¹ããŒã«ãšèšå®ã«ã€ããŠã¯ã ããã¥ã¢ã«ã§ 詳ãã説æãããŠããŸã ã ããŠãçªç¶èª°ããããã«åé¡ãæ±ããããç§ã¯åãã§ã³ã¡ã³ããæäŒããŸãã
ããã§ãShtumiPravBundleïŒPageã¢ãã«ãå ¥æããŸããããã®ã¢ãã«ã®å®å šãªã³ãŒãã¯äžèŠãªã®ã§ããã®èšäºã§ã¯èª¬æããŸããã
ä»åºŠã¯ããã¹ããããããªãŒã®æªãæ©èœã«ã€ããŠå°ã話ãããããšæããŸãããã®ããããã¹ãŠãæ°åå€æŽããå¿ èŠããããŸããã
- ããªãŒæ§é ãä¿åããããã«ãDoctrine Extensionsã¯èŠªãã£ãŒã«ãã ãã§ãªããããŒã¿ããŒã¹ã«ä¿åãããŠããã«ãŒããlftãrgtãlvlãã£ãŒã«ãã䜿çšããŸãã ãã£ãŒã«ãã®ç®çã¯æ確ã§ãããã£ãŒã«ãã¯ããªãŒå ã®åã®é åºã決å®ããããåçŽãªSQLã¯ãšãªãäœæããŠãããªãŒèŠçŽ ããæ£ãããé åºã§ååŸã§ããããã«ããŸãã ãããã®ãã£ãŒã«ãã¯èªåçã«èšç®ãããããŒã¿ããŒã¹ã«ä¿åãããŸãã ãã ãããã£ãŒã«ãlftããã³rgtã®å€ãèšç®ããã¢ã«ãŽãªãºã ãç解ã§ããŸããã§ããïŒäžçæžåœåªåããŸããã§ãããïŒã ã ããããã«ã ããªãŒã®ããããã®èŠçŽ ã«ãããããã®ãã£ãŒã«ãã®å€ã®1ã€ãäžæ£ç¢ºã«ãªã£ãå Žåãããã¯ããªãŒå šäœã®å èš³ã«ã€ãªãããŸãã äžèšã®ãã£ãŒã«ãã®èšç®ãè€éã§ãããªãŒèŠçŽ ã®æ°ãæãããšãä¿®æ£ãã»ãŒäžå¯èœãªå èš³ã
- Doctrine Extensions Treeã§ã¯ãã«ãŒãèŠçŽ ãæšæºã¡ãœããïŒmoveUpãmoveDownïŒãšäº€æããããšã¯äžå¯èœã§ãã ãããè¡ãããšãããšã察å¿ããã¡ãã»ãŒãžãšãšãã«ãäŸå€ããã¹ããŒãããŸãã èšããŸã§ããªãããã®æ¯ãèãã¯å¥åŠã§äºæ³å€ã§ãããããã«èããªããã°ãªããŸããã
- ã¹ããã1ã§ãã«ãŒããlftãrgtãã£ãŒã«ãã«ã€ããŠèª¬æããŸããããããã®å€ã倱æãããšãããªãŒå šäœãç Žå£ãããŸãã 次ã«ãç«ã«çæãè¿œå ããŸãã ãã®ãããªç¶æ³ã¯ãå€éšããŒã®ååšãåå ã§ããªãŒèŠçŽ ãåé€ãããšãã«é害ãçºçããå Žåã«çºçããŸãã ç§ã®å Žåããããã¯åèšäºã«ããã蟌ãŸãããè¿œå èŠçŽ ã§ããã ãã®åé¡ã¯ããµã€ããã³ã³ãã³ãã§æºãããåŸããã¹ãŠã®æ å ã§æããã«ãªããããªãŒã埩å ããã«ã¯å€ãã®ç¥ââçµãšåŽåãå¿ èŠã§ããã
管çããã«ã®ããªãŒæ§é ã®çµè«
解決ããå¿ èŠãããæåã®åé¡ã®1ã€ã¯ã管çããã«ã®ããªãŒåœ¢åŒã®ããŒãžã®åºåã§ãããã€ãŸããèšäºã¿ã€ãã«ã®åã®å·ŠåŽã«ãã¹ãã¬ãã«ã«å¯Ÿå¿ããã¹ããŒã¹ã®æ°ãè¿œå ããŸããã åãåé¡ã¯ãéžæããããããŠã³ã«ããããŸããã ãœãªã¥ãŒã·ã§ã³ã¯éåžžã«ã·ã³ãã«ã§ããããšãããããŸãã-__toStringã¡ãœãããšgetLaveledTitleã¡ãœãããã¢ãã«ã«è¿œå ããŸãã
class Page { ... public function __toString() { $prefix = ""; for ($i=2; $i<= $this->lvl; $i++){ $prefix .= "& nbsp;& nbsp;& nbsp;& nbsp;"; } return $prefix . $this->title; } public function getLaveledTitle() { return (string)$this; } ... }
ããã§ããªã¹ãèšå®ã§ããã®å Žã§çæãããlaveled_titleãã£ãŒã«ãã䜿çšã§ããããã«ãªããŸããã
解決çãæåã§ã¯ãªãããšã«åæããŸãããããã§ã¯ä»ã«èª¬æã¯ãããŸããã
äžèšã§æžããåé¡ã®ãã©ã°ã©ã2ãæãåºããŠãã ããã ãã®åé¡ãåé¿ããæãç°¡åãªæ¹æ³ã¯ã1ã€ã®ã«ãŒãèŠçŽ ãäœæããããããŸã£ãã䜿çšããªãããã¡ã€ã³ããŒãžã®ããã¹ããšããŠäœ¿çšããããšã§ãã
ã== Root Element ==ããšããååãä»ããä»ã®å Žæã§ã¯äœ¿çšããªãããšã«ããŸããã ã€ãŸãã管çããã«ã§ã®ç·šé/åé€ãçŠæ¢ããŸãã ä»ã®ãã¹ãŠã®èšäºã¯ããã®ã«ãŒãèŠçŽ ã®çŽæ¥ã®åå«ããŸãã¯åå«ã®åå«ã§ãªããã°ãªããŸããã ã«ãŒãèŠçŽ ã¯æäœæ¥ã§ããŒã¿ããŒã¹ã«äœæãããç·šéã§ããªãããã«ããããã«ãcreateQueryã¡ãœãããPageAdminã¯ã©ã¹ã«è¿œå ãããŸããã
ããã§ã¯PageAdminã¯ã©ã¹ã®å®å šãªã³ãŒãã瀺ãã以äžã§ã¯ã©ã®ã¡ãœãããšäœã䜿çšããããã説æããŸãã
<? namespace Shtumi\PravBundle\Admin; use Sonata\AdminBundle\Admin\Admin; use Sonata\AdminBundle\Form\FormMapper; use Sonata\AdminBundle\Datagrid\ListMapper; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; class PageAdmin extends Admin{ protected $maxPerPage = 2500; protected $maxPageLinks = 2500; protected $datagridValues = array( '_sort_order' => 'ASC', '_sort_by' => 'p.root, p.lft' ); public function createQuery($context = 'list') { $em = $this->modelManager->getEntityManager('Shtumi\PravBundle\Entity\Page'); $queryBuilder = $em ->createQueryBuilder('p') ->select('p') ->from('ShtumiPravBundle:Page', 'p') ->where('p.parent IS NOT NULL'); $query = new ProxyQuery($queryBuilder); return $query; } protected function configureListFields(ListMapper $listMapper) { $listMapper ->add('up', 'text', array('template' => 'ShtumiPravBundle:admin:field_tree_up.html.twig', 'label'=>' ')) ->add('down', 'text', array('template' => 'ShtumiPravBundle:admin:field_tree_down.html.twig', 'label'=>' ')) ->add('id', null, array('sortable'=>false)) ->addIdentifier('laveled_title', null, array('sortable'=>false, 'label'=>' ')) ->add('_action', 'actions', array( 'actions' => array( 'edit' => array(), 'delete' => array() ), 'label'=> '' )) ; } protected function configureFormFields(FormMapper $form) { $subject = $this->getSubject(); $id = $subject->getId(); $form ->with('') ->add('parent', null, array('label' => '' , 'required'=>true , 'query_builder' => function($er) use ($id) { $qb = $er->createQueryBuilder('p'); if ($id){ $qb ->where('p.id <> :id') ->setParameter('id', $id); } $qb ->orderBy('p.root, p.lft', 'ASC'); return $qb; } )) ->add('title', null, array('label' => '')) ->add('text', null, array('label' => ' ')) ->end() ; } public function preRemove($object) { $em = $this->modelManager->getEntityManager($object); $repo = $em->getRepository("ShtumiPravBundle:Page"); $subtree = $repo->childrenHierarchy($object); foreach ($subtree AS $el){ $menus = $em->getRepository('ShtumiPravBundle:AdditionalMenu') ->findBy(array('page'=> $el['id'])); foreach ($menus AS $m){ $em->remove($m); } $services = $em->getRepository('ShtumiPravBundle:Service') ->findBy(array('page'=> $el['id'])); foreach ($services AS $s){ $em->remove($s); } $em->flush(); } $repo->verify(); $repo->recover(); $em->flush(); } public function postPersist($object) { $em = $this->modelManager->getEntityManager($object); $repo = $em->getRepository("ShtumiPravBundle:Page"); $repo->verify(); $repo->recover(); $em->flush(); } public function postUpdate($object) { $em = $this->modelManager->getEntityManager($object); $repo = $em->getRepository("ShtumiPravBundle:Page"); $repo->verify(); $repo->recover(); $em->flush(); } }
ãã¹ããããããªãŒã§ããªãŒãæ§ç¯ããããšã«ã¯ã1ã€ã®ç¹åŸŽããããŸãã æ£ããé åºã§ããªãŒå šäœãå·Šããå³ã«åãã«ã¯ããŸãèŠçŽ ãã«ãŒããã£ãŒã«ãã§ãœãŒããã次ã«lftãã£ãŒã«ãã§ãœãŒãããå¿ èŠããããŸãã ãã®ããã$ datagridValuesããããã£ãè¿œå ãããŸããã
ããªãŒãç·šéããå Žåãã»ãšãã©ã®å ŽåãããŒãžããŒã·ã§ã³ã¯å¿ èŠãããŸããã ãããã£ãŠãèŠçŽ ã®æ°ãæšæºã®30ãã2500ã«1ããŒãžå¢ãããŸããã
èŠçŽ ã®è¿œå /ç·šé
ããã§ã®äž»ãªåé¡ã¯ãèšäºãç·šéãã圢åŒã§ã®èŠªã®éå±€ããããããŠã³ãªã¹ãã®åºåã§ããã ãã®åé¡ã¯ããšã³ãã£ãã£ã®èŠªãã£ãŒã«ãã«ã¯ããŒãžã£ãæã€query_builderãè¿œå ããããšã§è§£æ±ºããŸããã ããŒã¿ããŒã¹ã«ã«ãŒãèŠçŽ ã== Root element ==ããããããã芪ãã£ãŒã«ããå¿ èŠã§ãã
postPersistããã³postUpdateã¡ãœããã«é¢ããŠã¯ããããã®ã¢ã¯ã·ã§ã³ã®åŸãããªãŒæ§é ãç Žæããªãããšã確èªããããã«ããªããžããªã®verifyããã³recoverã¡ãœãããåŒã³åºãããã«è¿œå ãããŸããã
é£äººãåºæºã«ããŠã¢ã€ãã ã䞊ã¹æ¿ãã
ãŸãããŠãŒã¶ãŒãé£äººã«å¯ŸããŠèšäºãäžäžã«ç§»åã§ãããã¿ã³ãäœæããå¿ èŠããããŸããã SonataAdminBundleã䜿çšãããšãã¬ã³ãŒãã®ãªã¹ãã®ãã£ãŒã«ãã§ãã³ãã¬ãŒãã䜿çšã§ããŸãã ãããã£ãŠã次ã®2ã€ã®ãã³ãã¬ãŒããäœæããå¿ èŠããããŸãããããããäžäžãã¿ã³çšã§ãã
ShtumiPravBundleïŒç®¡çè ïŒfield_tree_up.html.twig
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %} {% block field %} {% spaceless %} {% if object.parent.children[0].id != object.id %} <a href="{{ path('page_tree_up', {'page_id': object.id}) }}"> <img src="{{ asset('bundles/shtumiprav/images/admin/arrow_up.png') }}" alt="{% trans %}{% endtrans %}" /> </a> {% endif %} {% endspaceless %} {% endblock %}
ShtumiPravBundleïŒç®¡çè ïŒfield_tree_down.html.twig
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %} {% block field %} {% spaceless %} {% if object.parent.children[object.parent.children|length - 1].id != object.id %} <a href="{{ path('page_tree_down', {'page_id': object.id}) }}"> <img src="{{ asset('bundles/shtumiprav/images/admin/arrow_down.png') }}" alt="{% trans %}{% endtrans %}" /> </a> {% endif %} {% endspaceless %} {% endblock %}
ãããã®ãã³ãã¬ãŒãã¯ãPageAdminã¯ã©ã¹ã®configureListFieldsã¡ãœããã§æ¥ç¶ãããŸãã
routing.ymlãã¡ã€ã«ã«2ã€ã®ãã¹ãè¿œå ããå¿ èŠããããŸããããããäžãã¿ã³ãšäžãã¿ã³çšã§ãã
page_tree_up: pattern: /admin/page_tree_up/{page_id} defaults: { _controller: ShtumiPravBundle:PageTreeSort:up } page_tree_down: pattern: /admin/page_tree_down/{page_id} defaults: { _controller: ShtumiPravBundle:PageTreeSort:down }
ãã¡ãããèšäºã®ç§»åãå®è¡ããPageTreeSortControllerã³ã³ãããŒã©ãŒãäœæããå¿ èŠããããŸãã
<?php namespace Shtumi\PravBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use JMS\SecurityExtraBundle\Annotation\Secure; class PageTreeSortController extends Controller { /** * @Secure(roles="ROLE_SUPER_ADMIN") */ public function upAction($page_id) { $em = $this->getDoctrine()->getEntityManager(); $repo = $em->getRepository('ShtumiPravBundle:Page'); $page = $repo->findOneById($page_id); if ($page->getParent()){ $repo->moveUp($page); } return $this->redirect($this->getRequest()->headers->get('referer')); } /** * @Secure(roles="ROLE_SUPER_ADMIN") */ public function downAction($page_id) { $em = $this->getDoctrine()->getEntityManager(); $repo = $em->getRepository('ShtumiPravBundle:Page'); $page = $repo->findOneById($page_id); if ($page->getParent()){ $repo->moveDown($page); } return $this->redirect($this->getRequest()->headers->get('referer')); } }
ãã®ã³ã³ãããŒã©ãŒã«ã¢ã¯ã»ã¹ã§ããã®ã¯ç®¡çè ã ããªã®ã§ãããŒã«ROLE_SUPER_ADMINã®å¶éãå¿ èŠã§ãã
ã¢ã€ãã ãåé€ãã
ããªãŒèŠçŽ ãåé€ããäž»ãªåŸ®åŠãªç¹ã¯ãå€éšããŒã«ãã競åãçºçãããããªãŒã«é害ãçºçããªãããã«æ³šæããå¿ èŠãããããšã§ãã ããã«ã€ããŠã¯ããã¹ããããããªãŒã®åé¡ã®ãã©ã°ã©ã3ã§ãã§ã«èª¬æããŸããã
èšäºãåé€ããåã«ãä»ã®ã¢ãã«ããèšäºã«é¢é£ãããã¹ãŠã®ãšã³ããªãåé€ããå¿ èŠãããããšã瀺ãããã«ãPageAdminã¯ã©ã¹ããpreRemoveã¡ãœãããåé€ããŸããã§ããã ç§ã®å Žåããããã¯AdditionalMenuããã³Serviceã¢ãã«ã§ããã
ãŸãããã®å Žåãã«ã¹ã±ãŒãåé€ã¢ãã«ã§ã®ã€ã³ã¹ããŒã«ãæ©èœããªãããšã«æ³šæããŠãã ããã å®éã®ãšãããDoctrine Extensions Treeã¯ç¬èªã®ã¡ãœããã䜿çšããŠåå«ãåé€ããŸãããåå«ã¯ã«ã¹ã±ãŒãã«æ³šæãæã£ãŠããŸããã 確ãã«ãç§ã¯ãŸã ã«ã¹ã±ãŒãåé€ãã€ã³ã¹ããŒã«ããŸããïŒ
class Page { ... /** * @ORM\OneToMany(targetEntity="Service", mappedBy="page", cascade={"all"}, orphanRemoval=true) * @ORM\OrderBy({"position"="ASC"}) */ protected $services; ... }
ãã¹ããããããªãŒã¯èªåçã«åå«ãåé€ããŸãã èšå®ãããã®ã¯äœããããŸããã§ããã
ãããã«
ç§ã説æãããœãªã¥ãŒã·ã§ã³ã«ã¯è€éãªããšã¯äœããªãããã«èŠããŸãããSonataAdminBundleã§ç®¡çè ãäœæããæ©èœãè€éã§ããããããã¹ããããããªãŒã®åäœãå®å šã«ééçã§ãªãå Žåãããããããã°ããã®éãã®ãœãªã¥ãŒã·ã§ã³ãããããåãå¿ èŠããããŸããã åæ§ã®ã¿ã¹ã¯ãå®è£ ãããšãã«ããããæéã®ç¯çŽã«åœ¹ç«ã€ããšãé¡ã£ãŠããŸãã
ãã®ãœãªã¥ãŒã·ã§ã³ã«ã¯äœãæ¬ ããŠããŸãã æåã«é ã«æµ®ãã¶ã®ã¯ããµãããªãŒãé ãããšã§ãã ã€ãŸããåèŠçŽ ã®æšªã«ããããã©ã¹ãã«ãããåå«ã衚瀺ã§ããŸãã ãã®ãããªãœãªã¥ãŒã·ã§ã³ã¯ãéåžžã«å€§ããªããªãŒã«é¢é£ããŸãã æ¹åã®2çªç®ã®ã¢ã€ãã¢ã¯æåããç¶ããŸã-管çè ããã©ã¹èšå·ãã¯ãªãã¯ããŠãã®èŠªèŠçŽ ãèšæ¶ããæ°ããèšäºãäœæãããšãã«ãã芪ããã£ãŒã«ãã§èªåçã«éžæããããã«ããŸãã
äž¡æ¹ã®åé¡ã®è§£æ±ºã¯é£ãããããŸããã ããã©ã¹èšå·ãçšã®å¥ã®ãã³ãã¬ãŒããäœæããã³ã³ãããŒã©ãŒã§ã»ãã·ã§ã³ã«ä¿åããèŠçŽ ãšè¡šç€ºããèŠçŽ ãä¿åããå¿ èŠããããŸãã ããŠãcreateQueryã¡ãœããã§ããã®ã»ãã·ã§ã³ã®ããŒã¿ãåŠçããŸãã