Advertisement
Guest User

typo3 9 lts custom aspect

a guest
Jan 8th, 2020
161
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 9.60 KB | None | 0 0
  1. <?php
  2. declare(strict_types=1);
  3.  
  4. namespace Vendor\Extension\Routing;
  5.  
  6. use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
  7. use Doctrine\DBAL\FetchMode;
  8. use Vendor\Extension\Service\Api\Client;
  9. use Vendor\Extension\Service\Api\Entity;
  10. use TYPO3\CMS\Core\Database\ConnectionPool;
  11. use TYPO3\CMS\Core\DataHandling\SlugHelper;
  12. use TYPO3\CMS\Core\Log\Logger;
  13. use TYPO3\CMS\Core\Log\LogManager;
  14. use TYPO3\CMS\Core\Routing\Aspect\PersistedMappableAspectInterface;
  15. use TYPO3\CMS\Core\Site\SiteLanguageAwareTrait;
  16. use TYPO3\CMS\Core\Utility\GeneralUtility;
  17.  
  18. class ApiAspect implements PersistedMappableAspectInterface
  19. {
  20.     use SiteLanguageAwareTrait;
  21.  
  22.     const TABLE_SLUGS = "tx_extension_api_slugs";
  23.     const TABLE_REDIRECTS = "tx_extension_api_slugs_redirects";
  24.  
  25.     /**
  26.      * The time we wait and dont check if a slug has changed
  27.      * This will increase performance because we avoid a lot of I/O
  28.      */
  29.     const DONT_CHECK_FOR = 60 * 60 * 24 * 7;
  30.  
  31.     /**
  32.      * @var ConnectionPool
  33.      */
  34.     protected $connectionPool;
  35.  
  36.     /**
  37.      * @var Logger
  38.      */
  39.     protected $logger;
  40.  
  41.     public function __construct()
  42.     {
  43.         $this->connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
  44.         $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(get_class());
  45.     }
  46.  
  47.     /**
  48.      * @param string $value
  49.      * @return string|null
  50.      * @throws
  51.      */
  52.     public function generate(string $value): ?string
  53.     {
  54.         $language = $this->getSiteLanguage()->getTwoLetterIsoCode();
  55.         $q = $this->connectionPool->getQueryBuilderForTable(static::TABLE_SLUGS);
  56.         $statement = $q
  57.             ->select("*")
  58.             ->from(static::TABLE_SLUGS)
  59.             ->where(
  60.                 $q->expr()->andX(
  61.                     $q->expr()->eq('uuid', $q->createNamedParameter($value)),
  62.                     $q->expr()->eq('language', $q->createNamedParameter($language))
  63.                 )
  64.             )
  65.             ->setMaxResults(1)
  66.             ->execute();
  67.         $slug = null;
  68.         if ($statement->rowCount() === 1) {
  69.             $row = $statement->fetch(FetchMode::ASSOCIATIVE);
  70.             $slug = $row["slug"];
  71.             if (time() - static::DONT_CHECK_FOR > (int) $row["tstamp"]) {
  72.                 $newSlug = $this->generateSlugFromData($this->getData($value), $value);
  73.                 if ($this->needsUpdate($slug, $newSlug)) {
  74.                     // seems like we need a new slug because the name changed
  75.                     $this->updateSlug($value, $language, $slug, $newSlug);
  76.                     return $newSlug;
  77.                 } else {
  78.                     // update timestamp
  79.                     $this->touchSlug($value, $language, $slug);
  80.                 }
  81.             }
  82.             return $slug;
  83.         } else {
  84.             $slug = $this->generateSlugFromData($this->getData($value), $value);
  85.             $this->insertNewSlug($value, $language, $slug);
  86.         }
  87.         return $slug;
  88.     }
  89.  
  90.     /**
  91.      * @param string $value
  92.      * @return string|null
  93.      */
  94.     public function resolve(string $value): ?string
  95.     {
  96.         $q = $this->connectionPool->getQueryBuilderForTable(static::TABLE_SLUGS);
  97.         $language = "de";
  98.         if ($this->getSiteLanguage()) {
  99.             $language = $this->getSiteLanguage()->getTwoLetterIsoCode();
  100.         }
  101.         $statement = $q->select("uuid")->from(static::TABLE_SLUGS)->where(
  102.             $q->expr()->andX(
  103.                 $q->expr()->eq("slug", $q->createNamedParameter($value)),
  104.                 $q->expr()->eq("language", $q->createNamedParameter($language)),
  105.             )
  106.         )->execute();
  107.         if ($statement->rowCount() > 0) {
  108.             $row = $statement->fetch(FetchMode::ASSOCIATIVE);
  109.             return $row["uuid"];
  110.         } else {
  111.             $r = $this->connectionPool->getQueryBuilderForTable(static::TABLE_REDIRECTS);
  112.             $statement = $r->select("uuid")->from(static::TABLE_REDIRECTS)->where(
  113.                 $r->expr()->andX(
  114.                     $r->expr()->eq("slug", $r->createNamedParameter($value)),
  115.                     $r->expr()->eq("language", $r->createNamedParameter($language)),
  116.                 )
  117.             )->execute();
  118.             if ($statement->rowCount() > 0) {
  119.                 $row = $statement->fetch(FetchMode::ASSOCIATIVE);
  120.                 $uuid = $row["uuid"];
  121.                 $statement = $q->select("*")->from(static::TABLE_SLUGS)->where(
  122.                     $q->expr()->andX(
  123.                         $q->expr()->eq("uuid", $q->createNamedParameter($uuid)),
  124.                         $q->expr()->eq("language", $q->createNamedParameter($language)),
  125.                     )
  126.                 )->execute();
  127.                 if ($statement->rowCount() > 0) {
  128.                     $row = $statement->fetch(FetchMode::ASSOCIATIVE);
  129.                     $this->sendRedirect($value, $row["slug"]);
  130.                 } else {
  131.                     // log the error and return the uuid so the content is delivered as it should
  132.                     $this->logger->error(
  133.                         "Can not find correct url for redirected uuid",
  134.                         [
  135.                             "uuid" => $uuid,
  136.                             "slug" => $value,
  137.                             "language" => $language,
  138.                         ]
  139.                     );
  140.                     return $uuid;
  141.                 }
  142.             } else {
  143.                 return null;
  144.             }
  145.         }
  146.     }
  147.  
  148.     protected function getData(string $uuid): ?Entity
  149.     {
  150.         return Client::getInstance()->get($uuid);
  151.     }
  152.  
  153.     protected function needsUpdate(string $currentSlug, string $newSlug): bool
  154.     {
  155.         if ($newSlug === $currentSlug) {
  156.             // its the same, no update
  157.             return false;
  158.         } elseif (strlen($newSlug) > strlen($currentSlug)) {
  159.             // the new slug is longer, so it must be updated
  160.             return true;
  161.         } elseif (strlen($newSlug) < strlen($currentSlug) && strpos($currentSlug, $newSlug) === 0) {
  162.             // the new slug is shorter but at the beginning of the old slug, so it might be an appendix (-1 or -2...)
  163.             $diff = strlen($currentSlug) - strlen($newSlug);
  164.             $remaining = substr($currentSlug, $diff * -1);
  165.             if (preg_match("/^\-\d{1,3}$/", $remaining)) {
  166.                 // so it is really the case, that we deal with an appendix, so nothing to do for now
  167.                 return false;
  168.             }
  169.             // something else changed, update required
  170.             return true;
  171.         }
  172.         // dont change by default
  173.         return false;
  174.     }
  175.  
  176.     protected function sendRedirect(string $currentSlug, string $newSlug): void
  177.     {
  178.         // @todo can this be solved by some typo3 api?
  179.         $currentUri = $_SERVER['REQUEST_URI'];
  180.         $newUrl = str_replace($currentSlug, $newSlug, $currentUri);
  181.         header("Location: $newUrl", true, 301);
  182.         exit;
  183.     }
  184.  
  185.     protected function updateSlug(string $uuid, string $language, string $slug, string $newSlug): void
  186.     {
  187.         $q = $this->connectionPool->getQueryBuilderForTable(static::TABLE_REDIRECTS);
  188.         $q->insert(static::TABLE_REDIRECTS)->values([
  189.             "uuid" => $uuid,
  190.             "language" => $language,
  191.             "slug" => $slug,
  192.             "crdate" => (string) time(),
  193.         ])->execute();
  194.         $q = $this->connectionPool->getQueryBuilderForTable(static::TABLE_SLUGS);
  195.         $q->delete(static::TABLE_SLUGS)->where(
  196.             $q->expr()->andX(
  197.                 $q->expr()->eq("slug", $q->createNamedParameter($slug)),
  198.                 $q->expr()->eq("language", $q->createNamedParameter($language)),
  199.             )
  200.         )->execute();
  201.         $this->insertNewSlug($uuid, $language, $newSlug);
  202.     }
  203.  
  204.     protected function insertNewSlug(string $uuid, string $language, string $slug): void
  205.     {
  206.         $q = $this->connectionPool->getQueryBuilderForTable(static::TABLE_SLUGS);
  207.         $try = 0;
  208.         while ($try < 100) {
  209.             try {
  210.                 $insertSlug = $try === 0 ? $slug : "{$slug}-{$try}";
  211.                 $q->insert(static::TABLE_SLUGS)->values([
  212.                     "uuid" => $uuid,
  213.                     "language" => $language,
  214.                     "slug" => $insertSlug,
  215.                     "tstamp" => (string) time(),
  216.                 ])->execute();
  217.                 if ($try > 0) {
  218.                     $this->logger->info("slug collision for uuid $uuid, adding slug with appendix $try");
  219.                 }
  220.                 break;
  221.             } catch (UniqueConstraintViolationException $error) {
  222.                 $try += 1;
  223.             }
  224.         }
  225.     }
  226.  
  227.     protected function touchSlug(string $uuid, string $language, string $slug): void
  228.     {
  229.         $q = $this->connectionPool->getQueryBuilderForTable(static::TABLE_SLUGS);
  230.         $q->update(static::TABLE_SLUGS)
  231.             ->set("tstamp", (string) time())
  232.             ->where($q->expr()->andX(
  233.                 $q->expr()->eq("uuid", $q->createNamedParameter($uuid)),
  234.                 $q->expr()->eq("language", $q->createNamedParameter($language)),
  235.                 $q->expr()->eq("slug", $q->createNamedParameter($slug)),
  236.             ))->execute();
  237.     }
  238.  
  239.     protected function generateSlugFromData(Entity $data, string $uuid): string
  240.     {
  241.         $parts = [
  242.             $data['contentType'] ?? null,
  243.             $data['name'] ?? $uuid,
  244.         ];
  245.         $helper = new SlugHelper("", "", []);
  246.         $input = implode("-", array_filter($parts));
  247.         $input = str_replace("/", "-", $input);
  248.         return $helper->sanitize($input);
  249.     }
  250.  
  251. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement