vendor/pimcore/pimcore/models/DataObject/Data/UrlSlug.php line 32

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Model\DataObject\Data;
  15. use Pimcore\Cache\RuntimeCache;
  16. use Pimcore\Db;
  17. use Pimcore\Logger;
  18. use Pimcore\Model\DataObject\ClassDefinition;
  19. use Pimcore\Model\DataObject\ClassDefinition\Data\Localizedfields;
  20. use Pimcore\Model\DataObject\ClassDefinition\Data\Objectbricks;
  21. use Pimcore\Model\DataObject\Concrete;
  22. use Pimcore\Model\DataObject\Fieldcollection;
  23. use Pimcore\Model\DataObject\Fieldcollection\Data\AbstractData;
  24. use Pimcore\Model\DataObject\Objectbrick\Definition;
  25. use Pimcore\Model\DataObject\OwnerAwareFieldInterface;
  26. use Pimcore\Model\DataObject\Traits\ObjectVarTrait;
  27. use Pimcore\Model\DataObject\Traits\OwnerAwareFieldTrait;
  28. class UrlSlug implements OwnerAwareFieldInterface
  29. {
  30.     use ObjectVarTrait;
  31.     use OwnerAwareFieldTrait;
  32.     public const TABLE_NAME 'object_url_slugs';
  33.     /**
  34.      * @var int
  35.      */
  36.     protected $objectId;
  37.     /**
  38.      * @var string
  39.      */
  40.     protected $classId;
  41.     /**
  42.      * @var string|null
  43.      */
  44.     protected $slug;
  45.     /**
  46.      * @var int|null
  47.      */
  48.     protected $siteId;
  49.     /**
  50.      * @var string
  51.      */
  52.     protected $fieldname;
  53.     /**
  54.      * @var int
  55.      */
  56.     protected $index;
  57.     /**
  58.      * @var string
  59.      */
  60.     protected $ownertype;
  61.     /**
  62.      * @var string
  63.      */
  64.     protected $ownername;
  65.     /**
  66.      * @var string
  67.      */
  68.     protected $position;
  69.     /**
  70.      * @var null|string
  71.      */
  72.     protected $previousSlug;
  73.     /**
  74.      * UrlSlug constructor.
  75.      *
  76.      * @param string|null $slug
  77.      * @param int|null $siteId
  78.      */
  79.     public function __construct(?string $slug, ?int $siteId 0)
  80.     {
  81.         $this->slug $slug;
  82.         $this->siteId $siteId ?? 0;
  83.     }
  84.     /**
  85.      * @return int
  86.      */
  87.     public function getObjectId(): int
  88.     {
  89.         return $this->objectId;
  90.     }
  91.     /**
  92.      * @param int $objectId
  93.      *
  94.      * @return $this
  95.      */
  96.     public function setObjectId(int $objectId)
  97.     {
  98.         $this->objectId $objectId;
  99.         return $this;
  100.     }
  101.     /**
  102.      * @return string|null
  103.      */
  104.     public function getSlug(): ?string
  105.     {
  106.         return $this->slug;
  107.     }
  108.     /**
  109.      * @param string|null $slug
  110.      *
  111.      * @return $this
  112.      */
  113.     public function setSlug(?string $slug)
  114.     {
  115.         $this->slug $slug;
  116.         return $this;
  117.     }
  118.     /**
  119.      * @internal
  120.      *
  121.      * @return string|null
  122.      */
  123.     public function getPreviousSlug(): ?string
  124.     {
  125.         return $this->previousSlug;
  126.     }
  127.     /**
  128.      * @internal
  129.      *
  130.      * @param string|null $previousSlug
  131.      */
  132.     public function setPreviousSlug(?string $previousSlug): void
  133.     {
  134.         $this->previousSlug $previousSlug;
  135.     }
  136.     /**
  137.      * @return int|null
  138.      */
  139.     public function getSiteId(): ?int
  140.     {
  141.         return $this->siteId;
  142.     }
  143.     /**
  144.      * @param int|null $siteId
  145.      *
  146.      * @return $this
  147.      */
  148.     public function setSiteId(?int $siteId)
  149.     {
  150.         $this->siteId $siteId ?? 0;
  151.         return $this;
  152.     }
  153.     /**
  154.      * @return string|null
  155.      */
  156.     public function getFieldname(): ?string
  157.     {
  158.         return $this->fieldname;
  159.     }
  160.     /**
  161.      * @param string|null $fieldname
  162.      *
  163.      * @return $this
  164.      */
  165.     public function setFieldname(?string $fieldname)
  166.     {
  167.         $this->fieldname $fieldname;
  168.         return $this;
  169.     }
  170.     /**
  171.      * @return int|null
  172.      */
  173.     public function getIndex(): ?int
  174.     {
  175.         return $this->index;
  176.     }
  177.     /**
  178.      * @param int|null $index
  179.      *
  180.      * @return $this
  181.      */
  182.     public function setIndex(?int $index)
  183.     {
  184.         $this->index $index;
  185.         return $this;
  186.     }
  187.     /**
  188.      * @return string|null
  189.      */
  190.     public function getOwnertype(): ?string
  191.     {
  192.         return $this->ownertype;
  193.     }
  194.     /**
  195.      * @param string|null $ownertype
  196.      *
  197.      * @return $this
  198.      */
  199.     public function setOwnertype(?string $ownertype)
  200.     {
  201.         $this->ownertype $ownertype;
  202.         return $this;
  203.     }
  204.     /**
  205.      * @return string|null
  206.      */
  207.     public function getOwnername(): ?string
  208.     {
  209.         return $this->ownername;
  210.     }
  211.     /**
  212.      * @param string|null $ownername
  213.      *
  214.      * @return $this
  215.      */
  216.     public function setOwnername(?string $ownername)
  217.     {
  218.         $this->ownername $ownername;
  219.         return $this;
  220.     }
  221.     /**
  222.      * @return string|null
  223.      */
  224.     public function getPosition(): ?string
  225.     {
  226.         return $this->position;
  227.     }
  228.     /**
  229.      * @param string|null $position
  230.      *
  231.      * @return $this
  232.      */
  233.     public function setPosition(?string $position)
  234.     {
  235.         $this->position $position;
  236.         return $this;
  237.     }
  238.     /**
  239.      * @return string
  240.      */
  241.     public function getClassId()
  242.     {
  243.         return $this->classId;
  244.     }
  245.     /**
  246.      * @param string $classId
  247.      *
  248.      * @return $this
  249.      */
  250.     public function setClassId($classId)
  251.     {
  252.         $this->classId $classId;
  253.         return $this;
  254.     }
  255.     /**
  256.      * @param array $rawItem
  257.      *
  258.      * @return UrlSlug
  259.      */
  260.     public static function createFromDataRow($rawItem): UrlSlug
  261.     {
  262.         $slug = new self($rawItem['slug'], $rawItem['siteId']);
  263.         $slug->setObjectId($rawItem['objectId']);
  264.         $slug->setClassId($rawItem['classId']);
  265.         $slug->setFieldname($rawItem['fieldname']);
  266.         $slug->setIndex($rawItem['index']);
  267.         $slug->setOwnertype($rawItem['ownertype']);
  268.         $slug->setOwnername($rawItem['ownername']);
  269.         $slug->setPosition($rawItem['position']);
  270.         $slug->setPreviousSlug($rawItem['slug']);
  271.         return $slug;
  272.     }
  273.     /**
  274.      * @internal
  275.      *
  276.      * @param string $path
  277.      * @param int $siteId
  278.      *
  279.      * @return UrlSlug|null
  280.      */
  281.     public static function resolveSlug($path$siteId 0)
  282.     {
  283.         $cacheKey self::getCacheKey($path$siteId);
  284.         if (RuntimeCache::isRegistered($cacheKey)) {
  285.             $slug RuntimeCache::get($cacheKey);
  286.             if ($slug instanceof UrlSlug) {
  287.                 return $slug;
  288.             }
  289.         }
  290.         $slug null;
  291.         $db Db::get();
  292.         try {
  293.             $filterSiteId 'siteId = 0';
  294.             if ($siteId) {
  295.                 $filterSiteId sprintf('(siteId = %d OR siteId = 0)'$siteId);
  296.             }
  297.             $query sprintf(
  298.                 'SELECT * FROM %s WHERE slug = %s AND %s ORDER BY siteId DESC LIMIT 1',
  299.                 self::TABLE_NAME,
  300.                 $db->quote($path),
  301.                 $filterSiteId
  302.             );
  303.             $rawItem $db->fetchAssociative($query);
  304.             if ($rawItem) {
  305.                 $slug self::createFromDataRow($rawItem);
  306.             }
  307.         } catch (\Exception $e) {
  308.             Logger::error((string) $e);
  309.         }
  310.         RuntimeCache::set($cacheKey$slug);
  311.         return $slug;
  312.     }
  313.     /**
  314.      * @internal
  315.      *
  316.      * @return string
  317.      *
  318.      * @throws \Exception
  319.      */
  320.     public function getAction()
  321.     {
  322.         /** @var ClassDefinition\Data\UrlSlug $fd */
  323.         $fd null;
  324.         $classDefinition ClassDefinition::getById($this->getClassId());
  325.         if ($classDefinition) {
  326.             // reverse look up the field definition ...
  327.             if ($this->getOwnertype() === 'object') {
  328.                 $fd $classDefinition->getFieldDefinition($this->getFieldname());
  329.             } elseif ($this->getOwnertype() === 'localizedfield') {
  330.                 $ownerName $this->getOwnername();
  331.                 if (strpos($ownerName'~') !== false) {
  332.                     // this is a localized field inside a field collection or objectbrick
  333.                     $parts explode('~'$this->getOwnername());
  334.                     $type trim($parts[0], '/');
  335.                     $objectFieldnameParts $this->getOwnername();
  336.                     $objectFieldnameParts explode('~'$objectFieldnameParts);
  337.                     $objectFieldnameParts $objectFieldnameParts[1];
  338.                     $objectFieldname explode('/'$objectFieldnameParts);
  339.                     $objectFieldname $objectFieldname[0];
  340.                     if ($type == 'objectbrick') {
  341.                         $objectFieldDef $classDefinition->getFieldDefinition($objectFieldname);
  342.                         if ($objectFieldDef instanceof Objectbricks) {
  343.                             $allowedBricks $objectFieldDef->getAllowedTypes();
  344.                             if (is_array($allowedBricks)) {
  345.                                 foreach ($allowedBricks as $allowedBrick) {
  346.                                     $brickDef Definition::getByKey($allowedBrick);
  347.                                     if ($brickDef instanceof Definition) {
  348.                                         $lfDef $brickDef->getFieldDefinition('localizedfields');
  349.                                         if ($lfDef instanceof Localizedfields) {
  350.                                             $fd $lfDef->getFieldDefinition($this->getFieldname());
  351.                                             break;
  352.                                         }
  353.                                     }
  354.                                 }
  355.                             }
  356.                         }
  357.                     } elseif ($type == 'fieldcollection') {
  358.                         // note that for fieldcollections we need the object data for resolving the
  359.                         // fieldcollection type. alternative: store the fc type as well (similar to class id)
  360.                         $object Concrete::getById($this->getObjectId());
  361.                         $getter 'get' ucfirst($objectFieldname);
  362.                         if ($object && method_exists($object$getter)) {
  363.                             $fc $object->$getter();
  364.                             if ($fc instanceof Fieldcollection) {
  365.                                 $index explode('/'$objectFieldnameParts);
  366.                                 $index = (int) $index[1];
  367.                                 $item $fc->get($index);
  368.                                 if ($item instanceof AbstractData) {
  369.                                     if ($colDef Fieldcollection\Definition::getByKey($item->getType())) {
  370.                                         $lfDef $colDef->getFieldDefinition('localizedfields');
  371.                                         if ($lfDef instanceof Localizedfields) {
  372.                                             $fd $lfDef->getFieldDefinition($this->getFieldname());
  373.                                         }
  374.                                     }
  375.                                 }
  376.                             }
  377.                         }
  378.                     }
  379.                 } else {
  380.                     $lfDef $classDefinition->getFieldDefinition('localizedfields');
  381.                     if ($lfDef instanceof Localizedfields) {
  382.                         $fd $lfDef->getFieldDefinition($this->getFieldname());
  383.                     }
  384.                 }
  385.             } elseif ($this->getOwnertype() === 'objectbrick') {
  386.                 $brickDef Definition::getByKey($this->getPosition());
  387.                 if ($brickDef) {
  388.                     $fd $brickDef->getFieldDefinition($this->getFieldname());
  389.                 }
  390.             } elseif ($this->getOwnertype() == 'fieldcollection') {
  391.                 $ownerName $this->getOwnername();
  392.                 $getter 'get' ucfirst($ownerName);
  393.                 // note that for fieldcollections we need the object data for resolving the
  394.                 // fieldcollection type. alternative: store the fc type as well (similar to class id)
  395.                 $object Concrete::getById($this->getObjectId());
  396.                 if (method_exists($object$getter)) {
  397.                     $fcValue $object->$getter();
  398.                     if ($fcValue instanceof Fieldcollection) {
  399.                         $item $fcValue->get($this->getIndex());
  400.                         $fcType $item->getType();
  401.                         if ($fcDef Fieldcollection\Definition::getByKey($fcType)) {
  402.                             $fd $fcDef->getFieldDefinition($this->getFieldname());
  403.                         }
  404.                     }
  405.                 }
  406.             }
  407.         }
  408.         if (!$fd instanceof \Pimcore\Model\DataObject\ClassDefinition\Data\UrlSlug) {
  409.             // slug could not be resolved which means that the data model has changed in the meantime, delete me.
  410.             $this->delete();
  411.             throw new \Exception('Could not resolve field definition for slug: ' $this->getSlug(). '. Remove it!');
  412.         }
  413.         return $fd->getAction();
  414.     }
  415.     /**
  416.      * @throws \Exception
  417.      */
  418.     public function delete()
  419.     {
  420.         $db Db::get();
  421.         $db->delete(self::TABLE_NAME, ['slug' => $this->getSlug(), 'siteId' => $this->getSiteId()]);
  422.         RuntimeCache::set(self::getCacheKey($this->getSlug(), $this->getSiteId()), null);
  423.     }
  424.     /**
  425.      * @param int $siteId
  426.      *
  427.      * @throws \Exception
  428.      */
  429.     public static function handleSiteDeleted(int $siteId)
  430.     {
  431.         $db Db::get();
  432.         $db->delete(self::TABLE_NAME, ['siteId' => $siteId]);
  433.     }
  434.     /**
  435.      * @param string $classId
  436.      *
  437.      * @throws \Exception
  438.      */
  439.     public static function handleClassDeleted(string $classId)
  440.     {
  441.         $db Db::get();
  442.         $db->delete(self::TABLE_NAME, ['classId' => $classId]);
  443.     }
  444.     /**
  445.      * @internal
  446.      *
  447.      * @param string $path
  448.      * @param int $siteId
  449.      *
  450.      * @return string
  451.      */
  452.     protected static function getCacheKey($path$siteId): string
  453.     {
  454.         return "UrlSlug~~{$path}~~{$siteId}";
  455.     }
  456. }