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

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