For coders TYPO3 Tech Corner

Create slugs in TYPO3 via PHP

Create slugs in TYPO3 via PHP

If the generated slugs are not quite correct after a TYPO3 update and have to be recreated, this can be done easily using a command, for example. But there are also other applications for creating slugs, such as creating data records in the frontend, which then also require a valid entry in table.path_segment at the end.

CreateSlug.php as an example service class for generating a new and unique slug value. In the example, the table name is hidden behind Event::TABLE_NAME:

<?php declare(strict_types = 1); namespace UniKn\UknCalendarize\Service; use TYPO3\CMS\Core\DataHandling\Model\RecordState; use TYPO3\CMS\Core\DataHandling\Model\RecordStateFactory; use TYPO3\CMS\Core\DataHandling\SlugHelper; use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\Utility\GeneralUtility; use UniKn\UknCalendarize\Domain\Model\Event; use UniKn\UknCalendarize\Utility\DatabaseUtility; use UniKn\UknCalendarize\Utility\TcaUtility; /** * Class CreateSlug */ class CreateSlug { const SLUG_FIELDNAME = 'slug'; /** * @param array $row tx_extension_domain_model_anything.* * @param string $tableName tx_extension_domain_model_anything * @return void * @throws SiteNotFoundException */ public function recreateSlug(array $row, string $tableName): void { $slugHelper = GeneralUtility::makeInstance( SlugHelper::class, $tableName, self::SLUG_FIELDNAME, TcaUtility::getTcaOfField(self::SLUG_FIELDNAME, $tableName)['config'] ); $slug = $slugHelper->generate($row, $row['pid']); $slugValue = $slugHelper->buildSlugForUniqueInSite($slug, $this->getRecordState($row, $tableName)); $this->persistSlug($tableName, $slugValue); } /** * @param array $row tx_extension_domain_model_anything.* * @param string $tableName tx_extension_domain_model_anything * @return RecordState */ protected function getRecordState(array $row, string $tableName): RecordState { return GeneralUtility::makeInstance(RecordStateFactory::class, $tableName) ->fromArray($row); } /** * @param array $row tx_extension_domain_model_anything.* * @param string $tableName tx_extension_domain_model_anything * @param string $slugValue * @return void */ protected function persistSlug(array $row, string $tableName, string $slugValue) { $queryBuilder = DatabaseUtility::getQueryBuilderForTable($tableName); $queryBuilder ->update($tableName) ->where('uid=' . (int)$row['uid']) ->set(self::SLUG_FIELDNAME, $slugValue) ->execute(); } }

The associated DatabaseUtility.php file could look like this:

<?php declare(strict_types=1); namespace UniKn\UknCalendarize\Utility; use Doctrine\DBAL\Driver\Exception as ExceptionDbalDriver; use Doctrine\DBAL\Exception as ExceptionDbal; use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Class DatabaseUtility */ class DatabaseUtility { /** * Cache existing fields * * @var array */ protected static array $fieldsExisting = []; /** * @param string $fieldName * @param string $tableName * @return bool * @throws ExceptionDbal * @throws ExceptionDbalDriver */ public static function isFieldExistingInTable(string $fieldName, string $tableName): bool { $found = false; if (isset(self::$fieldsExisting[$tableName][$fieldName]) === false) { $connection = self::getConnectionForTable($tableName); $queryResult = $connection->executeQuery('describe ' . $tableName . ';')->fetchAllAssociative(); foreach ($queryResult as $fieldProperties) { if ($fieldProperties['Field'] === $fieldName) { $found = true; break; } } self::$fieldsExisting[$tableName][$fieldName] = $found; } else { $found = self::$fieldsExisting[$tableName][$fieldName]; } return $found; } /** * @param string $tableName * @param bool $removeRestrictions * @return QueryBuilder */ public static function getQueryBuilderForTable(string $tableName, bool $removeRestrictions = false): QueryBuilder { /** @var QueryBuilder $queryBuilder */ $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tableName); if ($removeRestrictions === true) { $queryBuilder->getRestrictions()->removeAll(); } return $queryBuilder; } /** * @param string $tableName * @return Connection */ public static function getConnectionForTable(string $tableName): Connection { return GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($tableName); } }

Last but not least a TcaUtility.php class:

<?php declare(strict_types = 1); namespace UniKn\UknCalendarize\Utility; /** * Class TcaUtility */ class TcaUtility { /** * @param string $fieldName * @param string $tableName * @return array */ public static function getTcaOfField(string $fieldName, string $tableName): array { if (empty($GLOBALS['TCA'][$tableName]['columns'][$fieldName])) { throw new \LogicException('No TCA to field ' . $fieldName . ' and table ' . $tableName, 1570026984); } return (array)$GLOBALS['TCA'][$tableName]['columns'][$fieldName]; } }

Back

"Code faster, look at the time" - does this sound familiar to you?

How about time and respect for code quality? Working in a team? Automated tests?

Join us