Converter.php 4.18 KB
Newer Older
Ketan's avatar
Ketan committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
<?php
/**
 * Attributes configuration converter
 *
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
declare(strict_types=1);

namespace Magento\Framework\Setup\Declaration\Schema\Config;

/**
 * This converter is required for Declaration Filesystem reader:
 *
 * @see \Magento\Framework\Setup\Declaration\Schema\FileSystem\XmlReader
 *
 * Allows to convert declarative schema to raw array and add default values
 * for column types and for constraints.
 */
class Converter implements \Magento\Framework\Config\ConverterInterface
{
    /**
     * Convert config from XML to array.
     *
     * @param  \DOMDocument $source
     * @return array
     */
    public function convert($source): array
    {
        $output = $this->recursiveConvert($this->getTablesNode($source));
        return $output;
    }

    /**
     * We exactly know, that our schema is consists from tables.
     * So we do not need root elements in result, only table names.
     * So proposed to select only tables from all DOMDocument.
     *
     * @param  \DOMDocument $element
     * @return \DOMNodeList
     */
    private function getTablesNode(\DOMDocument $element): \DOMNodeList
    {
        return $element->getElementsByTagName('table');
    }

    /**
     * Convert elements.
     *
     * @param \Traversable $source
     * @return array
     */
    private function recursiveConvert(\Traversable $source): array
    {
        $output = [];
        foreach ($source as $element) {
            if ($element instanceof \DOMElement) {
                $key = $this->getIdAttributeValue($element);

                if ($element->hasChildNodes()) {
                    $output[$element->tagName][$key] =
                        array_replace(
                            $this->recursiveConvert($element->childNodes),
                            $this->interpretAttributes($element)
                        );
                } elseif ($this->hasAttributesExceptIdAttribute($element)) {
                    $output[$element->tagName][$key] = $this->interpretAttributes($element);
                } else {
                    $output[$element->tagName][$key] = $key;
                }
            }
        }

        return $output;
    }

    /**
     * Provide the value of the ID attribute for each element.
     *
     * @param \DOMElement $element
     * @return string
     */
    private function getIdAttributeValue(\DOMElement $element): string
    {
        $idAttributeValue = '';
        switch ($element->tagName) {
            case ('table'):
            case ('column'):
                $idAttributeValue = $element->getAttribute('name');
                break;
            case ('index'):
            case ('constraint'):
                $idAttributeValue = $element->getAttribute('referenceId');
                break;
        }

        return $idAttributeValue;
    }

    /**
     * Check whether we have any attributes except ID attribute.
     *
     * @param  \DOMElement $element
     * @return bool
     */
    private function hasAttributesExceptIdAttribute(\DOMElement $element)
    {
        return $element->hasAttribute('xsi:type') || $element->attributes->length >= 2;
    }

    /**
     * Mix attributes that comes from XML schema with default ones.
     *
     * So if you will not have some attribute in schema - it will be taken from default one.
     *
     * @param  \DOMElement $domElement
     * @return array
     */
    private function interpretAttributes(\DOMElement $domElement): array
    {
        $attributes = $this->getAttributes($domElement);
        $xsiType = $domElement->getAttribute('xsi:type');

        if ($xsiType) {
            $attributes['type'] = $xsiType;
        }

        return $attributes;
    }

    /**
     * Convert XML attributes into raw array with attributes.
     *
     * @param  \DOMElement $element
     * @return array
     */
    private function getAttributes(\DOMElement $element): array
    {
        $attributes = [];
        $attributeNodes = $element->attributes;

        /** @var \DOMAttr $attribute */
        foreach ($attributeNodes as $domAttr) {
            $attributes[$domAttr->name] = $domAttr->value;
        }

        return $attributes;
    }
}