-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement nested navigation - container for nested navigation instances - builder for navigation tree - registry for navigation containers (so you can register multiple nested navigation instances)
- Loading branch information
1 parent
1fd91b1
commit 9091774
Showing
8 changed files
with
398 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,14 +4,12 @@ | |
|
||
namespace Everlution\Navigation\Builder; | ||
|
||
use Everlution\Navigation\Item\ItemInterface; | ||
|
||
/** | ||
* Interface MatcherInterface. | ||
* | ||
* @author Ivan Barlog <[email protected]> | ||
*/ | ||
interface MatcherInterface | ||
{ | ||
public function isCurrent(ItemInterface $item): bool; | ||
public function isCurrent($item): bool; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Everlution\Navigation\Nested; | ||
|
||
use Everlution\Navigation\ContainerInterface; | ||
use Everlution\Navigation\Nested\Container\ContainerNotFoundException; | ||
use Everlution\Navigation\Nested\Container\NestableContainerInterface; | ||
|
||
/** | ||
* Class AdvancedNavigationInterface. | ||
* | ||
* @author Martin Lutter <[email protected]> | ||
*/ | ||
interface AdvancedNavigationInterface | ||
{ | ||
/** | ||
* @return NestableContainerInterface[] | ||
*/ | ||
public function getNavigationContainers(): array; | ||
|
||
/** | ||
* As argument provide FQCN of item class eg. MainNavigation::class. | ||
* | ||
* @param string $name | ||
* | ||
* @return NestableContainerInterface | ||
* | ||
* @throws ContainerNotFoundException | ||
*/ | ||
public function get(string $name): NestableContainerInterface; | ||
|
||
public function getRoot(): ContainerInterface; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Everlution\Navigation\Nested\Builder; | ||
|
||
use Everlution\Navigation\Nested\AdvancedNavigationInterface; | ||
use Everlution\Navigation\Nested\Container\ContainerNotFoundException; | ||
use Everlution\Navigation\Nested\Container\NestableContainerInterface; | ||
use Everlution\Navigation\Builder\MatcherInterface; | ||
use Everlution\Navigation\Builder\NoCurrentItemFoundException; | ||
use Everlution\Navigation\ContainerInterface; | ||
|
||
/** | ||
* Class NavigationBuilder. | ||
* | ||
* @author Martin Lutter <[email protected]> | ||
*/ | ||
class NavigationBuilder | ||
{ | ||
/** @var AdvancedNavigationInterface */ | ||
private $navigation; | ||
/** @var ParentNode */ | ||
private $root; | ||
/** @var ContainerInterface[] */ | ||
private $stack = []; | ||
/** @var ContainerInterface[] */ | ||
private $used = []; | ||
/** @var ParentNode */ | ||
private $current; | ||
/** @var MatcherInterface */ | ||
private $matcher; | ||
|
||
public function __construct(AdvancedNavigationInterface $container, MatcherInterface $matcher) | ||
{ | ||
$this->navigation = $container; | ||
$this->matcher = $matcher; | ||
$this->build($matcher); | ||
} | ||
|
||
/** | ||
* @return ParentNode | ||
* | ||
* @throws NoCurrentItemFoundException | ||
*/ | ||
public function getCurrent(): ParentNode | ||
{ | ||
if (!$this->current) { | ||
throw new NoCurrentItemFoundException(); | ||
} | ||
|
||
return $this->current; | ||
} | ||
|
||
/** | ||
* @param MatcherInterface $matcher | ||
*/ | ||
private function build(MatcherInterface $matcher) | ||
{ | ||
$this->root = new ParentNode($this->navigation->getRoot()); | ||
$this->setCurrentNode($matcher, $this->root); | ||
$this->addToUsed($this->root); | ||
|
||
foreach ($this->navigation->getNavigationContainers() as $container) { | ||
$this->walkToRootContainer($container); | ||
$this->addContainersFromStack(); | ||
} | ||
} | ||
|
||
/** | ||
* @param NestableContainerInterface $item | ||
* | ||
* @return NestableContainerInterface | ||
* | ||
* @throws ContainerNotFoundException | ||
*/ | ||
private function getParent(NestableContainerInterface $item): NestableContainerInterface | ||
{ | ||
return $this->navigation->get($item->getParentContainer()); | ||
} | ||
|
||
/** | ||
* @param NestableContainerInterface $container | ||
* | ||
* @return NestableContainerInterface | ||
*/ | ||
private function walkToRootContainer(NestableContainerInterface $container): NestableContainerInterface | ||
{ | ||
$this->stack[] = $container; | ||
|
||
try { | ||
return $this->walkToRootContainer($this->getParent($container)); | ||
} catch (ContainerNotFoundException $e) { | ||
return $container; | ||
} | ||
} | ||
|
||
private function addContainersFromStack(): void | ||
{ | ||
$root = $this->root; | ||
while ($container = array_pop($this->stack)) { | ||
if (false === $this->isUsed($container)) { | ||
$this->addContainer($root, $container); | ||
} | ||
|
||
$root = $root->get(get_class($container)); | ||
} | ||
} | ||
|
||
/** | ||
* @param ParentNode $root | ||
* @param NestableContainerInterface $container | ||
*/ | ||
private function addContainer(ParentNode $root, NestableContainerInterface $container): void | ||
{ | ||
$parentNode = new ParentNode($container); | ||
|
||
$root->addChild($parentNode); | ||
$this->addToUsed($parentNode); | ||
$this->setCurrentNode($this->matcher, $parentNode); | ||
} | ||
|
||
/** | ||
* @param MatcherInterface $matcher | ||
* @param ParentNode $node | ||
*/ | ||
private function setCurrentNode(MatcherInterface $matcher, ParentNode $node): void | ||
{ | ||
if (!$this->current && $matcher->isCurrent($node->getContainer())) { | ||
$this->current = $node; | ||
} | ||
} | ||
|
||
/** | ||
* @param ContainerInterface $container | ||
* | ||
* @return bool | ||
*/ | ||
private function isUsed(ContainerInterface $container): bool | ||
{ | ||
$name = get_class($container); | ||
|
||
return isset($this->used[$name]); | ||
} | ||
|
||
/** | ||
* @param ParentNode $node | ||
*/ | ||
private function addToUsed(ParentNode $node): void | ||
{ | ||
$name = get_class($node->getContainer()); | ||
$this->used[$name] = $node; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Everlution\Navigation\Nested\Builder; | ||
|
||
use Everlution\Navigation\ContainerInterface; | ||
|
||
/** | ||
* Class RootNode. | ||
* | ||
* @author Martin Lutter <[email protected]> | ||
*/ | ||
class ParentNode | ||
{ | ||
/** @var ContainerInterface */ | ||
protected $container; | ||
/** @var ParentNode[] */ | ||
protected $children = []; | ||
|
||
public function __construct(ContainerInterface $navigation) | ||
{ | ||
$this->container = $navigation; | ||
} | ||
|
||
/** | ||
* @return ContainerInterface | ||
*/ | ||
public function getContainer(): ContainerInterface | ||
{ | ||
return $this->container; | ||
} | ||
|
||
/** | ||
* @param ParentNode $item | ||
*/ | ||
public function addChild(ParentNode $item): void | ||
{ | ||
$this->children[get_class($item->getContainer())] = $item; | ||
} | ||
|
||
/** | ||
* @return bool | ||
*/ | ||
public function hasChildren(): bool | ||
{ | ||
return false === empty($this->children); | ||
} | ||
|
||
/** | ||
* @return array | ||
*/ | ||
public function getChildren(): array | ||
{ | ||
return $this->children; | ||
} | ||
|
||
/** | ||
* @param string $name | ||
* | ||
* @return bool | ||
*/ | ||
public function has(string $name): bool | ||
{ | ||
return array_key_exists($name, $this->children); | ||
} | ||
|
||
/** | ||
* @param string $name | ||
* | ||
* @return ParentNode | ||
*/ | ||
public function get(string $name): ParentNode | ||
{ | ||
return $this->children[$name]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Everlution\Navigation\Nested\Container; | ||
|
||
/** | ||
* Class ContainerNotFoundException. | ||
* | ||
* @author Martin Lutter <[email protected]> | ||
*/ | ||
class ContainerNotFoundException extends \Exception | ||
{ | ||
public function __construct(string $name) | ||
{ | ||
parent::__construct("$name container was not found"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Everlution\Navigation\Nested\Container; | ||
|
||
use Everlution\Navigation\ContainerInterface; | ||
use Everlution\Navigation\Item\ItemInterface; | ||
|
||
/** | ||
* Class NestableContainerInterface. | ||
* | ||
* @author Martin Lutter <[email protected]> | ||
*/ | ||
interface NestableContainerInterface extends ContainerInterface | ||
{ | ||
/** | ||
* @return string | ||
*/ | ||
public function getParentContainer(): string; | ||
|
||
/** | ||
* @return ItemInterface | ||
*/ | ||
public function getParentItem(): ItemInterface; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Everlution\Navigation\Nested; | ||
|
||
use Everlution\Navigation\ContainerInterface; | ||
use Everlution\Navigation\Nested\Container\ContainerNotFoundException; | ||
use Everlution\Navigation\Nested\Container\NestableContainerInterface; | ||
|
||
/** | ||
* Class NavigationContainer. | ||
* | ||
* @author Martin Lutter <[email protected]> | ||
*/ | ||
abstract class NavigationContainer implements AdvancedNavigationInterface | ||
{ | ||
/** @var NestableContainerInterface[] */ | ||
private $containers = []; | ||
|
||
public function add(NestableContainerInterface $container): void | ||
{ | ||
$this->containers[get_class($container)] = $container; | ||
} | ||
|
||
/** | ||
* @return ContainerInterface[] | ||
*/ | ||
public function getNavigationContainers(): array | ||
{ | ||
return $this->containers; | ||
} | ||
|
||
/** | ||
* @param string $name | ||
* | ||
* @return NestableContainerInterface | ||
* | ||
* @throws ContainerNotFoundException | ||
*/ | ||
public function get(string $name): NestableContainerInterface | ||
{ | ||
if (false === isset($this->containers[$name])) { | ||
throw new ContainerNotFoundException($name); | ||
} | ||
|
||
return $this->containers[$name]; | ||
} | ||
} |
Oops, something went wrong.