Search code examples
typo3slug

How to calculate slug with URL segment of parent object?


I would like to calculate the slug automatically by using the parent object. Like with the pages where the parent pages are part of the slug.

In my extension I have an object hierarchy like 'journal' > 'volume' > 'issue' > 'article' etc. I want the slug to be something like:

/volume/5/issue/7/headline-of-the-article

How can this be done?

[TYPO3 v11]


Solution

  • To calculate a slug for your custom object you need following:

    1.A path-segment field in your database. Add to your article models SQL:

    path_segment varchar(255) DEFAULT '' NOT NULL
    

    2.Add the field to the article models TCA:

    'path_segment' => [
        'exclude' => true,
        'label' => 'URL path segment',
        'l10n_mode' => 'exclude',
        'config' => [
            'type' => 'slug',
            'generatorOptions' => [
                'fields' => ['title'],  // Title of your article
                'fieldSeparator' => '/',
                'prefixParentPageSlug' => false,
                'postModifiers' => [
                    \VENDOR\ExtName\PostModifier\MyCoolPostModifier::class . '->modifySlug'
                ],
            ],
            'fallbackCharacter' => '-',
            'eval' => 'uniqueInSite',
            'default' => ''
        ],
    ],
    

    If the postmodifier does not work as described. Check out the docs. There it sais the postmodifier should be registered as a hook: https://docs.typo3.org/m/typo3/reference-tca/main/en-us/ColumnsConfig/Type/Slug/Properties/GeneratorOptions.html#confval-generatorOptions-postModifiers

    3.A postmodifier class with a modifySlug method:

    The method receives an parameter with the following values:

    [
        'slug' // ... the slug to be used
        'workspaceId' // ... the workspace ID, "0" if in live workspace
        'configuration' // ... the configuration of the TCA field
        'record' // ... the full record to be used
        'pid' // ... the resolved parent page ID
        'prefix' // ... the prefix that was added
        'tableName' // ... the table of the slug field
        'fieldName' // ... the field name of the slug field
    ];
    

    There you can load the parent objects of your article and then generate the slug. At the end return the modified slug. If you need help with that, give me a hint in the comments.

    EDIT:

    Here is an example postModifier that should fit your requirements:

    <?php
    namespace VENDOR\ExtName\PostModifier;
    
    use VENDOR\ExtName\Domain\Repository\ArticleRepository;
    use TYPO3\CMS\Core\Utility\GeneralUtility;
    use TYPO3\CMS\Extbase\Object\ObjectManager;
    use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
    
    class SlugPostModifier
    {
        public function modifySlug(array $data): string
        {
            // You will need the uid of the articles Record
            $recordUid = $data['record']['uid'];
    
            // To load the Object you will need the object manager and persistence manager
            $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
            $persistenceManager = GeneralUtility::makeInstance(PersistenceManager::class);
    
            // Instanciate your article repository and inject the persistence manager
            $articleRepository = GeneralUtility::makeInstance(ArticleRepository::class, $objectManager);
            $articleRepository->injectPersistenceManager($persistenceManager);
    
            // Fetch the article object by record uid
            $article = $articleRepository->findByUid($recordUid);
    
            // Load the articles parent objects - somehow like (Depends on your models)
            $issue = $article->getIssue();
            $volume = $issue->getVolume();
            $journal = $volume->getJournal();
    
            // Load the path segments of the articles parent objects
            $articleSlug = $data['slug'];
            $issueSlug = $issue->getPathSegment();
            $volumeSlug = $volume->getPathSegment();
            $journalSlug = $journal->getPathSegment();
    
            /**
             * If your articles parent objects do not have path segments,
             * then you could also load the title-fields.
             * But be aware that you than have to do several string operations.
             * Like strtolower(), str_replace() etc....
             * to replace capitals letters and special characters like german umlauts i.e.
             */
    
            return "$journalSlug/$volumeSlug/$issueSlug/$articleSlug";
        }
    }