Skip to content

Commit

Permalink
Merge pull request lochmueller#541 from okmiim/feature/slug
Browse files Browse the repository at this point in the history
Feature/slug - generation
  • Loading branch information
lochmueller authored Apr 12, 2021
2 parents c28fba2 + a74cdb1 commit 9b84d06
Show file tree
Hide file tree
Showing 8 changed files with 424 additions and 39 deletions.
90 changes: 90 additions & 0 deletions Classes/Event/BaseSlugGenerationEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

declare(strict_types=1);

namespace HDNET\Calendarize\Event;

use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;

final class BaseSlugGenerationEvent
{
/**
* @var string
*/
private $uniqueRegisterKey;

/**
* @var DomainObjectInterface|null
*/
private $model;

/**
* @var array
*/
private $record;

/**
* @var string
*/
private $baseSlug;

/**
* BaseSlugGenerationEvent constructor.
*
* @param string $uniqueRegisterKey
* @param DomainObjectInterface $model
* @param array $record
* @param string $baseSlug
*/
public function __construct(
string $uniqueRegisterKey,
DomainObjectInterface $model,
array $record,
string $baseSlug
) {
$this->uniqueRegisterKey = $uniqueRegisterKey;
$this->model = $model;
$this->record = $record;
$this->baseSlug = $baseSlug;
}

/**
* @return string
*/
public function getBaseSlug(): string
{
return $this->baseSlug;
}

/**
* @param string $baseSlug
*/
public function setBaseSlug(string $baseSlug): void
{
$this->baseSlug = $baseSlug;
}

/**
* @return DomainObjectInterface|null
*/
public function getModel(): ?DomainObjectInterface
{
return $this->model;
}

/**
* @return string
*/
public function getUniqueRegisterKey(): string
{
return $this->uniqueRegisterKey;
}

/**
* @return array
*/
public function getRecord(): array
{
return $this->record;
}
}
69 changes: 69 additions & 0 deletions Classes/Event/SlugSuffixGenerationEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

namespace HDNET\Calendarize\Event;

final class SlugSuffixGenerationEvent
{
/**
* @var string
*/
private $uniqueRegisterKey;

/**
* @var array
*/
private $record;

/**
* @var string
*/
private $slug;

/**
* SlugSuffixGenerationEvent constructor.
*
* @param string $uniqueRegisterKey
* @param array $record
* @param string $slug
*/
public function __construct(string $uniqueRegisterKey, array $record, string $slug)
{
$this->uniqueRegisterKey = $uniqueRegisterKey;
$this->record = $record;
$this->slug = $slug;
}

/**
* @return string
*/
public function getUniqueRegisterKey(): string
{
return $this->uniqueRegisterKey;
}

/**
* @return array
*/
public function getRecord(): array
{
return $this->record;
}

/**
* @return string
*/
public function getSlug(): string
{
return $this->slug;
}

/**
* @param string $slug
*/
public function setSlug(string $slug): void
{
$this->slug = $slug;
}
}
29 changes: 28 additions & 1 deletion Classes/Service/IndexPreparationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
namespace HDNET\Calendarize\Service;

use HDNET\Calendarize\Register;
use HDNET\Calendarize\Service\Url\SlugService;
use HDNET\Calendarize\Utility\HelperUtility;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
Expand All @@ -17,8 +18,18 @@
* Helper class for the IndexService
* Prepare the index.
*/
class IndexPreparationService
class IndexPreparationService extends AbstractService
{
/**
* @var SlugService
*/
protected $slugService;

public function __construct(SlugService $slugService)
{
$this->slugService = $slugService;
}

/**
* Build the index for one element.
*
Expand Down Expand Up @@ -63,6 +74,7 @@ public function prepareIndex($configurationKey, $tableName, $uid)
$this->addLanguageInformation($neededItems, $tableName, $rawRecord);
$this->addEnableFieldInformation($neededItems, $tableName, $rawRecord);
$this->addCtrlFieldInformation($neededItems, $tableName, $rawRecord);
$this->addSlugInformation($neededItems, $configurationKey, $rawRecord);

return $neededItems;
}
Expand Down Expand Up @@ -172,6 +184,21 @@ protected function addCtrlFieldInformation(array &$neededItems, $tableName, arra
}
}

/**
* Add slug to each index.
*
* @param array $neededItems
* @param string $uniqueRegisterKey
* @param array $record
*/
protected function addSlugInformation(array &$neededItems, string $uniqueRegisterKey, array $record): void
{
$slugs = $this->slugService->generateSlugForItems($uniqueRegisterKey, $record, $neededItems);
foreach ($neededItems as $key => $value) {
$neededItems[$key] = array_merge($value, $slugs[$key] ?? []);
}
}

/**
* Prepare the record for the database insert.
*
Expand Down
91 changes: 57 additions & 34 deletions Classes/Service/IndexerService.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace HDNET\Calendarize\Service;

use HDNET\Calendarize\Register;
use HDNET\Calendarize\Service\Url\SlugService;
use HDNET\Calendarize\Utility\ArrayUtility;
use HDNET\Calendarize\Utility\DateTimeUtility;
use HDNET\Calendarize\Utility\HelperUtility;
Expand All @@ -31,9 +32,24 @@ class IndexerService extends AbstractService
*/
protected $signalSlot;

public function __construct()
{
$this->signalSlot = GeneralUtility::makeInstance(Dispatcher::class);
/**
* @var IndexPreparationService
*/
protected $preparationService;

/**
* @var SlugService
*/
protected $slugService;

public function __construct(
Dispatcher $dispatcher,
IndexPreparationService $preparationService,
SlugService $slugService
) {
$this->signalSlot = $dispatcher;
$this->preparationService = $preparationService;
$this->slugService = $slugService;
}

/**
Expand All @@ -54,22 +70,18 @@ public function reindexAll()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));

$transPointer = $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] ?? false; // e.g. l10n_parent
$q->select('uid')
->from($tableName);

$transPointer = $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] ?? false; // e.g. l10n_parent
if ($transPointer) {
// Note: In localized tables, it is important, that the "default language records" are indexed first, so the
// overlays can connect with l10n_parent to the right default record.
$q->select('uid')
->from($tableName)
->orderBy((string)$transPointer);
} else {
$q->select('uid')
->from($tableName);
$q->orderBy((string)$transPointer);
}

$rows = $q->execute()->fetchAll();
foreach ($rows as $row) {
$this->updateIndex($key, $configuration['tableName'], (int)$row['uid']);
$this->updateIndex($key, $tableName, (int)$row['uid']);
}
}

Expand All @@ -85,14 +97,13 @@ public function reindexAll()
*/
public function reindex(string $configurationKey, string $tableName, int $uid)
{
$dispatcher = GeneralUtility::makeInstance(Dispatcher::class);
$dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'Pre', [$configurationKey, $tableName, $uid, $this]);
$this->signalSlot->dispatch(__CLASS__, __FUNCTION__ . 'Pre', [$configurationKey, $tableName, $uid, $this]);

$this->removeInvalidConfigurationIndex();
$this->removeInvalidRecordIndex($tableName);
$this->updateIndex($configurationKey, $tableName, $uid);

$dispatcher->dispatch(__CLASS__, __FUNCTION__ . 'Post', [$configurationKey, $tableName, $uid, $this]);
$this->signalSlot->dispatch(__CLASS__, __FUNCTION__ . 'Post', [$configurationKey, $tableName, $uid, $this]);
}

/**
Expand Down Expand Up @@ -155,12 +166,7 @@ public function getNextEvents($table, $uid, $limit = 5)
*/
protected function updateIndex(string $configurationKey, string $tableName, int $uid)
{
/** @var $preparationService IndexPreparationService */
static $preparationService = null;
if (null === $preparationService) {
$preparationService = GeneralUtility::makeInstance(IndexPreparationService::class);
}
$neededItems = $preparationService->prepareIndex($configurationKey, $tableName, $uid);
$neededItems = $this->preparationService->prepareIndex($configurationKey, $tableName, $uid);
$this->insertAndUpdateNeededItems($neededItems, $tableName, $uid);
}

Expand All @@ -178,10 +184,12 @@ protected function getCurrentItems(string $tableName, int $uid)
$q->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$q->resetQueryParts();
$q->select('*')
->from(self::TABLE_NAME)
->where($q->expr()->eq('foreign_table', $q->quote($tableName)), $q->expr()->eq('foreign_uid', $uid));
->where(
$q->expr()->eq('foreign_table', $q->createNamedParameter($tableName)),
$q->expr()->eq('foreign_uid', $q->createNamedParameter($uid, \PDO::PARAM_INT))
);

return $q->execute();
}
Expand All @@ -195,32 +203,47 @@ protected function getCurrentItems(string $tableName, int $uid)
*/
protected function insertAndUpdateNeededItems(array $neededItems, string $tableName, int $uid)
{
$databaseConnection = HelperUtility::getDatabaseConnection($tableName);
$databaseConnection = HelperUtility::getDatabaseConnection(self::TABLE_NAME);
$currentItems = $this->getCurrentItems($tableName, $uid)->fetchAll();

$this->signalSlot->dispatch(__CLASS__, __FUNCTION__ . 'Pre', [$neededItems, $tableName, $uid]);

foreach ($neededItems as $neededKey => $neededItem) {
$remove = false;
foreach ($currentItems as $currentKey => $currentItem) {
if (ArrayUtility::isEqualArray($neededItem, $currentItem)) {
$remove = true;
if (ArrayUtility::isEqualArray($neededItem, $currentItem, ['tstamp', 'crdate', 'slug'])) {
// Check if the current slug starts with the new slug
// Prevents regeneration for slugs with counting suffixes (added before insertion)
// False positives are possible (e.g. single event where a part gets removed)
if (0 !== mb_stripos($currentItem['slug'] ?? '', $neededItem['slug'], 0, 'utf-8')) {
// Slug changed
continue;
}

unset($neededItems[$neededKey], $currentItems[$currentKey]);

break;
}
}
if ($remove) {
continue;
}
}
foreach ($currentItems as $item) {
$databaseConnection->delete(self::TABLE_NAME, ['uid' => $item['uid']]);
}

$neededItems = array_values($neededItems);
if ($neededItems) {
$databaseConnection->bulkInsert(self::TABLE_NAME, $neededItems, array_keys($neededItems[0]));
$this->generateSlugAndInsert($neededItems);
}

/**
* Generates a slug and inserts the records in the db.
*
* @param array $neededItems
*/
protected function generateSlugAndInsert(array $neededItems): void
{
$db = HelperUtility::getDatabaseConnection(self::TABLE_NAME);
foreach ($neededItems as $key => $item) {
$item['slug'] = $this->slugService->makeSlugUnique($item);
// We need to insert after each index, so subsequent indices do not get the same slug
$db->insert(self::TABLE_NAME, $item);
}
}

Expand All @@ -242,7 +265,7 @@ protected function removeInvalidRecordIndex($tableName)

$rows = $q->execute()->fetchAll();

$q->resetQueryParts()->resetRestrictions();
$q = HelperUtility::getDatabaseConnection(self::TABLE_NAME)->createQueryBuilder();
$q->delete(self::TABLE_NAME)
->where(
$q->expr()->eq('foreign_table', $q->createNamedParameter($tableName))
Expand Down
Loading

0 comments on commit 9b84d06

Please sign in to comment.