brotkrueml / schema

TYPO3 extension providing an API and view helpers for schema.org markup
https://extensions.typo3.org/extension/schema
GNU General Public License v2.0
32 stars 9 forks source link

RFC: Add option to inject virtual breadcrumb items #57

Closed nostadt closed 4 years ago

nostadt commented 4 years ago

Currently the Breadcrumb is generated based on the simple page tree. In real projects using routeEnhancer one has virtual pages which are not represented by the page tree and those do not appear in the Breadcrumb. It would be neat to be able to add custom Breadcrumb-Items.

Looking into the code of the current implementation there's no way except to develop an own BreadcrumbSchemaManager // BreadcrumbAspect which can be enriched throughout the runtime.


POC:

1. Create a BreadcrumbListManager as Singleton.

<?php
declare(strict_types=1);

namespace Vendor\Extension\Manager;

use Brotkrueml\Schema\Model\Type\ListItem;
use TYPO3\CMS\Core\SingletonInterface;

class BreadcrumbListManager implements SingletonInterface
{
    /**
     * @var ListItem[]
     */
    protected $additionalListItems = [];

    public function addAdditionalListItem(ListItem $listItem): void
    {
        $this->additionalListItems[] = $listItem;
    }

    /**
     * @return ListItem[]
     */
    public function getAdditionalListItems(): array
    {
        return $this->additionalListItems;
    }
}

2. Create an own BreadcrumbListAspect which is basically a copy of the current implementation of ext:schema with a small addition

final class BreadcrumbListAspect implements AspectInterface
{
    // ...

    private function buildBreadCrumbList(array $rootLine): TypeInterface
    {
        // ...
        $indexCopy = 0; // <--- This is new
        foreach (\array_values($rootLine) as $index => $page) {
            // ...
        }

        /** @var BreadcrumbListManager $breadcrumbListManager */
        $breadcrumbListManager = GeneralUtility::makeInstance(BreadcrumbListManager::class);
        foreach ($breadcrumbListManager->getAdditionalListItems() as $listItem) {
            $listItem->setProperty('position', $indexCopy + 1);
            $breadcrumbList->addProperty('itemListElement', $listItem);
            $indexCopy++;
        }

        return $breadcrumbList;
    }
}

3. Replace the Aspect with your own

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/schema']['registerAspect']['breadcrumbList'] = \Vendor\Extension\Aspect\BreadcrumbListAspect::class;

4. Go to desired routeEnhancer aka Controller and register the additionalItem

if (ExtensionManagementUtility::isLoaded('schema')) {
    /** @var BreadcrumbListManager $breadcrumbListManager */
    $breadcrumbListManager = GeneralUtility::makeInstance(BreadcrumbListManager::class);
    $item = TypeFactory::createType('WebPage');
    $item->setId($this->uriBuilder->getRequest()->getRequestUri() . '#FOO');
    $listItem = TypeFactory::createType('ListItem')->setProperties([
        'name' => $download->getTitle(),
        'item' => $item,
    ]);
    $breadcrumbListManager->addAdditionalListItem($listItem);
}
brotkrueml commented 4 years ago

A breadcrumb should be shown also on the page (in my opinion) to help a user to understand, where on the site she is. Also it is a nice navigational feature to get to the pages in the upper hierarchy. So, the best way is to use an own MenuProcessor to inject, e.g., a category into the breadcrumb. You can see the principle in the news extension, where the detail record is added to the breadcrumb: https://github.com/georgringer/news/blob/master/Classes/DataProcessing/AddNewsToMenuProcessor.php

Have a look on this page for an example in frontend: https://www.jobrouter.com/en/video/jobrpa-innovative-process-automation-with-bots/ The "Modules" category is a virtual page with an Extbase plugin. An own MenuProcessor fetches for a detail page the category and the title of the detail page and injects it into the breadcrumb

You can then use the breadcrumb view helper to inject the JSON-LD markup: https://docs.typo3.org/p/brotkrueml/schema/1.8/en-us/Developer/Breadcrumb.html#view-helper-schema-breadcrumb

Does this fit your needs?

nostadt commented 4 years ago

Thanks for your response and thoughts.

Well, the approach with a MenuProcessor leads to a duplicate query. In ProductController::showAction for example I have the desired product already.

nostadt commented 4 years ago

Yet I agree that the suggestion by the issue does not consider the visible breadcrumb.

brotkrueml commented 4 years ago

I don't like the fact that the items of the visual breadcrumb and the structured markup are decoupled from each other. I consider that as bad style. The markup should reflect the content of the page. So, to add this feature to the API is no good as it encourages developers to use it.

I understand the point of the duplicate database query, you can't even cache it on your own (perhaps in a transient cache), because the MenuProcessor is called before the Extbase controller. And the Extbase magic injects the entity always into the action method with its own query.

Back to your PoC: There is the possibility to have more than one BreadcrumbList on a page. The BreadcrumbListManager is not capable of handling multiple lists.

I thought about something like SchemaManager->clearAllBreadcrumbLists(), so you can clear the lists and build and add your own under certain circumstances. But then we are at the beginning of this comment ;-)

nostadt commented 4 years ago

Yes, I indeed see your point here. I can't get rid of the thought that the routeEnhancer logic should extend the TSFE.rootline assuming TSFE is available in the future. After all the route aka rootline is IMO enhanced there. TYPO3 would fetch the object e.g. there and also pass it along to the Controller::Action.

nostadt commented 4 years ago

IMO this can be closed.