Я пытаюсь реализовать простой составной шаблон меню.
Это следующие классы, которые я придумал.
Пункт меню:
namespace MYNAME\MYBUNDLE\Entity;use MYNAME\MYBUNDLE\Menu\MenuComponent;
class MenuItem implements MenuComponent
{
private $id;
private $name;
private $path;
private $parent;
private $visible;
private $createdOn;
private $templating;
private $attr;
private $children;
private $website;
private $position = 1;
public function __construct($name = null, $path = null, $attr = array(), $visible = true)
{
$this->name = $name;
$this->path = $path;
$this->visible = $visible;
$this->attr = $attr;
$this->createdOn = new \DateTime;
}
public function prePersist()
{
$this->createdOn = new \DateTime;
}public function build()
{
$data['menu_item'] = $this;
$data['options'] = $this->attr;
if($this->hasChildren())
return $this->templating->render('MYBUNDLE:Menu:menu_dropdown.html.twig', $data);
if($this->isChild())
return $this->parent->getTemplating()->render('MYBUNDLE:Menu:menu_item.html.twig', $data);
return $this->templating->render('MYBUNDLE:Menu:menu_item.html.twig', $data);
}
public function __toString()
{
return $this->name;
}
public function setTemplating($templating)
{
$this->templating = $templating;
}
/**
* @return bool
*/
public function isChild()
{
return $this->hasParent();
}
/**
* @return bool
*/
public function hasParent()
{
return isset($this->parent);
}
/**
* @return bool
*/
public function hasChildren()
{
return count($this->children) > 0;
}
}
Если не использовать геттеры и сеттеры, чтобы сделать его здесь немного короче.
Как вы можете видеть, это сущность, и она содержит функцию build (), однако эта функция использует метод render, который, по моему мнению, не должен быть в сущности.
MenuController
<?php
namespace MYNAME\MYBUNDLE\Controller;use MYNAME\MYBUNDLE\Menu\Menu;
use MYNAME\MYBUNDLE\Entity\MenuItem;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class MenuController extends Controller
{
public function generateAction()
{
$menu = new Menu($this->get('templating'));
// load menu items
$items = $this->getDoctrine()->getRepository('MYBUNDLE:MenuItem')->findOrdered();
foreach($items as $item)
{
if(!$item->hasParent())
$menu->add($item);
}
return new Response($menu->build());
}
}
MenuController вызывается для отображения меню:
{{ render(controller('MYBUNDLE:Menu:generate')) }}
Я также хотел бы, чтобы это было по-другому, так как это выглядит неправильно. Возможно, лучше создать функцию ветки для отображения меню?
MenuComponent:
namespace MYNAME\MYBUNDLE\Menu;interface MenuComponent {
public function build();
}
Меню:
namespace MYNAME\MYBUNDLE\Menu;class Menu implements MenuComponent
{
private $children;
private $templating;
public function __construct($templating)
{
$this->templating = $templating;
}
public function add(MenuComponent $component)
{
$component->setTemplating($this->templating);
$this->children[] = $component;
}
public function build()
{
return $this->templating->render('MYBUNDLE:Menu:menu.html.twig', array("menu_items" => $this->children));
}
}
Menu Содержит MenuComponents и будет отображать меню первым, в каждом MenuItem вызывается его метод build ().
Я думаю, что лучше удалить логику рендеринга из моей сущности MenuItem и поместить ее где-нибудь еще, однако я не могу понять, как сделать это правильно в рамках этого шаблона проектирования.
Любая помощь или предложение приветствуется.
Задача ещё не решена.
Других решений пока нет …