-
Notifications
You must be signed in to change notification settings - Fork 47
[RFC] PhpcrProvider refactoring #257
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,11 +18,12 @@ | |
use Symfony\Component\HttpFoundation\Request; | ||
use PHPCR\PathNotFoundException; | ||
use PHPCR\Util\PathHelper; | ||
use Jackalope\Session; | ||
use Jackalope\Session as JackalopeSession; | ||
use Knp\Menu\FactoryInterface; | ||
use Knp\Menu\ItemInterface; | ||
use Knp\Menu\NodeInterface; | ||
use Knp\Menu\Provider\MenuProviderInterface; | ||
use PHPCR\SessionInterface; | ||
|
||
class PhpcrMenuProvider implements MenuProviderInterface | ||
{ | ||
|
@@ -157,116 +158,81 @@ public function setRequest(Request $request = null) | |
* menu root. You can thus pass a name or any relative path with slashes to | ||
* only load a submenu rather than a whole menu. | ||
* | ||
* @param string $name Name of the menu to load. This can be an | ||
* absolute PHPCR path or one relative to the menu root. | ||
* @param array $options | ||
* | ||
* @return ItemInterface The menu (sub)tree starting with name. | ||
* | ||
* @throws \InvalidArgumentException if the menu can not be found. | ||
* {@inheritDoc} | ||
*/ | ||
public function get($name, array $options = array()) | ||
{ | ||
$menu = $this->find($name, $options, true); | ||
$document = $this->find($name, $options, true); | ||
|
||
if (null === $document) { | ||
throw new \InvalidArgumentException(sprintf( | ||
'Menu "%s" could not be located by the PhpcrMenuProvider', | ||
$name | ||
)); | ||
} | ||
|
||
$menuItem = $this->loader->load($document); | ||
|
||
$menuItem = $this->loader->load($menu); | ||
if (empty($menuItem)) { | ||
throw new \InvalidArgumentException("Menu at '$name' is misconfigured (f.e. the route might be incorrect) and could therefore not be instanciated"); | ||
throw new \InvalidArgumentException(sprintf( | ||
'Menu "%s" is misconfigured (f.e. the route might be incorrect) and could therefore not be instansiated', | ||
$name | ||
)); | ||
} | ||
|
||
return $menuItem; | ||
} | ||
|
||
/** | ||
* Check if a menu node exists. | ||
* | ||
* If this method returns true, it means that you can call get() without | ||
* an exception. | ||
* | ||
* @param string $name Name of the menu to load. This can be an | ||
* absolute PHPCR path or one relative to the menu root. | ||
* @param array $options | ||
* | ||
* @return bool Whether a menu with this name can be loaded by this provider. | ||
* {@inheritDoc} | ||
*/ | ||
public function has($name, array $options = array()) | ||
{ | ||
return $this->find($name, $options, false) instanceof NodeInterface; | ||
$document = $this->find($name, $options); | ||
|
||
if (null === $document) { | ||
return false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now .. the only situation where |
||
} | ||
|
||
return true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this code is equivalent to i wonder if we should not do the instanceof check here and throw an exception if the interface is missing. its the earliest possible moment to detect the problem, which is usually the right place. the chain provider does not call has if a previous provider has the menu. and basically we are lying when we say we have the menu but when you fetch it we don't. |
||
} | ||
|
||
/** | ||
* @param string $name Name of the menu to load | ||
* Find the named menu or `null` if the menu cannot be located. | ||
* | ||
* @param string $name Name of the menu to load | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we could repeat the above doc that this can be a relative name to menu root or an absolute phpcr path |
||
* @param array $options | ||
* @param bool $throw Whether to throw an exception if the menu is not | ||
* found or no valid menu. Returns false if $throw is false and there | ||
* is no menu at $name. | ||
* | ||
* @return object|bool The menu root found with $name or false if $throw | ||
* is false and the menu was not found. | ||
* @return ItemInterface|null | ||
* | ||
* @throws \InvalidArgumentException Only if $throw is true throws this | ||
* exception if the name is empty or no menu found. | ||
* @throws \RuntimeException If the found node does not implement the | ||
* correct interface. | ||
*/ | ||
protected function find($name, array $options, $throw) | ||
protected function find($name, array $options) | ||
{ | ||
if (empty($name)) { | ||
if ($throw) { | ||
throw new \InvalidArgumentException('The menu name may not be empty'); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
$dm = $this->getObjectManager(); | ||
$session = $dm->getPhpcrSession(); | ||
$manager = $this->getObjectManager(); | ||
$session = $manager->getPhpcrSession(); | ||
$path = PathHelper::absolutizePath($name, $this->getMenuRoot()); | ||
|
||
try { | ||
$path = PathHelper::absolutizePath($name, $this->getMenuRoot()); | ||
PathHelper::assertValidAbsolutePath($path, false, true, $session->getNamespacePrefixes()); | ||
} catch (RepositoryException $e) { | ||
if ($throw) { | ||
throw $e; | ||
if ($this->getPrefetch() > 0) { | ||
if ($session instanceof JackalopeSession) { | ||
$this->jackalopePrefetch($session, $path); | ||
} | ||
|
||
return false; | ||
$this->genericPrefetch($session, $path); | ||
} | ||
|
||
if ($this->getPrefetch() > 0) { | ||
try { | ||
if ( | ||
$session instanceof Session | ||
&& 0 < $session->getSessionOption(Session::OPTION_FETCH_DEPTH) | ||
&& 0 === strncmp($path, $this->getMenuRoot(), strlen($this->getMenuRoot())) | ||
) { | ||
// we have jackalope with a fetch depth. prefetch all menu | ||
// nodes of all menues. | ||
$session->getNode($this->getMenuRoot(), $this->getPrefetch() + 1); | ||
} else { | ||
$session->getNode($path, $this->getPrefetch()); | ||
} | ||
} catch (PathNotFoundException $e) { | ||
if ($throw) { | ||
throw new \InvalidArgumentException(sprintf('The menu root "%s" does not exist.', $this->getMenuRoot())); | ||
} | ||
|
||
return false; | ||
} | ||
} | ||
$menu = $manager->find(null, $path); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dbu I probably need to add this back .. do you remember what the purpose of this was? (i.e. validating the absolute path and doing something with namespaces). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah i think we did this for better error messages when you use undefined namespaces or otherwise invalid paths. |
||
|
||
$menu = $dm->find(null, $path); | ||
if (null === $menu) { | ||
if ($throw) { | ||
throw new \InvalidArgumentException(sprintf('The menu "%s" is not defined.', $name)); | ||
} | ||
|
||
return false; | ||
return null; | ||
} | ||
if (!$menu instanceof NodeInterface) { | ||
if ($throw) { | ||
throw new \InvalidArgumentException("Menu at '$name' is not a valid menu node"); | ||
} | ||
|
||
return false; | ||
if (!$menu instanceof NodeInterface) { | ||
throw new \RuntimeException(sprintf( | ||
'Menu document at "%s" does not implement Knp\Menu\NodeInterface', | ||
$path | ||
)); | ||
} | ||
|
||
return $menu; | ||
|
@@ -281,4 +247,45 @@ protected function getObjectManager() | |
{ | ||
return $this->managerRegistry->getManager($this->managerName); | ||
} | ||
|
||
/** | ||
* Special case for Jackalope prefetching. | ||
*/ | ||
private function jackalopePrefetch(JackalopeSession $session, $path) | ||
{ | ||
$fetchDepth = $session->getSessionOption(JackalopeSession::OPTION_FETCH_DEPTH); | ||
|
||
// under what circumstance would the path not contain the menu root? | ||
$containsRoot = 0 === strncmp($path, $this->getMenuRoot(), strlen($this->getMenuRoot())); | ||
|
||
if (false === $containsRoot || 0 === $fetchDepth) { | ||
return $this->genericPrefetch($session, $path); | ||
} | ||
|
||
try { | ||
// we have jackalope with a fetch depth. prefetch all menu | ||
// nodes of all menues. | ||
$session->getNode($this->getMenuRoot(), $this->getPrefetch() + 1); | ||
} catch (PathNotFoundException $e) { | ||
throw new \InvalidArgumentException(sprintf( | ||
'The menu root "%s" does not exist when prefetching for Jackalope', | ||
$this->getMenuRoot() | ||
), null, $e); | ||
} | ||
} | ||
|
||
/** | ||
* Generic prefetch | ||
*/ | ||
private function genericPrefetch(SessionInterface $session, $path) | ||
{ | ||
try { | ||
$session->getNode($path, $this->getPrefetch()); | ||
} catch (PathNotFoundException $e) { | ||
throw new \InvalidArgumentException(sprintf( | ||
'The menu node "%s" does not exist.', | ||
$path | ||
), null, $e); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this information is kind of relevant and not part of the interface. agree that we can drop the rest, but we should keep this param doc.