Completed
Push — master ( da6ff3...4f4aae )
by Vitaly
02:49
created

Analyzer::transliterated()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 51
Code Lines 44

Duplication

Lines 51
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 51
loc 51
rs 9.4109
cc 1
eloc 44
nc 1
nop 1

How to fix   Long Method   

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
//[PHPCOMPRESSOR(remove,start)]
3
/**
4
 * Created by PhpStorm.
5
 * User: molodyko
6
 * Date: 13.02.2016
7
 * Time: 23:55
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
/**
17
 * Database analyzer and metadata creator.
18
 *
19
 * @package samsoncms\api\generator
20
 */
21
class Analyzer
22
{
23
    /** @var DatabaseInterface */
24
    protected $database;
25
26
    /** @var Metadata[] Collection of entities metadata */
27
    protected $metadata = array();
28
29
    /**
30
     * Generator constructor.
31
     * @param DatabaseInterface $database Database instance
32
     * @throws ParentEntityNotFound
33
     * @throws \samsoncms\api\exception\AdditionalFieldTypeNotFound
34
     */
35
    public function __construct(DatabaseInterface $database)
36
    {
37
        $this->database = $database;
38
    }
39
40
    /**
41
     * Analyze database and fill in metadata by type.
42
     *
43
     * @param int $type Metadata type
44
     * @param null|callable $filter Filtering callback
45
     *
46
     * @return Metadata[] Gathered metadata collection
47
     *
48
     * @throws ParentEntityNotFound
49
     * @throws \samsoncms\api\exception\AdditionalFieldTypeNotFound
50
     */
51 View Code Duplication
    public function analyze($type, $filter = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
52
    {
53
        $metadataCollection = [];
54
55
        // Iterate all structures, parents first
56
        foreach ($this->entityNavigations($type) as $structureRow) {
57
            // If filter is the function and filter return false then skip this structure
58
            if (is_callable($filter) && (false === $filter($structureRow))) {
59
                continue;
60
            }
61
62
            // Fill in entity metadata
63
            $metadata = new Metadata($type);
64
65
            // Get CapsCase and transliterated entity name
66
            $metadata->entity = $this->entityName($structureRow['Name']);
67
            $metadata->entityClassName = $this->fullEntityName($metadata->entity);
68
            $metadata->entityRealName = $structureRow['Name'];
69
            $metadata->entityID = $structureRow['StructureID'];
70
71
            // Try to find entity parent identifier for building future relations
72
            $metadata->parentID = $this->entityParent($structureRow['StructureID']);
73
            // Generate application from current entity
74
            $metadata->generateApplication = $structureRow['applicationGenerate'];
75
            // Show application from current entity
76
            $metadata->showApplication = $structureRow['applicationOutput'];
77
            // Icon for application from current entity
78
            $metadata->iconApplication = $structureRow['applicationIcon'];
79
            // Render application on main page
80
            $metadata->renderMainApplication = $structureRow['applicationRenderMain'];
81
82
            // TODO: Add multiple parent and fetching their data in a loop
83
84
            // Set pointer to parent entity
85
            if (null !== $metadata->parentID && $metadata->type !== Metadata::TYPE_TABLE) {
86
                if (array_key_exists($metadata->parentID, $this->metadata)) {
87
                    $metadata->parent = $this->metadata[$metadata->parentID];
88
                    // Add all parent metadata to current object
89
                    $metadata->defaultValues = $metadata->parent->defaultValues;
90
                    $metadata->realNames = $metadata->parent->realNames;
91
                    $metadata->allFieldIDs = $metadata->parent->allFieldIDs;
92
                    $metadata->allFieldNames = $metadata->parent->allFieldNames;
93
                    $metadata->allFieldValueColumns = $metadata->parent->allFieldValueColumns;
94
                    $metadata->allFieldTypes = $metadata->parent->allFieldTypes;
95
                    $metadata->fieldDescriptions = $metadata->parent->fieldDescriptions;
96
                    $metadata->localizedFieldIDs = $metadata->parent->localizedFieldIDs;
97
                    $metadata->notLocalizedFieldIDs = $metadata->parent->notLocalizedFieldIDs;
98
                } else {
99
                    throw new ParentEntityNotFound($metadata->parentID);
100
                }
101
            }
102
103
            // Get old AR collections of metadata
104
            $metadata->arSelect = \samson\activerecord\material::$_sql_select;
105
            $metadata->arAttributes = \samson\activerecord\material::$_attributes;
106
            $metadata->arMap = \samson\activerecord\material::$_map;
107
            $metadata->arFrom = \samson\activerecord\material::$_sql_from;
108
            $metadata->arGroup = \samson\activerecord\material::$_own_group;
109
            $metadata->arRelationAlias = \samson\activerecord\material::$_relation_alias;
110
            $metadata->arRelationType = \samson\activerecord\material::$_relation_type;
111
            $metadata->arRelations = \samson\activerecord\material::$_relations;
112
113
            // Add SamsonCMS material needed data
114
            $metadata->arSelect['this'] = ' STRAIGHT_JOIN ' . $metadata->arSelect['this'];
115
            $metadata->arFrom['this'] .= "\n" .
116
                'LEFT JOIN ' . dbMySQLConnector::$prefix . 'materialfield as _mf
117
            ON ' . dbMySQLConnector::$prefix . 'material.MaterialID = _mf.MaterialID';
118
            $metadata->arGroup[] = dbMySQLConnector::$prefix . 'material.MaterialID';
119
120
            // Iterate entity fields
121
            foreach ($this->navigationFields($structureRow['StructureID']) as $fieldID => $fieldRow) {
122
                // Get camelCase and transliterated field name
123
                $fieldName = $this->fieldName($fieldRow['Name']);
124
125
                // TODO: Set default for additional field storing type accordingly.
126
127
                // Store field metadata
128
                $metadata->realNames[$fieldRow['Name']] = $fieldName;
129
                $metadata->allFieldIDs[$fieldID] = $fieldName;
130
                $metadata->allFieldNames[$fieldName] = $fieldID;
131
                $metadata->allFieldValueColumns[$fieldID] = Field::valueColumn($fieldRow[Field::F_TYPE]);
132
                $metadata->allFieldTypes[$fieldID] = Field::phpType($fieldRow['Type']);
133
                $metadata->allFieldCmsTypes[$fieldID] = (int)$fieldRow['Type'];
134
                $metadata->fieldDescriptions[$fieldID] = $fieldRow['Description'] . ', ' . $fieldRow['Name'] . '#' . $fieldID;
135
                $metadata->fieldRawDescriptions[$fieldID] = $fieldRow['Description'];
136
137
                // Fill localization fields collections
138
                if ($fieldRow[Field::F_LOCALIZED] == 1) {
139
                    $metadata->localizedFieldIDs[$fieldID] = $fieldName;
140
                } else {
141
                    $metadata->notLocalizedFieldIDs[$fieldID] = $fieldName;
142
                }
143
144
                // Fill all fields which should display in list
145
                if ($fieldRow['showInList'] == 1) {
146
                    $metadata->showFieldsInList[] = $fieldID;
147
                }
148
149
                // Save custom type
150
                $metadata->customTypeFields[$fieldID] = $fieldRow['customTypeName'];
151
152
                // Set old AR collections of metadata
153
                $metadata->arAttributes[$fieldName] = $fieldName;
154
                $metadata->arMap[$fieldName] = dbMySQLConnector::$prefix . 'material.' . $fieldName;
155
156
                // Add additional field column to entity query
157
                $equal = '((_mf.FieldID = ' . $fieldID . ')&&(_mf.locale ' . ($fieldRow['local'] ? ' = "@locale"' : 'IS NULL') . '))';
158
                $metadata->arSelect['this'] .= "\n\t\t" . ',MAX(IF(' . $equal . ', _mf.`' . Field::valueColumn($fieldRow['Type']) . '`, NULL)) as `' . $fieldName . '`';
159
            }
160
161
            // Get id of child navigation
162
            foreach ($this->entityChildNavigation($structureRow['StructureID']) as $childNavigation) {
163
                $metadata->childNavigationIDs[] = $childNavigation['StructureID'];
164
            }
165
166
            // Store metadata by entity identifier
167
            $metadataCollection[$structureRow['StructureID']] = $metadata;
168
        }
169
170
171
        return $metadataCollection;
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 View Code Duplication
    protected function transliterated($string)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function entityParent($entityID)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    protected function navigationFields($navigationID)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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__)
0 ignored issues
show
Unused Code introduced by
The parameter $namespace is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
354
    {
355
        return '\samsoncms\api\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 View Code Duplication
    protected function constantNameByValue($value, $className = Field::ENTITY)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
//[PHPCOMPRESSOR(remove,end)]
393