Completed
Push — master ( 21f966...8b1dc6 )
by Vitaly
06:14
created

dbMySQLConnector::classes()   D

Complexity

Conditions 44
Paths 68

Size

Total Lines 338
Code Lines 142

Duplication

Lines 17
Ratio 5.03 %

Importance

Changes 9
Bugs 1 Features 3
Metric Value
c 9
b 1
f 3
dl 17
loc 338
rs 4.1818
cc 44
eloc 142
nc 68
nop 4

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
namespace samson\activerecord;
3
4
use samsonframework\orm\Database;
5
use samson\core\File;
6
7
//[PHPCOMPRESSOR(remove,start)]
8
use samsonphp\generator\Generator;
9
//[PHPCOMPRESSOR(remove,end)]
10
11
/**
12
 * Класс описывающий подключение к серверу MySQL
13
 * @author Vitaly Iegorov <[email protected]>
14
 * @author Nikita Kotenko <[email protected]>
15
 *
16
 */
17
class dbMySQLConnector extends Database
0 ignored issues
show
Coding Style introduced by
This class is not in CamelCase format.

Classes in PHP are usually named in CamelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. The whole name starts with a capital letter as well.

Thus the name database provider becomes DatabaseProvider.

Loading history...
18
{
19
    /** Table name prefix */
20
    public static $prefix = '';
21
22
    /**
23
     * Коллекция данных описывающих таблицы в БД
24
     * @var array
25
     */
26
    protected static $tables = array();
27
28
    /**
29
     * Коллекция данных описывающих связи таблиц в БД
30
     * @var array
31
     */
32
    protected static $relations = array();
33
34
    /**
35
     * Флаг подключения к БД
36
     * @var boolean
37
     */
38
    protected $connected = FALSE;
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected false, but found FALSE.
Loading history...
39
40
    /**
41
     * Экземпляр подключения к БД
42
     * @var mixed
43
     */
44
    public $link;
45
46
    /**
47
     * Имя БД
48
     * @var mixed
49
     */
50
    protected $base_name;
51
52
    /**
53
     * Коллекция созданных классов
54
     * @var array
55
     */
56
    private $created_classes = array();
57
58
    /**
59
     * Build database table column map
60
     * @param $table_name
61
     */
62
    private function __build_columns_map($table_name, array & $select = array(), array & $map = array(), array & $alias = array(), $alias_table_name = null)
0 ignored issues
show
Coding Style introduced by
Method name "dbMySQLConnector::__build_columns_map" is not in camel caps format
Loading history...
63
    {
64
        $alias_table_name = isset($alias_table_name) ? $alias_table_name : $table_name;
65
66
        // Iterate table column data
67
        foreach (self::$tables[$table_name] as $column_data) {
68
            // Get column name
69
            $column = $column_data['Field'];
70
71
            // Build alias name of related table column
72
            $column_alias = $alias_table_name . '_' . $column;
73
74
            // Build full name of related table column
75
            $column_full = $alias_table_name . '.' . $column;
76
77
            // Store column alias
78
            $alias[$column] = $column_alias;
79
80
            // Save sql "SELECT" statement
81
            $select[] = $column_full . ' as ' . $column_alias;
82
83
            // Save column map
84
            $map[$column_alias] = $column_full;
85
        }
86
    }
87
88
    //[PHPCOMPRESSOR(remove,start)]
89
    /**
90
     * Генератор описания классов и функций для работы с таблицами БД
91
     *
92
     * @return string Код на PHP для динамического создания классов и функций обращения к ним
93
     */
94
    public function classes($class_data, $class_name, $table_name = NULL, array $db_relations = NULL)
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected null, but found NULL.
Loading history...
95
    {
96
        // Сюда соберем код для генерации классов ActiveRecord
97
        $class_eval = '';
98
99
        $func_eval = '';
100
101
        // Префикс для функций обращения к классам БД
102
        if (!defined('__ARQ_Prefix__')) define('__ARQ_Prefix__', '_');
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
Coding Style introduced by
This constant is not in uppercase (expected '__ARQ_PREFIX__').
Loading history...
103
104
        // Сформируем имя функции для "вызова" класса
105
        $func_name = __ARQ_Prefix__ . $class_name;
106
107
        // If table name prefix is set
108
        if (isset(self::$prefix{0})) {
109
            // Remove prefix from class name
110
            $class_name = str_replace(self::$prefix, '', $class_name);
111
        }
112
113
        // Если такой класс не был описан вручную до этого
114
        if (!class_exists($class_name, false) && !in_array($class_name, $this->created_classes)) {
115
            // Создадим коллекцию созданных классов
116
            $this->created_classes[] = $class_name;
117
118
            // Определим реальную таблицу БД
119
            $table_name = isset($table_name) ? $table_name : self::$prefix . $class_name;
120
121
            // Флаг того что этот класс относительный
122
            $relational_class = $table_name != $class_name;
123
124
            // Добавим обдасть имен
125
            //$class_eval .= 'namespace Samson\ActiveRecord {';
126
127
            // Заполним комманду создания класса
128
            $class_eval .= "\n" . '/**';
129
130
            // Для относительных классов выведем специальный заголовок
131
            if ($relational_class) $class_eval .= "\n" . ' * ОТНОСИТЕЛЬНЫЙ Класс для работы с таблицей БД "' . $table_name . '" через "' . $class_name . '"';
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
132
            else $class_eval .= "\n" . ' * Класс для работы с таблицей БД "' . $table_name . '"';
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
133
            $class_eval .= "\n" . ' * @package SamsonActiveRecord';
134
            $class_eval .= "\n" . ' * @author Vitaly Iegorov <[email protected]>';
135
            $class_eval .= "\n" . ' * @author Nikita Kotenko <[email protected]>';
136
            $class_eval .= "\n" . ' * @version 2.0';
137
            $class_eval .= "\n" . ' */';
138
            $class_eval .= "\n" . 'class ' . $class_name . ' extends \samson\activerecord\dbRecord {';
139
140
            // Запишем реальное имя таблицы в БД
141
            $class_eval .= "\n\t" . '/** Настоящее имя таблицы в БД к которой привязан данный класс */';
142
            $class_eval .= "\n\t" . 'public static $_table_name = "' . $table_name . '";';
143
144
            $class_eval .= "\n\t" . '/** Внутрення группировка таблицы */';
145
            $class_eval .= "\n\t" . 'public static $_own_group = array();';
146
147
            // Коллекция уникальных переменных
148
            $unique_var = array();
149
150
            // Коллекция ключей переменных
151
            $index_var = array();
152
153
            // Коллекция типов переменных
154
            $var_types = array();
155
156
            // Primary field name
157
            $primary_field = '';
158
159
            // Переберем данные описывающие структуру таблицы, её колонки
160
            foreach ($class_data as & $column) {
161
                // Если это главный ключ таблицы запишем его в специальную переменную
162
                if ($column['Key'] == 'PRI' && $primary_field == '') {
163
                    $class_eval .= "\n\t" . '/** Название ключевого поля таблицы */';
164
                    $class_eval .= "\n\t" . 'public static $_primary = "' . $column['Field'] . '";';
165
                    $primary_field = $column['Field'];
166
                } // Уникальные поля
167
                else if ($column['Key'] == 'UNI') $unique_var[] = $column['Field'];
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
168
                // Ключевые поля
169
                else if ($column['Key'] == 'MUL') $index_var[] = $column['Field'];
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
170
171
                // Запишем тип переменной
172
                $var_types[$column['Field']] = $column['Type'];
173
            }
174
175
            // Коллекция определенных переменных
176
            $defined_vars = array();
177
178
            // Комманда на выборку полей
179
            $sql_select = array();
180
181
            // Счетчик не английских переменных
182
            $utf8_fields_count = 1;
183
184
            // Переменные
185
            $vars_eval = '';
186
187
            // Переберем данные описывающие структуру таблицы, её колонки
188
            foreach ($class_data as & $column) {
189
                // Получим реальное имя колонки в таблице БД
190
                $field = (isset($column['Column'])) ? $column['Column'] : $column['Field'];
191
192
                // Получим виртуальное имя колонки если оно задано
193
                $f_name = $column['Field'];
194
195
                // Если переменную с такими именем мы еще не создавали
196
                if (!in_array($field, $defined_vars)) {
197
                    // Сформируем SQL комманду для "правильной" выборки данных из БД
198
                    $sql_select[] = $table_name . '.' . $field;
199
200
                    // Если это русская переменная или содержащая не правильное имя переменной то мы называем её по своему
201
                    if (preg_match('/([^a-zA-Z_\s0-9]|[\-])+/ui', $field)) {
202
                        // Сгенерируем имя поля
203
                        $f_name = 'Field_' . ($utf8_fields_count++);
204
205
                        // Создадим переменную класса
206
                        $vars_eval .= "\n\t" . '/** ' . $field . ' */';
207
                    } // Если это не настоящие имя поля - укажем настоящее
208
                    else if ($table_name != $class_name) $vars_eval .= "\n\t" . '/** ' . $column['Field'] . ' */';
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
209
210
                    // Создадим саму переменную класса
211
                    $value = $column['Null'] !== 'NO' ? ' = null' : ' = ""';
212
213
                    // TODO: refactor
214
                    switch (gettype($column['Default'])) {
215
                        case 'string':
216
                            switch ($column['Default']) {
217
                                case 'CURRENT_TIMESTAMP': $value = ''; break;
0 ignored issues
show
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
218
                                case '': $value = ' = ""'; break;
0 ignored issues
show
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
219 View Code Duplication
                                default: $value = $column['Default'] !== null ? ' = "'.$column['Default'].'"' : $value;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
220
                            }
221
                            break;
222 View Code Duplication
                        case 'number': $value = $column['Default'] !== null ? ' = '.$column['Default'] : $value;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
223
                    }
224
225
                    $vars_eval .= "\n\t" . 'public $' . $f_name . $value . ';';
226
227
                    // Добавим имя переменной
228
                    $defined_vars[$f_name] = $field;
229
                } // Создадим специальную переменную с пометкой о дубликате и ссылкой на него
230
                else e('Ошибка: В таблице ##, найден дубликат колонки ##-##', D_SAMSON_ACTIVERECORD_DEBUG, array($class_name, $field, $f_name));
0 ignored issues
show
Deprecated Code introduced by
The function e() has been deprecated with message: Use custom exceptions

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
231
            }
232
233
            // Пропишем поля записи
234
            $class_eval .= "\n\t" . '/** Коллекция полей класса и имен колонок в таблице БД */';
235
            $class_eval .= "\n\t" . 'public static $_attributes = array(';
236 View Code Duplication
            foreach ($defined_vars as $name => $db_name) $class_eval .= "\n\t\t" . '"' . $name . '"=>"' . $db_name . '",';
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
237
            $class_eval .= "\n\t" . ');';
238
239
            // Пропишем колонки таблицы
240
            $class_eval .= "\n\t" . '/** Коллекция РЕАЛЬНЫХ имен колонок в таблице БД */';
241
            $class_eval .= "\n\t" . 'public static $_table_attributes = array(';
242 View Code Duplication
            foreach ($defined_vars as $name => $db_name) $class_eval .= "\n\t\t" . '"' . $name . '"=>"' . $db_name . '",';
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
243
            $class_eval .= "\n\t" . ');';
244
245
            // Сформируем SQL комманду для "правильной" выборки данных из БД
246
            $class_eval .= "\n\t" . '/** Коллекция параметров SQL комманды для запроса к таблице */';
247
            $class_eval .= "\n\t" . 'public static $_sql_from = array(';
248
            $class_eval .= "\n\t\t" . '"this" => "`' . $table_name . '`",';
249
250
            // Выборка полей для запроса
251
            $select_eval = "\n\t" . '/** Часть SQL-комманды на выборку полей класса для запроса к таблице БД */';
252
            $select_eval .= "\n\t" . 'public static $_sql_select = array(';
253
            $select_eval .= "\n\t\t" . '"this" => "' . implode(',' . "\n", $sql_select) . '",';
254
255
            // Коллекция связей имен полей связанных таблиц
256
            $relation_eval = "\n\t" . '/** Коллекция имен полей связанных таблиц для запроса к таблице БД */';
257
            $relation_eval .= "\n\t" . 'public static $_relations = array(';
258
259
            // Коллекция типов связей имен полей связанных таблиц
260
            $reltype_eval = "\n\t" . '/** Коллекция типов связанных таблиц для запроса к таблице БД */';
261
            $reltype_eval .= "\n\t" . 'public static $_relation_type = array(';
262
263
            // Коллекция типов связей имен полей связанных таблиц
264
            $relalias_eval = "\n\t" . '/** Коллекция алиасов связанных таблиц для запроса к таблице БД */';
265
            $relalias_eval .= "\n\t" . 'public static $_relation_alias = array(';
266
267
            // Коллекция имен полей для мапинга
268
            $map_vars = array();
269
            foreach ($defined_vars as $name => $db_name) $map_vars[$name] = $table_name . '.' . $db_name;
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
270
271
            // Permanent relation is specified
272
            if (isset($db_relations[$table_name])) {
273
                // Iterate table permanent relations
274
                foreach ($db_relations[$table_name] as $r_table => $i) {
275
                    // Check child table
276
                    if (!isset(self::$tables[$i->child])) {
277
                        e('Cannot create relation beetween ## and ## - Table ## does not exists', E_SAMSON_ACTIVERECORD_ERROR, array($i->parent, $i->child));
0 ignored issues
show
Deprecated Code introduced by
The function e() has been deprecated with message: Use custom exceptions

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
278
                        continue;
279
                    }
280
281
                    // Parent table name
282
                    $r_table_name = isset($i->alias{0}) ? $i->alias : $i->child;
283
284
                    // If relation alias is defined
285
                    if (isset($i->alias{0})) $class_eval .= "\n\t\t" . '"' . $i->alias . '"=>" LEFT JOIN `' . $i->child . '` AS ' . $i->alias;
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
286
                    else                     $class_eval .= "\n\t\t" . '"' . $i->child . '"=>" LEFT JOIN `' . $i->child . '`';
0 ignored issues
show
Coding Style introduced by
Expected 1 space after ELSE keyword; 21 found
Loading history...
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
287
288
                    // Parent table name
289
                    $ptable = $i->parent;
290
291
                    // If parent field not specified - use parent table primary field
292 View Code Duplication
                    if (!isset($i->parent_field)) $pfield = $primary_field;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
293
                    // Correctly determine parent field name
294
                    else {
295
                        // Define if parent field name has table name in it
296
                        $tableinpf = strpos($i->parent_field, '.');
297
298
                        // Get parent table field name
299
                        $pfield = $tableinpf !== false ? substr($i->parent_field, $tableinpf + 1) : $i->parent_field;
300
301
                        // Parent table field
302
                        $ptable = $tableinpf !== false ? substr($i->parent_field, 0, $tableinpf) : $i->parent;
303
                    }
304
305
                    // If no "." symbol in parent field name append parent table name
306
                    $pf = '`' . $ptable . '`.`' . $pfield . '`';
307
308
                    // If child field not specified
309
                    if (!isset($i->child_field{0})) $cf = '`' . $i->child . '`.`' . $pfield . '`';
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
310
                    // If no "." symbol in child field name append child table name
311 View Code Duplication
                    else $cf = strpos($i->child_field, '.') === false ? '`' . (isset($i->alias{0}) ? $i->alias : $i->child) . '`.' . $i->child_field : $i->child_field;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
312
313
                    // And joining field
314
                    $class_eval .= ' ON ' . $pf . ' = ' . $cf . '",';
315
316
                    // Установим тип связи
317
                    $reltype_eval .= "\n\t\t" . '"' . $r_table_name . '"=>' . $i->type . ',';
318
319
                    $relation_eval .= "\n\t\t" . '"' . $r_table_name . '" => array(';
320
321
                    $relalias_eval .= "\n\t\t" . '"' . $r_table_name . '" => "' . $i->child . '",';
322
323
                    // Array for select block
324
                    $select_eval_array = array();
325
326
                    // Переберем поля связанной таблицы
327
                    foreach (self::$tables[$i->child] as $column_data) {
328
                        // Имя поля связанной таблицы
329
                        $r_field_name = $r_table_name . '_' . $column_data['Field'];
330
331
                        // Имя поля связанной таблицы
332
                        $r_real_name = $r_table_name . '.' . $column_data['Field'];
333
334
                        // Добавим связь в мап
335
                        $map_vars[$r_field_name] = $r_real_name;
336
337
                        // Сформируем значение массива
338
                        $relation_eval .= "\n\t\t\t" . '"' . $column_data['Field'] . '" => "' . $r_field_name . '",';
339
340
                        // Часть SQL-комманды
341
                        $select_eval_array[] = "\n\t\t\t" . $r_real_name . ' as ' . $r_field_name . '';
342
                    }
343
344
                    // Сформируем массив имен полей для связи
345
                    $select_eval .= "\n\t\t" . '"' . $r_table_name . '" => "' . implode(',', $select_eval_array) . '",';
346
347
                    // Закроем коллекцию связей
348
                    $relation_eval .= "\n\t\t" . '),';
349
                }
350
            }
351
352
            // Закроем
353
            $class_eval .= "\n\t" . ');';
354
355
            // Закроем
356
            $select_eval .= "\n\t" . ');';
357
358
            // Закроем
359
            $relation_eval .= "\n\t" . ');';
360
361
            $reltype_eval .= "\n\t" . ');';
362
363
            $relalias_eval .= "\n\t" . ');';
364
365
            // Слепим все вместе
366
            $class_eval .= $relation_eval . $relalias_eval . $reltype_eval . $select_eval;
367
368
            // Пропишем типы полей записи
369
            $class_eval .= "\n\t" . '/** Коллекция типов полей записи в таблице БД */';
370
            $class_eval .= "\n\t" . 'public static $_types = array(';
371
            foreach ($var_types as $name => $type) $class_eval .= "\n\t\t" . '"' . $name . '"=>"' . $type . '",';
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
372
            $class_eval .= "\n\t" . ');';
373
374
            // Пропишем ключевые поля записи
375
            $class_eval .= "\n\t" . '/** Коллекция имен ключей записи в таблице БД */';
376
            $class_eval .= "\n\t" . 'public static $_indeces = array(';
377
            foreach ($index_var as $name) $class_eval .= "\n\t\t" . '"' . $name . '",';
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
378
            $class_eval .= "\n\t" . ');';
379
380
            // Пропишем уникальные поля записи
381
            $class_eval .= "\n\t" . '/** Коллекция имен уникальных полей записи в таблице БД */';
382
            $class_eval .= "\n\t" . 'public static $_unique = array(';
383
            foreach ($unique_var as $name) $class_eval .= "\n\t\t" . '"' . $name . '",';
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
384
            $class_eval .= "\n\t" . ');';
385
386
            // Пропишем поля записи
387
            $class_eval .= "\n\t" . '/** Коллекция связей между именами полей класса и именами колонок в таблице БД */';
388
            $class_eval .= "\n\t" . 'public static $_map = array(';
389
            foreach ($map_vars as $name => $db_name) $class_eval .= "\n\t\t" . '"' . $name . '"=>"' . $db_name . '",';
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
390
            $class_eval .= "\n\t" . ');';
391
392
            // Запишем имена всех полей класса как метаданные
393
            $class_eval .= $vars_eval;
394
395
            // Заполним конструктор класса для установки схемы
396
            //$class_eval .= "\n\t".'/** Создать экземпляр класса '.$class_name.' */';
397
            //$class_eval .= "\n\t".'public function __construct( $id = NULL, $class_name = NULL ){ if( !isset($class_name)) $class_name = "'.$class_name.'" ; parent::__construct( $id, $class_name ); }';
398
399
            // Геттер для получения переменных класса по другому названию
400
            //$class_eval .= "\n\t".'/** Попытаться получить переменную класса '.$class_name.' */';
401
            //$class_eval .= "\n\t".'public function __get( $name ){ if( isset( self::$_attributes[ $name ]) ){ $f = self::$_attributes[ $name ]; return $this->$f; } else return parent::__get( $name );}';
402
403
            // Сеттер для установки переменных класса по другому названию
404
            //$class_eval .= "\n\t".'/** Попытаться получить переменную класса '.$class_name.' */';
405
            //$class_eval .= "\n\t".'public function __set( $name, $value ){ if( isset( self::$_attributes[ $name ]) ){ $f = self::$_attributes[ $name ]; return $this->$f = $value; } else return parent::__set( $name, $value );}';
406
407
            // Закончим описание класса
408
            $class_eval .= "\n}";
409
410
            // Создадим подгруппу для хранения экземпляров данного класса
411
            $class_eval .= "\n" . 'dbRecord::$instances["' . $class_name . '"] = array();';
412
413
            // Закончим описание области имен
414
            //$class_eval .= "\n}";
415
416
            // Если мы еще не объявляли функцию для данного класса
417
            if (!function_exists($func_name)) {
418
                // Создадим специальную функцию для динамических запросов к АктивРекорду
419
                //$class_eval .= "\n".'namespace {';
420
                $func_eval .= "\n" . '/** ' . "\n" . ' * @return dbQuery ' . "\n" . ' */';
421
                $func_eval .= "\n" . 'function ' . $func_name . '(){ return new \samson\activerecord\dbQuery("' . $class_name . '"); }' . "\n" . "\n";
422
                //$class_eval .= "\n}";
423
            }
424
        }
425
426
        // Установим переменную для оптимизации
427
        //$this->classes .= $class_eval;
428
429
        // Вернем строку с описанием классов для таблиц БД
430
        return array($class_eval, $func_eval);
431
    }
432
433
    /** Generate database table relations */
434
    public function relations($cachePath = '')
435
    {
436
        // Generate unique file name
437
        $relations_file = $cachePath . '/relations/' . md5(serialize(TableRelation::$instances)) . '.php';
438
439
        // Relations file does not exists - create it
440
        if (!file_exists($relations_file)) {
441
            // Get directory path
442
            $dir = pathname($relations_file);
443
444
            // Create folder
445
            if (!file_exists($dir)) mkdir($dir, 0777, TRUE);
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected true, but found TRUE.
Loading history...
446
            //  Clear folder
447
            else File::clear($dir);
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
448
449
            // Processed permanent table relations
450
            $db_relations = array();
451
452
            // Iterate permanent relations
453
            foreach (TableRelation::$instances as $row) {
454
                // Create relations data for specific table
455
                if (!isset($db_relations[$row->parent])) $db_relations[$row->parent] = array();
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
456
457
                // Define child relation table name
458
                $child_relation = !isset($row->alias{0}) ? $row->child : $row->alias;
459
460
                $row->parent = self::$prefix . $row->parent;
461
                $row->child = self::$prefix . $row->child;
462
463
                // Save relation data
464
                $db_relations[$row->parent][$child_relation] = $row;
465
            }
466
467
            // TODO: Pass generator and others via constructor DI
468
469
            // Create code generator instance
470
            $g = new Generator('samson\activerecord');
471
            $g->multicomment(array('Static ActiveRecord generated table relations'));
472
473
            // Array of "FROM" sql statements for related tables
474
            $sql_from = array();
475
476
            // Array of "SELECT" sql statements for related tables
477
            $sql_select = array();
478
479
            // Array related tables columns names and aliases
480
            $relations = array();
481
482
            // Array of table aliases
483
            $aliases = array();
484
485
            // Array of table relation type
486
            $types = array();
487
488
            // Array of columns map
489
            $map = array();
490
491
            // Iterate grouped relations
492
            foreach ($db_relations as $parent => $relation) {
493
                // Check if parent class in relation exists
494
                if (class_exists(__NAMESPACE__ . '\\' . $parent, false)) {
495
                    // Iterate table permanent relations
496
                    foreach ($relation as $r_table => $i) {
497
                        // Array of "SELECT" sql statements for this related tables
498
                        $_sql_select = array();
499
500
                        // Array related tables columns names and aliases for this related tables
501
                        $_relations = array();
502
503
                        // Parent table name
504
                        $r_table_name = isset($i->alias{0}) ? $i->alias : $i->child;
505
506
                        // Define start of join sql statement
507
                        $_sql_from = 'LEFT JOIN `' . $i->child . '`';
508
                        // If relation alias is defined
509
                        if (isset($i->alias{0})) $_sql_from = 'LEFT JOIN `' . $i->child . '` AS ' . $i->alias;
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
510
511
                        // Parent table name
512
                        $ptable = $i->parent;
513
514
                        // If parent field not specified - use parent table primary field
515 View Code Duplication
                        if (!isset($i->parent_field)) $pfield = $primary_field;
0 ignored issues
show
Bug introduced by
The variable $primary_field does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
516
                        // Correctly determine parent field name
517
                        else {
518
                            // Define if parent field name has table name in it
519
                            $tableinpf = strpos($i->parent_field, '.');
520
521
                            // Get parent table field name
522
                            $pfield = $tableinpf !== false ? substr($i->parent_field, $tableinpf + 1) : $i->parent_field;
523
524
                            // Parent table field
525
                            $ptable = $tableinpf !== false ? dbMySQLConnector::$prefix . substr($i->parent_field, 0, $tableinpf) : $i->parent;
526
                        }
527
528
                        // If no "." symbol in parent field name append parent table name
529
                        $pf = '`' . $ptable . '`.`' . $pfield . '`';
530
531
                        // If child field not specified
532
                        if (!isset($i->child_field{0})) $cf = '`' . (isset($i->alias{0}) ? $i->alias : $i->child) . '`.`' . $pfield . '`';
533
                        // If no "." symbol in child field name append child table name
534 View Code Duplication
                        else $cf = strpos($i->child_field, '.') === false ? '`' . (isset($i->alias{0}) ? $i->alias : $i->child) . '`.' . $i->child_field : $i->child_field;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
535
536
                        // Build columns metadata
537
                        $this->__build_columns_map($i->child, $_sql_select, $map, $_relations, $i->alias);
538
539
                        // Array of "SELECT" sql statements to all related tables
540
                        $sql_select[$r_table_name] = implode(',', $_sql_select);
541
                        $relations[$r_table_name] = $_relations;
542
                        $sql_from[$r_table_name] = $_sql_from . ' ON ' . $pf . ' = ' . $cf;
543
                        $aliases[$r_table_name] = $i->child;
544
                        $types[$r_table_name] = $i->type;
545
                    }
546
547
                    // Remove prefix
548
                    $class_name = str_replace(self::$prefix, '', $parent);
549
550
                    // Generate code for this table
551
                    $g->newline()
552
                        ->comment('Relation data for table "' . $parent . '"')
553
                        ->defarraymerge($class_name . '::$_sql_from', $sql_from)
554
                        ->defarraymerge($class_name . '::$_sql_select', $sql_select)
555
                        ->defarraymerge($class_name . '::$_map', $map)
556
                        ->defvar($class_name . '::$_relation_alias', $aliases)
557
                        ->defvar($class_name . '::$_relation_type', $types)
558
                        ->defvar($class_name . '::$_relations', $relations);
559
                }
560
            }
561
562
            // Save file to wwwrot
563
            $g->write($relations_file);
564
        } // Or just include file
565
566
        include($relations_file);
567
    }
568
569
    /**
570
     * Generate ORM classes
571
     * @param string $force Force class generation
572
     */
573
    public function generate($force = false, $cachePath = '')
574
    {
575
        // Processed permanent table relations
576
        $db_relations = array();
577
578
        // Get all virtual tables structure data
579
        $db_mapper = array();
580
581
        // Получим информацию о всех таблицах из БД
582
        $rows = $this->fetch(
583
            'SELECT
584
              `TABLES`.`TABLE_NAME` as `TABLE_NAME`,
585
              `COLUMNS`.`COLUMN_NAME` as `Field`,
586
              `COLUMNS`.`DATA_TYPE` as `Type`,
587
              `COLUMNS`.`IS_NULLABLE` as `Null`,
588
              `COLUMNS`.`COLUMN_KEY` as `Key`,
589
              `COLUMNS`.`COLUMN_DEFAULT` as `Default`,
590
              `COLUMNS`.`EXTRA` as `Extra`
591
              FROM `information_schema`.`TABLES` as `TABLES`
592
              LEFT JOIN `information_schema`.`COLUMNS` as `COLUMNS`
593
              ON `TABLES`.`TABLE_NAME`=`COLUMNS`.`TABLE_NAME`
594
              WHERE `TABLES`.`TABLE_SCHEMA`="' . $this->database . '" AND `COLUMNS`.`TABLE_SCHEMA`="' . $this->database . '"'
595
        );
596
597
598
        foreach ($rows as $row) {
599
            // Получим имя таблицы
600
            $table_name = $row['TABLE_NAME'];
601
602
            // Создадим коллекцию для описания структуры таблицы
603
            if (!isset(self::$tables[$table_name])) {
604
                self::$tables[$table_name] = array();
605
            }
606
607
            // Удалим имя таблицы из масива
608
            unset($row['TABLE_NAME']);
609
610
            // Запишем описание каждой колонки таблиц в специальный массив
611
            self::$tables[$table_name][] = $row;
612
        }
613
614
        $bstr = md5(serialize(self::$tables));
615
616
        //TODO: check if virtual table has not changed and add it to hash
617
618
        // Создадим имя файла содержащего пути к модулям
619
        $md5_file = $cachePath . 'metadata/classes_' . $bstr . '.php';
620
        $md5_file_func = $cachePath . 'metadata/func_' . $bstr . '.php';
621
622
        // Если еще не создан отпечаток базы данных - создадим его
623
        if (!file_exists($md5_file) || $force) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $force of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
624
            // Get directory path
625
            $dir = pathname($md5_file);
626
627
            // Create folder
628
            if (!file_exists($dir)) {
629
                mkdir($dir, 0777, true);
630
            } //  Clear folder
631
            else {
632
                File::clear($dir);
633
            }
634
635
            // Удалим все файлы с расширением map
636
            //foreach ( \samson\core\File::dir( getcwd(), 'dbs' ) as $file ) unlink( $file );
637
638
            // Если еще не создан отпечаток базы данных - создадим его
639
640
            // Сохраним классы БД
641
            $db_classes = 'namespace samson\activerecord;';
642
643
            $db_func = '';
644
645
            // Создадим классы
646
            foreach ($db_mapper as $table_name => $table_data) {
647
                $file_full = $this->classes($table_data, $table_name, $virtualTable->table, $db_relations);
0 ignored issues
show
Bug introduced by
The variable $virtualTable does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
648
                $db_classes .= $file_full[0];
649
                $db_func .= $file_full[1];
650
            }
651
652
            // Создадим классы
653
            foreach (self::$tables as $table_name => $table_data) {
654
                $file_full = $this->classes(self::$tables[$table_name], $table_name, $table_name, $db_relations);
655
                $db_classes .= $file_full[0];
656
                $db_func .= $file_full[1];
657
            }
658
659
            // Запишем файл для IDE в корень проекта
660
            file_put_contents($md5_file, '<?php ' . $db_classes . '?>');
661
            file_put_contents($md5_file_func, '<?php ' . $db_func . '?>');
662
663
            // Подключим наш ХУК для АктивРекорда!!!!!
664
            eval($db_classes);
665
            eval($db_func);
666
        } // Иначе просто его подключим
667
        else {
668
            include($md5_file);
669
            include($md5_file_func);
670
        }
671
672
        //elapsed('end');
673
    }
674
    //[PHPCOMPRESSOR(remove,end)]
675
}
676