vendor/pimcore/pimcore/models/DataObject/ClassDefinition/Data/ManyToManyRelation.php line 284

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\ClassDefinition\Data;
  15. use Pimcore\Model;
  16. use Pimcore\Model\Asset;
  17. use Pimcore\Model\DataObject;
  18. use Pimcore\Model\DataObject\ClassDefinition\Data\Relations\AbstractRelations;
  19. use Pimcore\Model\Document;
  20. use Pimcore\Model\Element;
  21. use Pimcore\Normalizer\NormalizerInterface;
  22. class ManyToManyRelation extends AbstractRelations implements QueryResourcePersistenceAwareInterfaceOptimizedAdminLoadingInterfaceTypeDeclarationSupportInterfaceVarExporterInterfaceNormalizerInterfaceIdRewriterInterfacePreGetDataInterfacePreSetDataInterface
  23. {
  24.     use Model\DataObject\ClassDefinition\Data\Extension\Relation;
  25.     use Extension\QueryColumnType;
  26.     use DataObject\ClassDefinition\Data\Relations\AllowObjectRelationTrait;
  27.     use DataObject\ClassDefinition\Data\Relations\AllowAssetRelationTrait;
  28.     use DataObject\ClassDefinition\Data\Relations\AllowDocumentRelationTrait;
  29.     use DataObject\ClassDefinition\Data\Relations\ManyToManyRelationTrait;
  30.     use DataObject\ClassDefinition\Data\Extension\RelationFilterConditionParser;
  31.     /**
  32.      * Static type of this element
  33.      *
  34.      * @internal
  35.      *
  36.      * @var string
  37.      */
  38.     public $fieldtype 'manyToManyRelation';
  39.     /**
  40.      * @internal
  41.      *
  42.      * @var string|int
  43.      */
  44.     public $width 0;
  45.     /**
  46.      * Type for the column to query
  47.      *
  48.      * @internal
  49.      *
  50.      * @var string|int
  51.      */
  52.     public $height 0;
  53.     /**
  54.      * @internal
  55.      *
  56.      * @var int|null
  57.      */
  58.     public $maxItems;
  59.     /**
  60.      * @internal
  61.      *
  62.      * @var string
  63.      */
  64.     public $assetUploadPath;
  65.     /**
  66.      * Type for the column to query
  67.      *
  68.      * @internal
  69.      *
  70.      * @var string
  71.      */
  72.     public $queryColumnType 'text';
  73.     /**
  74.      * @internal
  75.      *
  76.      * @var bool
  77.      */
  78.     public $relationType true;
  79.     /**
  80.      * @internal
  81.      *
  82.      * @var bool
  83.      */
  84.     public $objectsAllowed false;
  85.     /**
  86.      * @internal
  87.      *
  88.      * @var bool
  89.      */
  90.     public $assetsAllowed false;
  91.     /**
  92.      * Allowed asset types
  93.      *
  94.      * @internal
  95.      *
  96.      * @var array
  97.      */
  98.     public $assetTypes = [];
  99.     /**
  100.      * @internal
  101.      *
  102.      * @var bool
  103.      */
  104.     public $documentsAllowed false;
  105.     /**
  106.      * Allowed document types
  107.      *
  108.      * @internal
  109.      *
  110.      * @var array
  111.      */
  112.     public $documentTypes = [];
  113.     /**
  114.      * @internal
  115.      *
  116.      * @var bool
  117.      */
  118.     public $enableTextSelection false;
  119.     /**
  120.      * @return bool
  121.      */
  122.     public function getObjectsAllowed()
  123.     {
  124.         return $this->objectsAllowed;
  125.     }
  126.     /**
  127.      * @param bool $objectsAllowed
  128.      *
  129.      * @return $this
  130.      */
  131.     public function setObjectsAllowed($objectsAllowed)
  132.     {
  133.         $this->objectsAllowed $objectsAllowed;
  134.         return $this;
  135.     }
  136.     /**
  137.      * @return bool
  138.      */
  139.     public function getDocumentsAllowed()
  140.     {
  141.         return $this->documentsAllowed;
  142.     }
  143.     /**
  144.      * @param bool $documentsAllowed
  145.      *
  146.      * @return $this
  147.      */
  148.     public function setDocumentsAllowed($documentsAllowed)
  149.     {
  150.         $this->documentsAllowed $documentsAllowed;
  151.         return $this;
  152.     }
  153.     /**
  154.      * @return array
  155.      */
  156.     public function getDocumentTypes()
  157.     {
  158.         return $this->documentTypes ?: [];
  159.     }
  160.     /**
  161.      * @param array $documentTypes
  162.      *
  163.      * @return $this
  164.      */
  165.     public function setDocumentTypes($documentTypes)
  166.     {
  167.         $this->documentTypes Element\Service::fixAllowedTypes($documentTypes'documentTypes');
  168.         return $this;
  169.     }
  170.     /**
  171.      *
  172.      * @return bool
  173.      */
  174.     public function getAssetsAllowed()
  175.     {
  176.         return $this->assetsAllowed;
  177.     }
  178.     /**
  179.      *
  180.      * @param bool $assetsAllowed
  181.      *
  182.      * @return $this
  183.      */
  184.     public function setAssetsAllowed($assetsAllowed)
  185.     {
  186.         $this->assetsAllowed $assetsAllowed;
  187.         return $this;
  188.     }
  189.     /**
  190.      * @return array
  191.      */
  192.     public function getAssetTypes()
  193.     {
  194.         return $this->assetTypes;
  195.     }
  196.     /**
  197.      * @param array $assetTypes
  198.      *
  199.      * @return $this
  200.      */
  201.     public function setAssetTypes($assetTypes)
  202.     {
  203.         $this->assetTypes Element\Service::fixAllowedTypes($assetTypes'assetTypes');
  204.         return $this;
  205.     }
  206.     /**
  207.      * {@inheritdoc}
  208.      */
  209.     protected function prepareDataForPersistence($data$object null$params = [])
  210.     {
  211.         $return = [];
  212.         if (is_array($data) && count($data) > 0) {
  213.             $counter 1;
  214.             foreach ($data as $object) {
  215.                 if ($object instanceof Element\ElementInterface) {
  216.                     $return[] = [
  217.                         'dest_id' => $object->getId(),
  218.                         'type' => Element\Service::getElementType($object),
  219.                         'fieldname' => $this->getName(),
  220.                         'index' => $counter,
  221.                     ];
  222.                 }
  223.                 $counter++;
  224.             }
  225.             return $return;
  226.         } elseif (is_array($data) && count($data) === 0) {
  227.             //give empty array if data was not null
  228.             return [];
  229.         } else {
  230.             //return null if data was null  - this indicates data was not loaded
  231.             return null;
  232.         }
  233.     }
  234.     /**
  235.      * {@inheritdoc}
  236.      */
  237.     protected function loadData(array $data$object null$params = [])
  238.     {
  239.         $elements = [
  240.             'dirty' => false,
  241.             'data' => [],
  242.         ];
  243.         foreach ($data as $element) {
  244.             $e null;
  245.             if ($element['type'] === 'object') {
  246.                 $e DataObject::getById($element['dest_id']);
  247.             } elseif ($element['type'] === 'asset') {
  248.                 $e Asset::getById($element['dest_id']);
  249.             } elseif ($element['type'] === 'document') {
  250.                 $e Document::getById($element['dest_id']);
  251.             }
  252.             if ($e instanceof Element\ElementInterface) {
  253.                 $elements['data'][] = $e;
  254.             } else {
  255.                 $elements['dirty'] = true;
  256.             }
  257.         }
  258.         //must return array - otherwise this means data is not loaded
  259.         return $elements;
  260.     }
  261.     /**
  262.      * @see QueryResourcePersistenceAwareInterface::getDataForQueryResource
  263.      *
  264.      * @param mixed $data
  265.      * @param null|DataObject\Concrete $object
  266.      * @param mixed $params
  267.      *
  268.      * @throws \Exception
  269.      *
  270.      * @return string|null
  271.      */
  272.     public function getDataForQueryResource($data$object null$params = [])
  273.     {
  274.         //return null when data is not set
  275.         if (!$data) {
  276.             return null;
  277.         }
  278.         $d = [];
  279.         if (is_array($data)) {
  280.             foreach ($data as $element) {
  281.                 if ($element instanceof Element\ElementInterface) {
  282.                     $elementType Element\Service::getElementType($element);
  283.                     $d[] = $elementType '|' $element->getId();
  284.                 }
  285.             }
  286.             return ',' implode(','$d) . ',';
  287.         }
  288.         throw new \Exception('invalid data passed to getDataForQueryResource - must be array');
  289.     }
  290.     /**
  291.      * @see Data::getDataForEditmode
  292.      *
  293.      * @param array $data
  294.      * @param null|DataObject\Concrete $object
  295.      * @param mixed $params
  296.      *
  297.      * @return array|null
  298.      */
  299.     public function getDataForEditmode($data$object null$params = [])
  300.     {
  301.         $return = [];
  302.         if (is_array($data) && count($data) > 0) {
  303.             foreach ($data as $element) {
  304.                 if ($element instanceof DataObject\Concrete) {
  305.                     $return[] = [$element->getId(), $element->getRealFullPath(), DataObject::OBJECT_TYPE_OBJECT$element->getClassName(), $element->getPublished()];
  306.                 } elseif ($element instanceof DataObject\AbstractObject) {
  307.                     $return[] = [$element->getId(), $element->getRealFullPath(), DataObject::OBJECT_TYPE_OBJECTDataObject::OBJECT_TYPE_FOLDER];
  308.                 } elseif ($element instanceof Asset) {
  309.                     $return[] = [$element->getId(), $element->getRealFullPath(), 'asset'$element->getType()];
  310.                 } elseif ($element instanceof Document) {
  311.                     $return[] = [$element->getId(), $element->getRealFullPath(), 'document'$element->getType(), $element->getPublished()];
  312.                 }
  313.             }
  314.             if (empty($return)) {
  315.                 $return null;
  316.             }
  317.             return $return;
  318.         }
  319.         return null;
  320.     }
  321.     /**
  322.      * @see Data::getDataFromEditmode
  323.      *
  324.      * @param array|null|false $data
  325.      * @param null|DataObject\Concrete $object
  326.      * @param mixed $params
  327.      *
  328.      * @return array|null
  329.      */
  330.     public function getDataFromEditmode($data$object null$params = [])
  331.     {
  332.         //if not set, return null
  333.         if ($data === null || $data === false) {
  334.             return null;
  335.         }
  336.         $elements = [];
  337.         if (is_array($data) && count($data) > 0) {
  338.             foreach ($data as $element) {
  339.                 $e null;
  340.                 if ($element['type'] == 'object') {
  341.                     $e DataObject::getById($element['id']);
  342.                 } elseif ($element['type'] == 'asset') {
  343.                     $e Asset::getById($element['id']);
  344.                 } elseif ($element['type'] == 'document') {
  345.                     $e Document::getById($element['id']);
  346.                 }
  347.                 if ($e instanceof Element\ElementInterface) {
  348.                     $elements[] = $e;
  349.                 }
  350.             }
  351.         }
  352.         //must return array if data shall be set
  353.         return $elements;
  354.     }
  355.     /**
  356.      * @param array $data
  357.      * @param null|DataObject\Concrete $object
  358.      * @param mixed $params
  359.      *
  360.      * @return array
  361.      */
  362.     public function getDataFromGridEditor($data$object null$params = [])
  363.     {
  364.         return $this->getDataFromEditmode($data$object$params);
  365.     }
  366.     /**
  367.      * @param array|null $data
  368.      * @param DataObject\Concrete $object
  369.      * @param array $params
  370.      *
  371.      * @return array
  372.      *
  373.      * @todo: $pathes is undefined
  374.      */
  375.     public function getDataForGrid($data$object null$params = [])
  376.     {
  377.         return $this->getDataForEditmode($data$object$params);
  378.     }
  379.     /**
  380.      * @see Data::getVersionPreview
  381.      *
  382.      * @param Element\ElementInterface[]|null $data
  383.      * @param null|DataObject\Concrete $object
  384.      * @param mixed $params
  385.      *
  386.      * @return string|null
  387.      */
  388.     public function getVersionPreview($data$object null$params = [])
  389.     {
  390.         if (is_array($data) && count($data) > 0) {
  391.             $paths = [];
  392.             foreach ($data as $element) {
  393.                 if ($element instanceof Element\ElementInterface) {
  394.                     $paths[] = Element\Service::getElementType($element) .' '$element->getRealFullPath();
  395.                 }
  396.             }
  397.             return implode('<br />'$paths);
  398.         }
  399.         return null;
  400.     }
  401.     /**
  402.      * @return string|int
  403.      */
  404.     public function getWidth()
  405.     {
  406.         return $this->width;
  407.     }
  408.     /**
  409.      * @param string|int $width
  410.      *
  411.      * @return $this
  412.      */
  413.     public function setWidth($width)
  414.     {
  415.         if (is_numeric($width)) {
  416.             $width = (int)$width;
  417.         }
  418.         $this->width $width;
  419.         return $this;
  420.     }
  421.     /**
  422.      * @return string|int
  423.      */
  424.     public function getHeight()
  425.     {
  426.         return $this->height;
  427.     }
  428.     /**
  429.      * @param string|int $height
  430.      *
  431.      * @return $this
  432.      */
  433.     public function setHeight($height)
  434.     {
  435.         if (is_numeric($height)) {
  436.             $height = (int)$height;
  437.         }
  438.         $this->height $height;
  439.         return $this;
  440.     }
  441.     /**
  442.      * {@inheritdoc}
  443.      */
  444.     public function checkValidity($data$omitMandatoryCheck false$params = [])
  445.     {
  446.         if (!$omitMandatoryCheck && $this->getMandatory() && empty($data)) {
  447.             throw new Element\ValidationException('Empty mandatory field [ ' $this->getName() . ' ]');
  448.         }
  449.         $allow true;
  450.         if (is_array($data)) {
  451.             $this->performMultipleAssignmentCheck($data);
  452.             foreach ($data as $d) {
  453.                 if ($d instanceof Document) {
  454.                     $allow $this->allowDocumentRelation($d);
  455.                 } elseif ($d instanceof Asset) {
  456.                     $allow $this->allowAssetRelation($d);
  457.                 } elseif ($d instanceof DataObject\AbstractObject) {
  458.                     $allow $this->allowObjectRelation($d);
  459.                 } elseif (empty($d)) {
  460.                     $allow true;
  461.                 } else {
  462.                     $allow false;
  463.                 }
  464.                 if (!$allow) {
  465.                     throw new Element\ValidationException(sprintf('Invalid relation in field `%s` [type: %s]'$this->getName(), $this->getFieldtype()));
  466.                 }
  467.             }
  468.             if ($this->getMaxItems() && count($data) > $this->getMaxItems()) {
  469.                 throw new Element\ValidationException('Number of allowed relations in field `' $this->getName() . '` exceeded (max. ' $this->getMaxItems() . ')');
  470.             }
  471.         }
  472.     }
  473.     /**
  474.      * {@inheritdoc}
  475.      */
  476.     public function getForCsvExport($object$params = [])
  477.     {
  478.         $data $this->getDataFromObjectParam($object$params);
  479.         if (is_array($data)) {
  480.             $paths = [];
  481.             foreach ($data as $eo) {
  482.                 if ($eo instanceof Element\ElementInterface) {
  483.                     $paths[] = Element\Service::getElementType($eo) . ':' $eo->getRealFullPath();
  484.                 }
  485.             }
  486.             return implode(','$paths);
  487.         }
  488.         return '';
  489.     }
  490.     /**
  491.      * {@inheritdoc}
  492.      */
  493.     public function getCacheTags($data, array $tags = [])
  494.     {
  495.         return $tags;
  496.     }
  497.     /**
  498.      * @param Element\ElementInterface[]|null $data
  499.      *
  500.      * @return array
  501.      */
  502.     public function resolveDependencies($data)
  503.     {
  504.         $dependencies = [];
  505.         if (is_array($data) && count($data) > 0) {
  506.             foreach ($data as $e) {
  507.                 if ($e instanceof Element\ElementInterface) {
  508.                     $elementType Element\Service::getElementType($e);
  509.                     $dependencies[$elementType '_' $e->getId()] = [
  510.                         'id' => $e->getId(),
  511.                         'type' => $elementType,
  512.                     ];
  513.                 }
  514.             }
  515.         }
  516.         return $dependencies;
  517.     }
  518.     /**
  519.      * { @inheritdoc }
  520.      */
  521.     public function preGetData(/** mixed */ $container/** array */ $params = []) // : mixed
  522.     {
  523.         $data null;
  524.         if ($container instanceof DataObject\Concrete) {
  525.             $data $container->getObjectVar($this->getName());
  526.             if (!$container->isLazyKeyLoaded($this->getName())) {
  527.                 $data $this->load($container);
  528.                 $container->setObjectVar($this->getName(), $data);
  529.                 $this->markLazyloadedFieldAsLoaded($container);
  530.                 if ($container instanceof Element\DirtyIndicatorInterface) {
  531.                     $container->markFieldDirty($this->getName(), false);
  532.                 }
  533.             }
  534.         } elseif ($container instanceof DataObject\Localizedfield) {
  535.             $data $params['data'];
  536.         } elseif ($container instanceof DataObject\Fieldcollection\Data\AbstractData) {
  537.             parent::loadLazyFieldcollectionField($container);
  538.             $data $container->getObjectVar($this->getName());
  539.         } elseif ($container instanceof DataObject\Objectbrick\Data\AbstractData) {
  540.             parent::loadLazyBrickField($container);
  541.             $data $container->getObjectVar($this->getName());
  542.         }
  543.         if (DataObject::doHideUnpublished() && is_array($data)) {
  544.             $publishedList = [];
  545.             foreach ($data as $listElement) {
  546.                 if (Element\Service::isPublished($listElement)) {
  547.                     $publishedList[] = $listElement;
  548.                 }
  549.             }
  550.             return $publishedList;
  551.         }
  552.         return is_array($data) ? $data : [];
  553.     }
  554.     /**
  555.      * { @inheritdoc }
  556.      */
  557.     public function preSetData(/** mixed */ $container/**  mixed */ $data/** array */ $params = []) // : mixed
  558.     {
  559.         if ($data === null) {
  560.             $data = [];
  561.         }
  562.         $this->markLazyloadedFieldAsLoaded($container);
  563.         return $data;
  564.     }
  565.     /**
  566.      * @param int|null $maxItems
  567.      *
  568.      * @return $this
  569.      */
  570.     public function setMaxItems($maxItems)
  571.     {
  572.         $this->maxItems $this->getAsIntegerCast($maxItems);
  573.         return $this;
  574.     }
  575.     /**
  576.      * @return int|null
  577.      */
  578.     public function getMaxItems()
  579.     {
  580.         return $this->maxItems;
  581.     }
  582.     /**
  583.      * @param string $assetUploadPath
  584.      *
  585.      * @return $this
  586.      */
  587.     public function setAssetUploadPath($assetUploadPath)
  588.     {
  589.         $this->assetUploadPath $assetUploadPath;
  590.         return $this;
  591.     }
  592.     /**
  593.      * @return string
  594.      */
  595.     public function getAssetUploadPath()
  596.     {
  597.         return $this->assetUploadPath;
  598.     }
  599.     /**
  600.      * {@inheritdoc}
  601.      */
  602.     public function isDiffChangeAllowed($object$params = [])
  603.     {
  604.         return true;
  605.     }
  606.     /** Generates a pretty version preview (similar to getVersionPreview) can be either html or
  607.      * a image URL. See the https://github.com/pimcore/object-merger bundle documentation for details
  608.      *
  609.      * @param array|null $data
  610.      * @param DataObject\Concrete|null $object
  611.      * @param mixed $params
  612.      *
  613.      * @return array
  614.      */
  615.     public function getDiffVersionPreview($data$object null$params = [])
  616.     {
  617.         $value = [];
  618.         $value['type'] = 'html';
  619.         $value['html'] = '';
  620.         if ($data) {
  621.             $html $this->getVersionPreview($data$object$params);
  622.             $value['html'] = $html;
  623.         }
  624.         return $value;
  625.     }
  626.     /**
  627.      * { @inheritdoc }
  628.      */
  629.     public function rewriteIds(/** mixed */ $container/** array */ $idMapping/** array */ $params = []) /** :mixed */
  630.     {
  631.         $data $this->getDataFromObjectParam($container$params);
  632.         $data $this->rewriteIdsService($data$idMapping);
  633.         return $data;
  634.     }
  635.     /**
  636.      * @param DataObject\ClassDefinition\Data\ManyToManyRelation $mainDefinition
  637.      */
  638.     public function synchronizeWithMainDefinition(DataObject\ClassDefinition\Data $mainDefinition)
  639.     {
  640.         $this->maxItems $mainDefinition->maxItems;
  641.         $this->assetUploadPath $mainDefinition->assetUploadPath;
  642.         $this->relationType $mainDefinition->relationType;
  643.         $this->objectsAllowed $mainDefinition->objectsAllowed;
  644.         $this->assetsAllowed $mainDefinition->assetsAllowed;
  645.         $this->assetTypes $mainDefinition->assetTypes;
  646.         $this->documentsAllowed $mainDefinition->documentsAllowed;
  647.         $this->documentTypes $mainDefinition->documentTypes;
  648.     }
  649.     /**
  650.      * @deprecated will be removed in Pimcore 11
  651.      *
  652.      * @param DataObject\ClassDefinition\Data\ManyToManyRelation $masterDefinition
  653.      */
  654.     public function synchronizeWithMasterDefinition(DataObject\ClassDefinition\Data $masterDefinition)
  655.     {
  656.         trigger_deprecation(
  657.             'pimcore/pimcore',
  658.             '10.6.0',
  659.             sprintf('%s is deprecated and will be removed in Pimcore 11. Use %s instead.'__METHOD__str_replace('Master''Main'__METHOD__))
  660.         );
  661.         $this->synchronizeWithMainDefinition($masterDefinition);
  662.     }
  663.     /**
  664.      * {@inheritdoc}
  665.      */
  666.     protected function getPhpdocType()
  667.     {
  668.         return implode(' | '$this->getPhpDocClassString(true));
  669.     }
  670.     /**
  671.      * {@inheritdoc}
  672.      */
  673.     public function normalize($value$params = [])
  674.     {
  675.         if (is_array($value)) {
  676.             $result = [];
  677.             foreach ($value as $element) {
  678.                 $type Element\Service::getElementType($element);
  679.                 $id $element->getId();
  680.                 $result[] = [
  681.                     'type' => $type,
  682.                     'id' => $id,
  683.                 ];
  684.             }
  685.             return $result;
  686.         }
  687.         return null;
  688.     }
  689.     /** See marshal
  690.      *
  691.      * @param mixed $value
  692.      * @param mixed $params
  693.      *
  694.      * @return array|null
  695.      */
  696.     public function denormalize($value$params = [])
  697.     {
  698.         if (is_array($value)) {
  699.             $result = [];
  700.             foreach ($value as $elementData) {
  701.                 $type $elementData['type'];
  702.                 $id $elementData['id'];
  703.                 $element Element\Service::getElementById($type$id);
  704.                 if ($element) {
  705.                     $result[] = $element;
  706.                 }
  707.             }
  708.             return $result;
  709.         }
  710.         return null;
  711.     }
  712.     /**
  713.      * Returns a ID which must be unique across the grid rows
  714.      *
  715.      * @param array $item
  716.      *
  717.      * @return string
  718.      */
  719.     public function buildUniqueKeyForDiffEditor($item)
  720.     {
  721.         $parts = [
  722.             $item['id'],
  723.             $item['path'],
  724.             $item['type'],
  725.             $item['subtype'],
  726.         ];
  727.         return json_encode($parts);
  728.     }
  729.     /**
  730.      * @param Element\ElementInterface[]|null $originalData
  731.      * @param array|null $data
  732.      * @param null|DataObject\Concrete $object
  733.      * @param array $params
  734.      *
  735.      * @return array
  736.      */
  737.     protected function processDiffDataForEditMode($originalData$data$object null$params = [])
  738.     {
  739.         if ($data) {
  740.             $data $data[0];
  741.             $items $data['data'];
  742.             $newItems = [];
  743.             if ($items) {
  744.                 foreach ($items as $in) {
  745.                     $item = [];
  746.                     $item['id'] = $in[0];
  747.                     $item['path'] = $in[1];
  748.                     $item['type'] = $in[2];
  749.                     $item['subtype'] = $in[3];
  750.                     $unique $this->buildUniqueKeyForDiffEditor($item);
  751.                     $itemId json_encode($item);
  752.                     $raw $itemId;
  753.                     $newItems[] = [
  754.                         'itemId' => $itemId,
  755.                         'title' => $item['path'],
  756.                         'raw' => $raw,
  757.                         'gridrow' => $item,
  758.                         'unique' => $unique,
  759.                     ];
  760.                 }
  761.                 $data['data'] = $newItems;
  762.             }
  763.             $data['value'] = [
  764.                 'type' => 'grid',
  765.                 'columnConfig' => [
  766.                     'id' => [
  767.                         'width' => 60,
  768.                     ],
  769.                     'path' => [
  770.                         'flex' => 2,
  771.                     ],
  772.                 ],
  773.                 'html' => $this->getVersionPreview($originalData$object$params),
  774.             ];
  775.             $newData = [];
  776.             $newData[] = $data;
  777.             return $newData;
  778.         }
  779.         return $data;
  780.     }
  781.     /**
  782.      * {@inheritdoc}
  783.      */
  784.     public function getDiffDataForEditMode($data$object null$params = [])
  785.     {
  786.         $originalData $data;
  787.         $data parent::getDiffDataForEditMode($data$object$params);
  788.         $data $this->processDiffDataForEditMode($originalData$data$object$params);
  789.         return $data;
  790.     }
  791.     /** See parent class.
  792.      * @param array $data
  793.      * @param DataObject\Concrete|null $object
  794.      * @param mixed $params
  795.      *
  796.      * @return array|null
  797.      */
  798.     public function getDiffDataFromEditmode($data$object null$params = [])
  799.     {
  800.         if ($data) {
  801.             $tabledata $data[0]['data'];
  802.             $result = [];
  803.             if ($tabledata) {
  804.                 foreach ($tabledata as $in) {
  805.                     $out json_decode($in['raw'], true);
  806.                     $result[] = $out;
  807.                 }
  808.             }
  809.             return $this->getDataFromEditmode($result$object$params);
  810.         }
  811.         return null;
  812.     }
  813.     /**
  814.      * {@inheritdoc}
  815.      */
  816.     public function isOptimizedAdminLoading(): bool
  817.     {
  818.         return true;
  819.     }
  820.     /**
  821.      * @return bool
  822.      */
  823.     public function isEnableTextSelection(): bool
  824.     {
  825.         return $this->enableTextSelection;
  826.     }
  827.     /**
  828.      * @param bool $enableTextSelection
  829.      */
  830.     public function setEnableTextSelection(bool $enableTextSelection): void
  831.     {
  832.         $this->enableTextSelection $enableTextSelection;
  833.     }
  834.     /**
  835.      * {@inheritdoc}
  836.      */
  837.     public function isFilterable(): bool
  838.     {
  839.         return true;
  840.     }
  841.     /**
  842.      * {@inheritdoc}
  843.      */
  844.     public function addListingFilter(DataObject\Listing $listing$data$operator '=')
  845.     {
  846.         if ($data instanceof Element\ElementInterface) {
  847.             $data = [
  848.                 'id' => $data->getId(),
  849.                 'type' => Element\Service::getElementType($data),
  850.             ];
  851.         }
  852.         if (!isset($data['id'], $data['type'])) {
  853.             throw new \InvalidArgumentException('Please provide an array with keys "id" and "type" or an object which implements '.Element\ElementInterface::class);
  854.         }
  855.         if ($operator === '=') {
  856.             $listing->addConditionParam('`'.$this->getName().'` LIKE ?''%,'.$data['type'].'|'.$data['id'].',%');
  857.             return $listing;
  858.         }
  859.         throw new \InvalidArgumentException('Filtering '.__CLASS__.' does only support "=" operator');
  860.     }
  861.     /**
  862.      * Filter by relation feature
  863.      *
  864.      * @param array|string|null $value
  865.      * @param string            $operator
  866.      * @param array             $params
  867.      *
  868.      * @return string
  869.      */
  870.     public function getFilterConditionExt($value$operator$params = [])
  871.     {
  872.         $name $params['name'] ?: $this->name;
  873.         return $this->getRelationFilterCondition($value$operator$name);
  874.     }
  875. }