Hi Marvin,
You’d ideally model these categories as document nodes. They could be placed under one common “Categories” parent page that is a child of the site root. So your document node tree could look like this:
yoursite (Your.Site:RootPage)
- page 1 (Neos.NodeTypes:Page)
- page 2 (Neos.NodeTypes:Page)
- page n (Neos.NodeTypes:Page)
- categories (Your.Site:Categories)
- category 1 (Your.Site:Category)
- category 2 (Your.Site:Category)
- category n (Your.Site:Category)
The node type constraints for Your.Site:Categories
and Your.Site:Category
should be set accordingly. Your.Site:Categories
could be an auto-created child node of your site root.
A link from a page to a category can be modelled with a property of type reference
or references
, like this:
categories:
type: references
ui:
label: 'Categories'
inspector:
group: 'document'
editorOptions:
nodeTypes: ['Your.Site:Category']
Listing all linked pages for a category is a bit more tricky. I created my own FlowQuery operation that filters a collection of nodes and checks if they link to a certain other node (which would be your category node). This is probably not an ideal solution performance-wise, doing it with ElasticSearch might be faster. Nevertheless, here goes:
<?php
namespace Your\Site\Fusion\Eel\FlowQueryOperations;
use Neos\Eel\FlowQuery\FlowQuery;
use Neos\Eel\FlowQuery\FlowQueryException;
use Neos\Eel\FlowQuery\Operations\AbstractOperation;
use Neos\Flow\Annotations as Flow;
use Neos\ContentRepository\Domain\Model\NodeInterface;
/**
* FlowQuery operation to filter by properties of type reference or references
*/
class FilterByReferenceOperation extends AbstractOperation
{
/**
* {@inheritdoc}
*/
protected static $shortName = 'filterByReference';
/**
* {@inheritdoc}
*/
protected static $priority = 100;
/**
* {@inheritdoc}
*
* We can only handle CR Nodes.
*/
public function canEvaluate($context)
{
return (!isset($context[0]) || ($context[0] instanceof NodeInterface));
}
/**
* {@inheritdoc}
*
* @param array $arguments The arguments for this operation.
* First argument is property to filter by, must be of reference of references type.
* Second is object to filter by, must be Node.
*
* @return void
* @throws FlowQueryException
*/
public function evaluate(FlowQuery $flowQuery, array $arguments)
{
if (empty($arguments[0])) {
throw new FlowQueryException('filterByReference() needs reference property name by which nodes should be filtered', 1332492263);
}
if (empty($arguments[1])) {
throw new FlowQueryException('filterByReference() needs node reference by which nodes should be filtered', 1332493263);
}
/** @var NodeInterface $nodeReference */
list($filterByPropertyPath, $nodeReference) = $arguments;
$filteredNodes = [];
foreach ($flowQuery->getContext() as $node) {
/** @var NodeInterface $node */
$propertyValue = $node->getProperty($filterByPropertyPath);
if ($nodeReference === $propertyValue || (is_array($propertyValue) && in_array($nodeReference, $propertyValue, true))) {
$filteredNodes[] = $node;
}
}
$flowQuery->setContext($filteredNodes);
}
}
This operation can be used like this in Fusion to find all nodes linking to a category, assuming you put this in the Fusion prototype of the Your.Site:Category
document nodetype:
pagesLinkingToCurrentCategory = ${q(site).find('[instanceof Neos.Neos:Document]').filterByReference('categories', node)}
Hint: q(site).find()
searches your entire CR, so this is really not optimal performance-wise. Also, it’s probably better to put this in its own content nodetype (e.g. a Your.Site:ListOfPagesInCategory) in order to configure the cache properly.
A category menu can be created by using the default Neos.Fusion:Menu
prototype and restricting it to your category nodes only:
categoryMenu = Neos.Fusion:Menu {
filter = 'Your.Site:Categories'
}
Hope that helps.
Regards,
Bastian