Completed
Push — master ( 1711fe...805de4 )
by Vitaly
02:36
created

Virtual::analyzeEntityRecord()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
rs 9.4285
cc 1
eloc 7
nc 1
nop 2
1
<?php
2
//[PHPCOMPRESSOR(remove,start)]
3
/**
4
 * Created by Vitaly Iegorov <[email protected]>.
5
 * on 23.03.16 at 11:45
6
 */
7
namespace samsoncms\api\generator\analyzer;
8
9
use samson\activerecord\dbMySQLConnector;
10
use samsoncms\api\Field;
11
use samsoncms\api\generator\exception\ParentEntityNotFound;
12
13
/**
14
 * Generic entities metadata analyzer.
15
 *
16
 * @package samsoncms\api\analyzer
17
 */
18
class Virtual extends Generic
19
{
20
    /**
21
     * Analyze virtual entities and gather their metadata.
22
     *
23
     * @return \samsoncms\api\generator\metadata\Virtual[]
24
     * @throws ParentEntityNotFound
25
     */
26
    public function analyze()
27
    {
28
        $metadataCollection = [];
29
30
        // Iterate all structures, parents first
31
        foreach ($this->getVirtualEntities() as $structureRow) {
32
            // Fill in entity metadata
33
            $metadata = new \samsoncms\api\generator\metadata\Virtual();
34
35
            $this->analyzeEntityRecord($metadata, $structureRow);
36
37
            // TODO: Add multiple parent and fetching their data in a loop
38
39
            // Set pointer to parent entity
40
            if (null !== $metadata->parentID) {
41
                if (array_key_exists($metadata->parentID, $metadataCollection)) {
42
                    $metadata->parent = $metadataCollection[$metadata->parentID];
43
                    // Add all parent metadata to current object
44
                    $metadata->defaultValues = $metadata->parent->defaultValues;
45
                    $metadata->realNames = $metadata->parent->realNames;
46
                    $metadata->allFieldIDs = $metadata->parent->allFieldIDs;
47
                    $metadata->allFieldNames = $metadata->parent->allFieldNames;
48
                    $metadata->allFieldValueColumns = $metadata->parent->allFieldValueColumns;
49
                    $metadata->allFieldTypes = $metadata->parent->allFieldTypes;
50
                    $metadata->fieldDescriptions = $metadata->parent->fieldDescriptions;
51
                    $metadata->localizedFieldIDs = $metadata->parent->localizedFieldIDs;
52
                    $metadata->notLocalizedFieldIDs = $metadata->parent->notLocalizedFieldIDs;
53
                } else {
54
                    throw new ParentEntityNotFound($metadata->parentID);
55
                }
56
            }
57
58
            // Get old AR collections of metadata
59
            $metadata->arSelect = \samson\activerecord\material::$_sql_select;
60
            $metadata->arAttributes = \samson\activerecord\material::$_attributes;
61
            $metadata->arMap = \samson\activerecord\material::$_map;
62
            $metadata->arFrom = \samson\activerecord\material::$_sql_from;
63
            $metadata->arGroup = \samson\activerecord\material::$_own_group;
64
            $metadata->arRelationAlias = \samson\activerecord\material::$_relation_alias;
65
            $metadata->arRelationType = \samson\activerecord\material::$_relation_type;
66
            $metadata->arRelations = \samson\activerecord\material::$_relations;
67
68
            // Add SamsonCMS material needed data
69
            $metadata->arSelect['this'] = ' STRAIGHT_JOIN ' . $metadata->arSelect['this'];
70
            $metadata->arFrom['this'] .= "\n" .
71
                'LEFT JOIN ' . dbMySQLConnector::$prefix . 'materialfield as _mf
72
            ON ' . dbMySQLConnector::$prefix . 'material.MaterialID = _mf.MaterialID';
73
            $metadata->arGroup[] = dbMySQLConnector::$prefix . 'material.MaterialID';
74
75
            // Iterate entity fields
76
            foreach ($this->getEntityFields($structureRow['StructureID']) as $fieldID => $fieldRow) {
77
                $this->analyzeFieldRecord($metadata, $fieldID, $fieldRow);
78
79
                // Get camelCase and transliterated field name
80
                $fieldName = $this->fieldName($fieldRow['Name']);
81
82
                // Fill localization fields collections
83
                if ($fieldRow[Field::F_LOCALIZED] == 1) {
84
                    $metadata->localizedFieldIDs[$fieldID] = $fieldName;
85
                } else {
86
                    $metadata->notLocalizedFieldIDs[$fieldID] = $fieldName;
87
                }
88
89
                // Set old AR collections of metadata
90
                $metadata->arAttributes[$fieldName] = $fieldName;
91
                $metadata->arMap[$fieldName] = dbMySQLConnector::$prefix . 'material.' . $fieldName;
92
93
                // Add additional field column to entity query
94
                $equal = '((_mf.FieldID = ' . $fieldID . ')&&(_mf.locale ' . ($fieldRow['local'] ? ' = "@locale"' : 'IS NULL') . '))';
95
                $metadata->arSelect['this'] .= "\n\t\t" . ',MAX(IF(' . $equal . ', _mf.`' . Field::valueColumn($fieldRow['Type']) . '`, NULL)) as `' . $fieldName . '`';
96
            }
97
98
            // Store metadata by entity identifier
99
            $metadataCollection[$structureRow['StructureID']] = $metadata;
100
            // Store global collection
101
            self::$metadata[$structureRow['StructureID']] = $metadata;
102
        }
103
104
105
        return $metadataCollection;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $metadataCollection; (array) is incompatible with the return type of the parent method samsoncms\api\generator\analyzer\Generic::analyze of type samsoncms\api\generator\metadata\Generic[]|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
106
    }
107
108
109
    /**
110
     * Analyze entity.
111
     *
112
     * @param \samsoncms\api\generator\metadata\Virtual $metadata
113
     * @param array $structureRow Entity database row
114
     */
115
    public function analyzeEntityRecord(&$metadata, array $structureRow)
116
    {
117
        $metadata->structureRow = $structureRow;
118
119
        // Get CapsCase and transliterated entity name
120
        $metadata->entity = $this->entityName($structureRow['Name']);
121
        $metadata->entityClassName = $this->fullEntityName($metadata->entity);
122
        $metadata->entityRealName = $structureRow['Name'];
123
        $metadata->entityID = $structureRow['StructureID'];
124
125
        // Try to find entity parent identifier for building future relations
126
        $metadata->parentID = $this->getParentEntity($structureRow['StructureID']);
127
    }
128
129
    /**
130
     * Virtual entity additional field analyzer.
131
     *
132
     * @param \samsoncms\api\generator\metadata\Virtual $metadata Metadata instance for filling
133
     * @param int      $fieldID Additional field identifier
134
     * @param array $fieldRow Additional field database row
135
     */
136
    public function analyzeFieldRecord(&$metadata, $fieldID, array $fieldRow)
137
    {
138
        // Get camelCase and transliterated field name
139
        $fieldName = $this->fieldName($fieldRow['Name']);
140
141
        // TODO: Set default for additional field storing type accordingly.
142
143
        // Store field metadata
144
        $metadata->realNames[$fieldRow['Name']] = $fieldName;
145
        $metadata->allFieldIDs[$fieldID] = $fieldName;
146
        $metadata->allFieldNames[$fieldName] = $fieldID;
147
        $metadata->allFieldValueColumns[$fieldID] = Field::valueColumn($fieldRow[Field::F_TYPE]);
148
        $metadata->allFieldTypes[$fieldID] = Field::phpType($fieldRow['Type']);
149
        $metadata->allFieldCmsTypes[$fieldID] = (int)$fieldRow['Type'];
150
        $metadata->fieldDescriptions[$fieldID] = $fieldRow['Description'] . ', ' . $fieldRow['Name'] . '#' . $fieldID;
151
        $metadata->fieldRawDescriptions[$fieldID] = $fieldRow['Description'];
152
    }
153
154
    /**
155
     * Get entity fields.
156
     *
157
     * @param int $entityID Entity identifier
158
     *
159
     * @return array Collection of entity fields
160
     */
161
    protected function getEntityFields($entityID)
162
    {
163
        $return = array();
164
        // TODO: Optimize queries make one single query with only needed data
165
        foreach ($this->database->fetch('SELECT * FROM `structurefield` WHERE `StructureID` = "' . $entityID . '" AND `Active` = "1"') as $fieldStructureRow) {
166
            foreach ($this->database->fetch('SELECT * FROM `field` WHERE `FieldID` = "' . $fieldStructureRow['FieldID'] . '"') as $fieldRow) {
167
                $return[$fieldRow['FieldID']] = $fieldRow;
168
            }
169
        }
170
171
        return $return;
172
    }
173
174
    /**
175
     * Find entity parent identifier.
176
     *
177
     * @param int $entityID Entity identifier
178
     *
179
     * @return null|int Parent entity identifier
180
     */
181
    public function getParentEntity($entityID)
182
    {
183
        $parentData = $this->database->fetch('
184
SELECT *
185
FROM structure_relation as sm
186
JOIN structure as s ON s.StructureID = sm.parent_id
187
WHERE sm.child_id = "' . $entityID . '"
188
AND s.StructureID != "' . $entityID . '"
189
');
190
        // Get parent entity identifier
191
        return count($parentData) ? $parentData[0]['StructureID'] : null;
192
    }
193
194
    /**
195
     * Get child entities by parent identifier.
196
     *
197
     * @param int $parentId Parent entity identifier
198
     *
199
     * @return array Get collection of child navigation objects
200
     */
201
    protected function getChildEntities($parentId)
202
    {
203
        return $this->database->fetch('
204
        SELECT * FROM `structure`
205
        WHERE `Active` = "1" AND `ParentID` = ' . $parentId . '
206
        ORDER BY `ParentID` ASC
207
        ');
208
    }
209
210
    /**
211
     * Get virtual entities from database by their type.
212
     *
213
     * @param int $type Virtual entity type
214
     *
215
     * @return array Get collection of navigation objects
216
     */
217
    protected function getVirtualEntities($type = 0)
218
    {
219
        return $this->database->fetch('
220
        SELECT * FROM `structure`
221
        WHERE `Active` = "1" AND `Type` = "' . $type . '"
222
        ORDER BY `ParentID` ASC
223
        ');
224
    }
225
}
226
//[PHPCOMPRESSOR(remove,end)]