Completed
Pull Request — master (#52)
by Olexandr
03:33
created

Generator::fillMetadata()   D

Complexity

Conditions 14
Paths 77

Size

Total Lines 127
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 5
Metric Value
c 6
b 0
f 5
dl 0
loc 127
rs 4.9516
cc 14
eloc 67
nc 77
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: molodyko
5
 * Date: 13.02.2016
6
 * Time: 23:55
7
 */
8
9
namespace samsoncms\api\generator;
10
11
use samson\activerecord\dbMySQLConnector;
12
use samsoncms\api\Field;
13
use samsoncms\api\generator\exception\ParentEntityNotFound;
14
use samsonframework\orm\DatabaseInterface;
15
16
abstract class Generator
17
{
18
    /** @var DatabaseInterface */
19
    protected $database;
20
21
    /** @var \samsonphp\generator\Generator */
22
    protected $generator;
23
24
    /** @var Metadata[] Collection of entities metadata */
25
    protected $metadata = array();
26
27
    /**
28
     * Generator constructor.
29
     * @param DatabaseInterface $database Database instance
30
     * @throws ParentEntityNotFound
31
     * @throws \samsoncms\api\exception\AdditionalFieldTypeNotFound
32
     */
33
    public function __construct(DatabaseInterface $database)
34
    {
35
        $this->generator = new \samsonphp\generator\Generator();
36
        $this->database = $database;
37
    }
38
39
    /**
40
     * Fill metadata
41
     *
42
     * @param null $filter Filter navigations
43
     * @throws ParentEntityNotFound
44
     * @throws \samsoncms\api\exception\AdditionalFieldTypeNotFound
45
     */
46
    public function fillMetadata($filter = null)
47
    {
48
        // Iterate all metadata types
49
        foreach (Metadata::$types as $type) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
50
51
            // Iterate all structures, parents first
52
            foreach ($this->entityNavigations($type) as $structureRow) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
53
54
                // If filter is the function and filter return false then skip this structure
55
                if (is_callable($filter) && (false === $filter($structureRow))) {
56
                    continue;
57
                }
58
59
                // Fill in entity metadata
60
                $metadata = new Metadata($type);
61
62
                // Get CapsCase and transliterated entity name
63
                $metadata->entity = $this->entityName($structureRow['Name']);
64
                // Store entity original data
65
                $metadata->entityRealName = $structureRow['Name'];
66
                $metadata->entityID = $structureRow['StructureID'];
67
68
                // Append entity name suffix dependening on its type
69
                if ($type === Metadata::TYPE_TABLE) {
70
                    $metadata->entity .= 'Table';
71
                }
72
73
                // Try to find entity parent identifier for building future relations
74
                $metadata->parentID = $this->entityParent($structureRow['StructureID']);
75
                // Generate application from current entity
76
                $metadata->generateApplication = $structureRow['applicationGenerate'];
77
                // Show application from current entity
78
                $metadata->showApplication = $structureRow['applicationOutput'];
79
                // Icon for application from current entity
80
                $metadata->iconApplication = $structureRow['applicationIcon'];
81
                // Render application on main page
82
                $metadata->renderMainApplication = $structureRow['applicationRenderMain'];
83
84
                // TODO: Add multiple parent and fetching their data in a loop
85
86
                // Set pointer to parent entity
87
                if (null !== $metadata->parentID && $metadata->type !== Metadata::TYPE_TABLE) {
88
                    if (array_key_exists($metadata->parentID, $this->metadata)) {
89
                        $metadata->parent = $this->metadata[$metadata->parentID];
90
                        // Add all parent metadata to current object
91
                        $metadata->defaultValues = $metadata->parent->defaultValues;
92
                        $metadata->realNames = $metadata->parent->realNames;
93
                        $metadata->allFieldIDs = $metadata->parent->allFieldIDs;
94
                        $metadata->allFieldNames = $metadata->parent->allFieldNames;
95
                        $metadata->allFieldValueColumns = $metadata->parent->allFieldValueColumns;
96
                        $metadata->allFieldTypes = $metadata->parent->allFieldTypes;
97
                        $metadata->fieldDescriptions = $metadata->parent->fieldDescriptions;
98
                        $metadata->localizedFieldIDs = $metadata->parent->localizedFieldIDs;
99
                        $metadata->notLocalizedFieldIDs = $metadata->parent->notLocalizedFieldIDs;
100
                    } else {
101
                        throw new ParentEntityNotFound($metadata->parentID);
102
                    }
103
                }
104
105
                // Get old AR collections of metadata
106
                $metadata->arSelect = \samson\activerecord\material::$_sql_select;
107
                $metadata->arAttributes = \samson\activerecord\material::$_attributes;
108
                $metadata->arMap = \samson\activerecord\material::$_map;
109
                $metadata->arFrom = \samson\activerecord\material::$_sql_from;
110
                $metadata->arGroup = \samson\activerecord\material::$_own_group;
111
                $metadata->arRelationAlias = \samson\activerecord\material::$_relation_alias;
112
                $metadata->arRelationType = \samson\activerecord\material::$_relation_type;
113
                $metadata->arRelations = \samson\activerecord\material::$_relations;
114
115
                // Add SamsonCMS material needed data
116
                $metadata->arSelect['this'] = ' STRAIGHT_JOIN ' . $metadata->arSelect['this'];
117
                $metadata->arFrom['this'] .= "\n" .
118
                    'LEFT JOIN ' . dbMySQLConnector::$prefix . 'materialfield as _mf
119
                ON ' . dbMySQLConnector::$prefix . 'material.MaterialID = _mf.MaterialID';
120
                $metadata->arGroup[] = dbMySQLConnector::$prefix . 'material.MaterialID';
121
122
                // Iterate entity fields
123
                foreach ($this->navigationFields($structureRow['StructureID']) as $fieldID => $fieldRow) {
124
                    // Get camelCase and transliterated field name
125
                    $fieldName = $this->fieldName($fieldRow['Name']);
126
127
                    // TODO: Set default for additional field storing type accordingly.
128
129
                    // Store field metadata
130
                    $metadata->realNames[$fieldRow['Name']] = $fieldName;
131
                    $metadata->allFieldIDs[$fieldID] = $fieldName;
132
                    $metadata->allFieldNames[$fieldName] = $fieldID;
133
                    $metadata->allFieldValueColumns[$fieldID] = Field::valueColumn($fieldRow[Field::F_TYPE]);
134
                    $metadata->allFieldTypes[$fieldID] = Field::phpType($fieldRow['Type']);
135
                    $metadata->allFieldCmsTypes[$fieldID] = (int)$fieldRow['Type'];
136
                    $metadata->fieldDescriptions[$fieldID] = $fieldRow['Description'] . ', ' . $fieldRow['Name'] . '#' . $fieldID;
137
                    $metadata->fieldRawDescriptions[$fieldID] = $fieldRow['Description'];
138
139
                    // Fill localization fields collections
140
                    if ($fieldRow[Field::F_LOCALIZED] == 1) {
141
                        $metadata->localizedFieldIDs[$fieldID] = $fieldName;
142
                    } else {
143
                        $metadata->notLocalizedFieldIDs[$fieldID] = $fieldName;
144
                    }
145
146
                    // Fill all fields which should display in list
147
                    if ($fieldRow['showInList'] == 1) {
148
                        $metadata->showFieldsInList[] = $fieldID;
149
                    }
150
151
                    // Save custom type
152
                    $metadata->customTypeFields[$fieldID] = $fieldRow['customTypeName'];
153
154
                    // Set old AR collections of metadata
155
                    $metadata->arAttributes[$fieldName] = $fieldName;
156
                    $metadata->arMap[$fieldName] = dbMySQLConnector::$prefix . 'material.' . $fieldName;
157
158
                    // Add additional field column to entity query
159
                    $equal = '((_mf.FieldID = ' . $fieldID . ')&&(_mf.locale ' . ($fieldRow['local'] ? ' = "@locale"' : 'IS NULL') . '))';
160
                    $metadata->arSelect['this'] .= "\n\t\t" . ',MAX(IF(' . $equal . ', _mf.`' . Field::valueColumn($fieldRow['Type']) . '`, NULL)) as `' . $fieldName . '`';
161
                }
162
163
                // Get id of child navigation
164
                foreach ($this->entityChildNavigation($structureRow['StructureID']) as $childNavigation) {
165
                    $metadata->childNavigationIDs[] = $childNavigation['StructureID'];
166
                }
167
168
                // Store metadata by entity identifier
169
                $this->metadata[$structureRow['StructureID']] = $metadata;
170
            }
171
        }
172
    }
173
174
    /** @return array Get collection of navigation objects */
175
    protected function entityNavigations($type = 0)
176
    {
177
        return $this->database->fetch('
178
        SELECT * FROM `structure`
179
        WHERE `Active` = "1" AND `Type` = "' . $type . '"
180
        ORDER BY `ParentID` ASC
181
        ');
182
    }
183
184
    /**
185
     * Get correct entity name.
186
     *
187
     * @param string $navigationName Original navigation entity name
188
     * @return string Correct PHP-supported entity name
189
     */
190
    protected function entityName($navigationName)
191
    {
192
        return ucfirst($this->getValidName($this->transliterated($navigationName)));
193
    }
194
195
    /**
196
     * Remove all wrong characters from entity name
197
     *
198
     * @param string $navigationName Original navigation entity name
199
     *
200
     * @return string Correct PHP-supported entity name
201
     */
202
    protected function getValidName($navigationName)
203
    {
204
        return preg_replace('/(^\d*)|([^\w\d_])/', '', $navigationName);
205
    }
206
207
    /**
208
     * Transliterate string to english.
209
     *
210
     * @param string $string Source string
211
     * @return string Transliterated string
212
     */
213
    protected function transliterated($string)
214
    {
215
        return str_replace(
216
            ' ',
217
            '',
218
            ucwords(iconv("UTF-8", "UTF-8//IGNORE", strtr($string, array(
219
                            "'" => "",
220
                            "`" => "",
221
                            "-" => " ",
222
                            "_" => " ",
223
                            "а" => "a", "А" => "a",
224
                            "б" => "b", "Б" => "b",
225
                            "в" => "v", "В" => "v",
226
                            "г" => "g", "Г" => "g",
227
                            "д" => "d", "Д" => "d",
228
                            "е" => "e", "Е" => "e",
229
                            "ж" => "zh", "Ж" => "zh",
230
                            "з" => "z", "З" => "z",
231
                            "и" => "i", "И" => "i",
232
                            "й" => "y", "Й" => "y",
233
                            "к" => "k", "К" => "k",
234
                            "л" => "l", "Л" => "l",
235
                            "м" => "m", "М" => "m",
236
                            "н" => "n", "Н" => "n",
237
                            "о" => "o", "О" => "o",
238
                            "п" => "p", "П" => "p",
239
                            "р" => "r", "Р" => "r",
240
                            "с" => "s", "С" => "s",
241
                            "т" => "t", "Т" => "t",
242
                            "у" => "u", "У" => "u",
243
                            "ф" => "f", "Ф" => "f",
244
                            "х" => "h", "Х" => "h",
245
                            "ц" => "c", "Ц" => "c",
246
                            "ч" => "ch", "Ч" => "ch",
247
                            "ш" => "sh", "Ш" => "sh",
248
                            "щ" => "sch", "Щ" => "sch",
249
                            "ъ" => "", "Ъ" => "",
250
                            "ы" => "y", "Ы" => "y",
251
                            "ь" => "", "Ь" => "",
252
                            "э" => "e", "Э" => "e",
253
                            "ю" => "yu", "Ю" => "yu",
254
                            "я" => "ya", "Я" => "ya",
255
                            "і" => "i", "І" => "i",
256
                            "ї" => "yi", "Ї" => "yi",
257
                            "є" => "e", "Є" => "e"
258
                        )
259
                    )
260
                )
261
            )
262
        );
263
    }
264
265
    /**
266
     * Find entity parent.
267
     *
268
     * @param $entityID
269
     *
270
     * @return null|int Parent entity identifier
271
     */
272
    public function entityParent($entityID)
273
    {
274
        $parentData = $this->database->fetch('
275
SELECT *
276
FROM structure_relation as sm
277
JOIN structure as s ON s.StructureID = sm.parent_id
278
WHERE sm.child_id = "' . $entityID . '"
279
AND s.StructureID != "' . $entityID . '"
280
');
281
282
        // Get parent entity identifier
283
        return count($parentData) ? $parentData[0]['StructureID'] : null;
284
    }
285
286
    /** @return array Collection of navigation additional fields */
287
    protected function navigationFields($navigationID)
288
    {
289
        $return = array();
290
        // TODO: Optimize queries make one single query with only needed data
291
        foreach ($this->database->fetch('SELECT * FROM `structurefield` WHERE `StructureID` = "' . $navigationID . '" AND `Active` = "1"') as $fieldStructureRow) {
292
            foreach ($this->database->fetch('SELECT * FROM `field` WHERE `FieldID` = "' . $fieldStructureRow['FieldID'] . '"') as $fieldRow) {
293
                $return[$fieldRow['FieldID']] = $fieldRow;
294
            }
295
        }
296
297
        return $return;
298
    }
299
300
    /**
301
     * Get correct field name.
302
     *
303
     * @param string $fieldName Original field name
304
     *
305
     * @return string Correct PHP-supported field name
306
     */
307
    protected function fieldName($fieldName)
308
    {
309
        return $fieldName = lcfirst($this->transliterated($fieldName));
0 ignored issues
show
Unused Code introduced by
$fieldName is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
310
    }
311
312
    /** @return array Get collection of child navigation objects */
313
    protected function entityChildNavigation($parentId)
314
    {
315
        return $this->database->fetch('
316
        SELECT * FROM `structure`
317
        WHERE `Active` = "1" AND `ParentID` = ' . $parentId . '
318
        ORDER BY `ParentID` ASC
319
        ');
320
    }
321
322
    /** @return string Entity state hash */
323
    public function entityHash()
324
    {
325
        // Получим информацию о всех таблицах из БД
326
        return md5(serialize($this->database->fetch(
327
            'SELECT `TABLES`.`TABLE_NAME` as `TABLE_NAME`
328
              FROM `information_schema`.`TABLES` as `TABLES`
329
              WHERE `TABLES`.`TABLE_SCHEMA`="' . $this->database->database() . '";'
330
        )));
331
    }
332
333
    /**
334
     * Make correct code formatting
335
     *
336
     * @param $code
337
     *
338
     * @return mixed
339
     */
340
    protected function formatTab($code)
341
    {
342
        // Replace indentation
343
        return str_replace("\t", '    ', $code);
344
    }
345
346
    /**
347
     * Get correct full entity name with name space.
348
     *
349
     * @param string $navigationName Original navigation entity name
350
     * @param string $namespace Namespace
351
     * @return string Correct PHP-supported entity name
352
     */
353
    protected function fullEntityName($navigationName, $namespace = __NAMESPACE__)
354
    {
355
        return '\\'.$namespace.'\\generated\\'.$this->entityName($navigationName);
356
    }
357
358
    /**
359
     * Get additional field type in form of Field constant name
360
     * by database additional field type identifier.
361
     *
362
     * @param integer $fieldType Additional field type identifier
363
     * @return string Additional field type constant
364
     */
365
    protected function additionalFieldType($fieldType)
366
    {
367
        return 'Field::'.$this->constantNameByValue($fieldType);
368
    }
369
370
    /**
371
     * Get class constant name by its value.
372
     *
373
     * @param string $value     Constant value
374
     * @param string $className Class name
375
     *
376
     * @return string Full constant name
377
     */
378
    protected function constantNameByValue($value, $className = Field::ENTITY)
379
    {
380
        // Get array where class constants are values and their values are keys
381
        $nameByValue = array_flip((new \ReflectionClass($className))->getConstants());
382
383
        // Try to find constant by its value
384
        if (null !== $nameByValue[$value]) {
385
            // Return constant name
386
            return $nameByValue[$value];
387
        }
388
389
        return '';
390
    }
391
}
392