Adding navigation container and pages dynamically

zend-mvc
zend-navigation

#1

hello everyone, just out of curiosity is it possible to have a model that creates navigation container and pages from options that are available from querying the database? I already use Zend-Navigation but it is needed to be provided configuration array in the application module.config.php.


Dynamic Menu items
#2

Sorry this is wrong, because this is only one way. There are more options to create a navigation.

Create your own factory for the navigation and combine the navigation configuration from the config and the database (repository, service etc). Register the new factory for the navigation, see at the quick start.

(Note to myself: adding an example to the documentation)


#3

What I got from your advice is as follows:
I already have a database and tables. My desired menu items are defined in menu table and I used an entity class (Menu.php):
Menu.php

namespace Application\Model;
class Menu
{
    private $id;
    private $title;
    private $is_head;
    private $link;
    private $order;
    public function __construct($id,$title,$is_head,$link,$order)
    {
        $this->id = $id;
        $this->title = $title;
        $this->is_head = $is_head;
        $this->link = $link;
        $this->order = $order;
    }
    public function getId()
    {
        return $this->id;
    }
    public function getTitle()
    {
        return $this->title;
    }
    public function getIsHead()
    {
        return $this->is_head;
    }
    public function getLink()
    {
        return $this->link;
    }
    public function getOrder()
    {
        return $this->order;
    }
}

and a repository (MenuRepository.php):
MenuRepository.php

namespace Application\Model;
use Zend\Db\Sql\Sql;
use Zend\Db\Adapter\AdapterInterface;
use Zend\Db\ResultSet\HydratingResultSet;
use Zend\Hydrator\HydratorInterface;
use RuntimeException;
use Zend\Db\Adapter\Driver\ResultInterface;

class MenuRepository implements MenuRepositoryInterface
{
    private $db;
    private $menu;
    private $hydrator;
    public function __construct(adapterInterface $db,
                                HydratorInterface $hydrator,
                                Menu $menu)
    {
        $this->db = $db;
        $this->menu = $menu;
        $this->hydrator = $hydrator;
    }
    public function getMenuItems()
    {
        $sql = new Sql($this->db);
        $select = $sql->select('menu');
        $select->order('ord');
        $statement = $sql->prepareStatementForSqlObject($select);
        $result = $statement->execute();
        if (! $result instanceof ResultInterface || ! $result->isQueryResult()) {
            throw new RuntimeException(sprintf(
                'Failed retrieving blog post with identifier "%s"; unknown database error.',
                $result
            ));
        }
        $resultSet = new HydratingResultSet($this->hydrator, $this->menu);
        $resultSet->initialize($result);
        return $resultSet;

    }
}

to store and retrieve them. So far so good. but the problem I encountered after this step was how to use them as a Navigation items.
At this point I don’t know to use my IndexController to call the methods OR use Bootstrap($e) within the Application\Module.php???
I am open to any suggestion.
thanks.


#4

You forgot the factory. The class MenuCreator is not needed. (See my comment above!)

Here some (pseudo) code:

namespace Application\Navigation;

use Application\Model\MenuRepositoryInterface;
use Interop\Container\ContainerInterface;
use Zend\Navigation\Service\AbstractNavigationFactory;

class MyNavigationFactory extends AbstractNavigationFactory
{
    protected function getName() : string
    {
        return 'my-navigation';
    }

    protected function getPages(ContainerInterface $container) : array
    {
        if (null === $this->pages) {
            /** @var MenuRepositoryInterface $menuRepository */
            $menuRepository = $container->get(MenuRepositoryInterface::class);

            $menuItems = $menuRepository->getMenuItems();
            $pages     = [];
            foreach ($menuItems as $item) {
                $pages[] = [
                    'label' => $item->getTitle(),
                    'route' => $item->getLink(),
                ];
            }

            $this->pages = $this->preparePages($container, $pages);
        }

        return $this->pages;
    }
}

Register your navigation in config:

'service_manager' => [
    'factories'  => [
        'my-navigation' => Application\Navigation\MyNavigationFactory::class,
    ]
]

Use your navigation in the layout-script:

<?= $this->navigation('my-navigation')->menu() ?>

#5

@erfan
Please delete all the code for the MenuCreator class from your comment before someone copies something. This is wrong and will not work. Thanks!


#6

thanks for your helping I am pretty sure this will work out!!! but I didn’t get what you meant from factory. there were lots of coding in the reference that made me confused and had no clue where to begin.