vendor/sulu/sulu/src/Sulu/Component/Rest/ListBuilder/Metadata/ListXmlLoader.php line 138

  1. <?php
  2. /*
  3.  * This file is part of Sulu.
  4.  *
  5.  * (c) Sulu GmbH
  6.  *
  7.  * This source file is subject to the MIT license that is bundled
  8.  * with this source code in the file LICENSE.
  9.  */
  10. namespace Sulu\Component\Rest\ListBuilder\Metadata;
  11. use Sulu\Component\Rest\ListBuilder\FieldDescriptorInterface;
  12. use Sulu\Component\Util\XmlUtil;
  13. use Symfony\Component\Config\Util\XmlUtils;
  14. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  15. /**
  16.  * Parses data from xml and returns general list-builder metadata.
  17.  */
  18. class ListXmlLoader
  19. {
  20.     /**
  21.      * @var ParameterBagInterface
  22.      */
  23.     private $parameterBag;
  24.     public function __construct(ParameterBagInterface $parameterBag)
  25.     {
  26.         $this->parameterBag $parameterBag;
  27.     }
  28.     public function load($resource)
  29.     {
  30.         $listMetadata = new ListMetadata();
  31.         $cwd \getcwd();
  32.         // Necessary only for Windows, no effect on linux. Mute errors for PHP with chdir disabled to avoid E_WARNINGs
  33.         @\chdir(\dirname($resource));
  34.         $xmlDocument XmlUtils::loadFile(
  35.             $resource,
  36.             function(\DOMDocument $dom) use ($resource) {
  37.                 $dom->documentURI $resource;
  38.                 $dom->xinclude();
  39.                 return @$dom->schemaValidate(__DIR__ '/Resources/schema/list-2.0.xsd');
  40.             }
  41.         );
  42.         // Necessary only for Windows, no effect on linux. Mute errors for PHP with chdir disabled to avoid E_WARNINGs
  43.         @\chdir($cwd);
  44.         $xpath = new \DOMXPath($xmlDocument);
  45.         $xpath->registerNamespace('x''http://schemas.sulu.io/list-builder/list');
  46.         $listMetadata->setResource($resource);
  47.         $listMetadata->setKey($xpath->query('/x:list/x:key')->item(0)->nodeValue);
  48.         foreach ($xpath->query('/x:list/x:properties/x:*') as $propertyNode) {
  49.             $listMetadata->addPropertyMetadata($this->loadPropertyMetadata($xpath$propertyNode));
  50.         }
  51.         return $listMetadata;
  52.     }
  53.     /**
  54.      * Extracts attributes from dom-node to create a new property-metadata object.
  55.      *
  56.      * @return AbstractPropertyMetadata
  57.      */
  58.     private function loadPropertyMetadata(\DOMXPath $xpath\DOMNode $propertyNode)
  59.     {
  60.         $propertyMetadata null;
  61.         switch ($propertyNode->nodeName) {
  62.             case 'concatenation-property':
  63.                 $propertyMetadata $this->loadConcatenationPropertyMetadata($xpath$propertyNode);
  64.                 break;
  65.             case 'identity-property':
  66.                 $propertyMetadata $this->loadIdentityPropertyMetadata($xpath$propertyNode);
  67.                 break;
  68.             case 'group-concat-property':
  69.                 $propertyMetadata $this->loadGroupConcatPropertyMetadata($xpath$propertyNode);
  70.                 break;
  71.             case 'case-property':
  72.                 $propertyMetadata $this->loadCasePropertyMetadata($xpath$propertyNode);
  73.                 break;
  74.             case 'count-property':
  75.                 $propertyMetadata $this->loadCountPropertyMetadata($xpath$propertyNode);
  76.                 break;
  77.             case 'property':
  78.                 $propertyMetadata $this->loadSinglePropertyMetadata($xpath$propertyNode);
  79.                 break;
  80.             default:
  81.                 throw new \InvalidArgumentException(\sprintf(
  82.                     'The tag "%s" cannot be handled by this loader',
  83.                     $propertyNode->nodeName
  84.                 ));
  85.         }
  86.         if (null !== $translation XmlUtil::getValueFromXPath('@translation'$xpath$propertyNode)) {
  87.             $propertyMetadata->setTranslation($translation);
  88.         }
  89.         $propertyMetadata->setVisibility(
  90.             XmlUtil::getValueFromXPath(
  91.                 '@visibility',
  92.                 $xpath,
  93.                 $propertyNode,
  94.                 FieldDescriptorInterface::VISIBILITY_NO
  95.             )
  96.         );
  97.         $propertyMetadata->setSearchability(
  98.             XmlUtil::getValueFromXPath(
  99.                 '@searchability',
  100.                 $xpath,
  101.                 $propertyNode,
  102.                 FieldDescriptorInterface::SEARCHABILITY_NEVER
  103.             )
  104.         );
  105.         $propertyMetadata->setSortable(
  106.             XmlUtil::getBooleanValueFromXPath('@sortable'$xpath$propertyNodetrue)
  107.         );
  108.         $propertyMetadata->setWidth(
  109.             XmlUtil::getValueFromXPath(
  110.                 '@width',
  111.                 $xpath,
  112.                 $propertyNode,
  113.                 FieldDescriptorInterface::WIDTH_AUTO
  114.             )
  115.         );
  116.         if (null !== $type XmlUtil::getValueFromXPath('x:transformer/@type'$xpath$propertyNode)) {
  117.             $propertyMetadata->setType($type);
  118.         } elseif (null !== $type XmlUtil::getValueFromXPath('@type'$xpath$propertyNode)) {
  119.             @trigger_deprecation('sulu/sulu''2.2''Attribute "type" of list property should not be used anymore! Use "<transformer type="..."/>" inside of property instead.');
  120.             $propertyMetadata->setType($type);
  121.         }
  122.         $transformerParamNodes $xpath->query('x:transformer/x:params'$propertyNode);
  123.         if (\count($transformerParamNodes) > 0) {
  124.             $propertyMetadata->setTransformerTypeParameters(
  125.                 $this->getParameters(
  126.                     $xpath,
  127.                     $transformerParamNodes->item(0// There can only be one transformer node
  128.                 )
  129.             );
  130.         }
  131.         $propertyMetadata->setFilterType((string) XmlUtil::getValueFromXPath('x:filter/@type'$xpath$propertyNode));
  132.         $filterParamNodes $xpath->query('x:filter/x:params'$propertyNode);
  133.         if (\count($filterParamNodes) > 0) {
  134.             $propertyMetadata->setFilterTypeParameters(
  135.                 $this->getParameters(
  136.                     $xpath,
  137.                     $filterParamNodes->item(0// There can only be one filter node
  138.                 )
  139.             );
  140.         }
  141.         return $propertyMetadata;
  142.     }
  143.     private function loadIdentityPropertyMetadata(\DOMXPath $xpath\DOMElement $propertyNode)
  144.     {
  145.         $field $this->getField($xpath$propertyNode);
  146.         $propertyMetadata = new IdentityPropertyMetadata(
  147.             (string) XmlUtil::getValueFromXPath('@name'$xpath$propertyNode)
  148.         );
  149.         $propertyMetadata->setField($field);
  150.         return $propertyMetadata;
  151.     }
  152.     private function loadCasePropertyMetadata(\DOMXPath $xpath\DOMElement $propertyNode)
  153.     {
  154.         $propertyMetadata = new CasePropertyMetadata(
  155.             (string) XmlUtil::getValueFromXPath('@name'$xpath$propertyNode)
  156.         );
  157.         foreach ($xpath->query('x:field'$propertyNode) as $fieldNode) {
  158.             if (null === $case $this->getField($xpath$fieldNode)) {
  159.                 continue;
  160.             }
  161.             $propertyMetadata->addCase($case);
  162.         }
  163.         return $propertyMetadata;
  164.     }
  165.     private function loadGroupConcatPropertyMetadata(\DOMXPath $xpath\DOMElement $propertyNode)
  166.     {
  167.         $field $this->getField($xpath$propertyNode);
  168.         $propertyMetadata = new GroupConcatPropertyMetadata(
  169.             (string) XmlUtil::getValueFromXPath('@name'$xpath$propertyNode)
  170.         );
  171.         $propertyMetadata->setField($field);
  172.         $propertyMetadata->setGlue((string) XmlUtil::getValueFromXPath('@glue'$xpath$propertyNode' '));
  173.         $propertyMetadata->setDistinct(XmlUtil::getBooleanValueFromXPath('@distinct'$xpath$propertyNode) ?? false);
  174.         return $propertyMetadata;
  175.     }
  176.     private function loadSinglePropertyMetadata(\DOMXPath $xpath\DOMNode $propertyNode)
  177.     {
  178.         $field $this->getField($xpath$propertyNode);
  179.         $propertyMetadata = new SinglePropertyMetadata(
  180.             (string) XmlUtil::getValueFromXPath('@name'$xpath$propertyNode)
  181.         );
  182.         $propertyMetadata->setField($field);
  183.         return $propertyMetadata;
  184.     }
  185.     private function loadCountPropertyMetadata(\DOMXPath $xpath\DOMNode $propertyNode)
  186.     {
  187.         $field $this->getField($xpath$propertyNode);
  188.         $propertyMetadata = new CountPropertyMetadata(
  189.             (string) XmlUtil::getValueFromXPath('@name'$xpath$propertyNode)
  190.         );
  191.         $propertyMetadata->setField($field);
  192.         $propertyMetadata->setDistinct(XmlUtil::getBooleanValueFromXPath('@distinct'$xpath$propertyNode) ?? false);
  193.         return $propertyMetadata;
  194.     }
  195.     private function loadConcatenationPropertyMetadata(\DOMXPath $xpath\DOMNode $propertyNode)
  196.     {
  197.         $propertyMetadata = new ConcatenationPropertyMetadata(
  198.             (string) XmlUtil::getValueFromXPath('@name'$xpath$propertyNode)
  199.         );
  200.         $propertyMetadata->setGlue((string) (XmlUtil::getValueFromXPath('@glue'$xpath$propertyNode) ?? ' '));
  201.         foreach ($xpath->query('x:field'$propertyNode) as $fieldNode) {
  202.             if (null === $field $this->getField($xpath$fieldNode)) {
  203.                 continue;
  204.             }
  205.             $propertyMetadata->addField($field);
  206.         }
  207.         return $propertyMetadata;
  208.     }
  209.     /**
  210.      * Extracts filter type parameters from dom-node.
  211.      *
  212.      * @return ?array
  213.      */
  214.     protected function getParameters(\DOMXPath $xpath\DOMNode $filterNode)
  215.     {
  216.         $parameters = [];
  217.         foreach ($xpath->query('x:param'$filterNode) as $paramNode) {
  218.             $name XmlUtil::getValueFromXPath('@name'$xpath$paramNode);
  219.             $type XmlUtil::getValueFromXPath('@type'$xpath$paramNode);
  220.             if ('collection' === $type) {
  221.                 $parameters[$name] = $this->getParameters($xpath$paramNode);
  222.             } else {
  223.                 $value $this->parameterBag->resolveValue(
  224.                     \trim((string) XmlUtil::getValueFromXPath('@value'$xpath$paramNode))
  225.                 );
  226.                 if (null === $name) {
  227.                     $parameters[] = $value;
  228.                 } else {
  229.                     $parameters[$name] = $value;
  230.                 }
  231.             }
  232.         }
  233.         if (\count($parameters) > 0) {
  234.             return $parameters;
  235.         }
  236.         return null;
  237.     }
  238.     private function getField(\DOMXPath $xpath\DOMElement $fieldNode)
  239.     {
  240.         if (null !== $reference XmlUtil::getValueFromXPath('@property-ref'$xpath$fieldNode)) {
  241.             $nodeList $xpath->query(\sprintf('/x:list/x:properties/x:*[@name="%s"]'$reference));
  242.             if (=== $nodeList->length) {
  243.                 throw new \Exception(\sprintf('Rest metadata doctrine field reference "%s" was not found.'$reference));
  244.             }
  245.             return $this->getField($xpath$nodeList->item(0));
  246.         }
  247.         if (null === ($fieldName XmlUtil::getValueFromXPath('x:field-name'$xpath$fieldNode))
  248.             || null === ($entityName XmlUtil::getValueFromXPath('x:entity-name'$xpath$fieldNode))
  249.         ) {
  250.             return;
  251.         }
  252.         $field = new FieldMetadata($this->resolveParameter($fieldName), $this->resolveParameter($entityName));
  253.         $joinsNodeList $xpath->query('x:joins'$fieldNode);
  254.         if ($joinsNodeList->length 0) {
  255.             $this->getJoinsMetadata($xpath$joinsNodeList->item(0), $field);
  256.         }
  257.         return $field;
  258.     }
  259.     private function getJoinsMetadata(\DOMXPath $xpath\DOMElement $joinsNodeFieldMetadata $field)
  260.     {
  261.         if (null !== $reference XmlUtil::getValueFromXPath('@ref'$xpath$joinsNode)) {
  262.             $nodeList $xpath->query(\sprintf('/x:list/x:joins[@name="%s"]'$reference));
  263.             if (=== $nodeList->length) {
  264.                 throw new \Exception(\sprintf('Rest metadata doctrine joins reference "%s" was not found.'$reference));
  265.             }
  266.             $this->getJoinsMetadata($xpath$nodeList->item(0), $field);
  267.         }
  268.         foreach ($xpath->query('x:join'$joinsNode) as $joinNode) {
  269.             $field->addJoin($this->getJoinMetadata($xpath$joinNode));
  270.         }
  271.     }
  272.     protected function getJoinMetadata(\DOMXPath $xpath\DOMElement $joinNode)
  273.     {
  274.         $joinMetadata = new JoinMetadata();
  275.         if (null !== $fieldName XmlUtil::getValueFromXPath('x:field-name'$xpath$joinNode)) {
  276.             $joinMetadata->setEntityField($this->resolveParameter($fieldName));
  277.         }
  278.         if (null !== $entityName XmlUtil::getValueFromXPath('x:entity-name'$xpath$joinNode)) {
  279.             $joinMetadata->setEntityName($this->resolveParameter($entityName));
  280.         }
  281.         if (null !== $condition XmlUtil::getValueFromXPath('x:condition'$xpath$joinNode)) {
  282.             $joinMetadata->setCondition($this->resolveParameter($condition));
  283.         }
  284.         if (null !== $conditionMethod XmlUtil::getValueFromXPath('x:condition-method'$xpath$joinNode)) {
  285.             /** @var 'ON'|'WITH' $conditionMethod */
  286.             $joinMetadata->setConditionMethod($this->resolveParameter($conditionMethod));
  287.         }
  288.         if (null !== $method XmlUtil::getValueFromXPath('x:method'$xpath$joinNode)) {
  289.             /** @var 'LEFT'|'INNER'|'RIGHT' $method */
  290.             $joinMetadata->setMethod($method);
  291.         }
  292.         return $joinMetadata;
  293.     }
  294.     private function resolveParameter($value)
  295.     {
  296.         return $this->parameterBag->resolveValue($value);
  297.     }
  298. }