Completed
Push — master ( 53b99e...05e097 )
by Basil
03:48
created

BaseCrudController::generateClassName()   B

Complexity

Conditions 10
Paths 28

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 7.6666
c 0
b 0
f 0
cc 10
nc 28
nop 2

How to fix   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
namespace luya\console\commands;
4
5
use Yii;
6
use luya\console\Command;
7
use yii\db\Schema;
8
use yii\helpers\Inflector;
9
use yii\base\NotSupportedException;
10
11
/**
12
 * Base Crud Controller
13
 *
14
 * As we can not ensure to access the gii model generate class we have to copy the base of the class, check the see section.
15
 *
16
 * @see https://github.com/yiisoft/yii2-gii/blob/master/generators/model/Generator.php
17
 * @author Basil Suter <[email protected]>
18
 * @since 1.0.0
19
 */
20
abstract class BaseCrudController extends Command
21
{
22
    /**
23
     * @var boolean Whether to use schem name or not
24
     */
25
    public $useSchemaName = true;
26
    
27
    /**
28
     * @var array A list of class names.
29
     */
30
    protected $classNames;
31
    
32
    /**
33
     * @var array A list of table names.
34
     */
35
    protected $tableNames;
36
    
37
    /**
38
     * @var string The name of the table.
39
     */
40
    public $tableName;
41
    
42
    /**
43
     * @var boolean Whether to generate labels from comments or not.
44
     */
45
    public $generateLabelsFromComments = false;
46
    
47
    /**
48
     * Get the sql tables from the current database connection
49
     *
50
     * @return array An array with all sql tables.
51
     */
52
    public function getSqlTablesArray()
53
    {
54
        $names = Yii::$app->db->schema->tableNames;
55
    
56
        return array_combine($names, $names);
57
    }
58
    /**
59
     * Generates validation rules for the specified table.
60
     * @param \yii\db\TableSchema $table the table schema
61
     * @return array the generated validation rules
62
     */
63
    public function generateRules($table)
64
    {
65
        $types = [];
66
        $lengths = [];
67
        foreach ($table->columns as $column) {
68
            if ($column->autoIncrement) {
69
                continue;
70
            }
71
            if (!$column->allowNull && $column->defaultValue === null) {
72
                $types['required'][] = $column->name;
73
            }
74
            switch ($column->type) {
75
                case Schema::TYPE_SMALLINT:
76
                case Schema::TYPE_INTEGER:
77
                case Schema::TYPE_BIGINT:
78
                    $types['integer'][] = $column->name;
79
                    break;
80
                case Schema::TYPE_BOOLEAN:
81
                    $types['boolean'][] = $column->name;
82
                    break;
83
                case Schema::TYPE_FLOAT:
84
                case 'double': // Schema::TYPE_DOUBLE, which is available since Yii 2.0.3
85
                case Schema::TYPE_DECIMAL:
86
                case Schema::TYPE_MONEY:
87
                    $types['number'][] = $column->name;
88
                    break;
89
                case Schema::TYPE_DATE:
90
                case Schema::TYPE_TIME:
91
                case Schema::TYPE_DATETIME:
92
                case Schema::TYPE_TIMESTAMP:
93
                    $types['safe'][] = $column->name;
94
                    break;
95
                default: // strings
96
                    if ($column->size > 0) {
97
                        $lengths[$column->size][] = $column->name;
98
                    } else {
99
                        $types['string'][] = $column->name;
100
                    }
101
            }
102
        }
103
        $rules = [];
104
        foreach ($types as $type => $columns) {
105
            $rules[] = "[['" . implode("', '", $columns) . "'], '$type']";
106
        }
107
        foreach ($lengths as $length => $columns) {
108
            $rules[] = "[['" . implode("', '", $columns) . "'], 'string', 'max' => $length]";
109
        }
110
        $db = $this->getDbConnection();
111
        // Unique indexes rules
112
        try {
113
            $uniqueIndexes = $db->getSchema()->findUniqueIndexes($table);
114
            foreach ($uniqueIndexes as $uniqueColumns) {
115
                // Avoid validating auto incremental columns
116
                if (!$this->isColumnAutoIncremental($table, $uniqueColumns)) {
117
                    $attributesCount = count($uniqueColumns);
118
                    if ($attributesCount === 1) {
119
                        $rules[] = "[['" . $uniqueColumns[0] . "'], 'unique']";
120
                    } elseif ($attributesCount > 1) {
121
                        $labels = array_intersect_key($this->generateLabels($table), array_flip($uniqueColumns));
122
                        $lastLabel = array_pop($labels);
123
                        $columnsList = implode("', '", $uniqueColumns);
124
                        $rules[] = "[['$columnsList'], 'unique', 'targetAttribute' => ['$columnsList'], 'message' => 'The combination of " . implode(', ', $labels) . " and $lastLabel has already been taken.']";
125
                    }
126
                }
127
            }
128
        } catch (NotSupportedException $e) {
129
            // doesn't support unique indexes information...do nothing
130
        }
131
        // Exist rules for foreign keys
132
        foreach ($table->foreignKeys as $refs) {
133
            $refTable = $refs[0];
134
            $refTableSchema = $db->getTableSchema($refTable);
135
            if ($refTableSchema === null) {
136
                // Foreign key could point to non-existing table: https://github.com/yiisoft/yii2-gii/issues/34
137
                continue;
138
            }
139
            $refClassName = $this->generateClassName($refTable);
140
            unset($refs[0]);
141
            $attributes = implode("', '", array_keys($refs));
142
            $targetAttributes = [];
143
            foreach ($refs as $key => $value) {
144
                $targetAttributes[] = "'$key' => '$value'";
145
            }
146
            $targetAttributes = implode(', ', $targetAttributes);
147
            $rules[] = "[['$attributes'], 'exist', 'skipOnError' => true, 'targetClass' => $refClassName::className(), 'targetAttribute' => [$targetAttributes]]";
148
        }
149
        return $rules;
150
    }
151
    
152
    /**
153
     * Generates a class name from the specified table name.
154
     * @param string $tableName the table name (which may contain schema prefix)
155
     * @param boolean $useSchemaName should schema name be included in the class name, if present
156
     * @return string the generated class name
157
     */
158
    protected function generateClassName($tableName, $useSchemaName = null)
159
    {
160
        if (isset($this->classNames[$tableName])) {
161
            return $this->classNames[$tableName];
162
        }
163
        $schemaName = '';
164
        $fullTableName = $tableName;
165
        if (($pos = strrpos($tableName, '.')) !== false) {
166
            if (($useSchemaName === null && $this->useSchemaName) || $useSchemaName) {
167
                $schemaName = substr($tableName, 0, $pos) . '_';
168
            }
169
            $tableName = substr($tableName, $pos + 1);
170
        }
171
        $db = $this->getDbConnection();
172
        $patterns = [];
173
        $patterns[] = "/^{$db->tablePrefix}(.*?)$/";
174
        $patterns[] = "/^(.*?){$db->tablePrefix}$/";
175
        if (strpos($this->tableName, '*') !== false) {
176
            $pattern = $this->tableName;
177
            if (($pos = strrpos($pattern, '.')) !== false) {
178
                $pattern = substr($pattern, $pos + 1);
179
            }
180
            $patterns[] = '/^' . str_replace('*', '(\w+)', $pattern) . '$/';
181
        }
182
        $className = $tableName;
183
        foreach ($patterns as $pattern) {
184
            if (preg_match($pattern, $tableName, $matches)) {
185
                $className = $matches[1];
186
                break;
187
            }
188
        }
189
        return $this->classNames[$fullTableName] = Inflector::id2camel($schemaName.$className, '_');
190
    }
191
    
192
    /**
193
     * Checks if any of the specified columns is auto incremental.
194
     * @param \yii\db\TableSchema $table the table schema
195
     * @param array $columns columns to check for autoIncrement property
196
     * @return boolean whether any of the specified columns is auto incremental.
197
     */
198
    protected function isColumnAutoIncremental($table, $columns)
199
    {
200
        foreach ($columns as $column) {
201
            if (isset($table->columns[$column]) && $table->columns[$column]->autoIncrement) {
202
                return true;
203
            }
204
        }
205
        return false;
206
    }
207
    
208
    /**
209
     * Generates the attribute labels for the specified table.
210
     * @param \yii\db\TableSchema $table the table schema
211
     * @return array the generated attribute labels (name => label)
212
     */
213
    public function generateLabels($table)
214
    {
215
        $labels = [];
216
        foreach ($table->columns as $column) {
217
            if ($this->generateLabelsFromComments && !empty($column->comment)) {
218
                $labels[$column->name] = $column->comment;
219
            } elseif (!strcasecmp($column->name, 'id')) {
220
                $labels[$column->name] = 'ID';
221
            } else {
222
                $label = Inflector::camel2words($column->name);
223
                if (!empty($label) && substr_compare($label, ' id', -3, 3, true) === 0) {
224
                    $label = substr($label, 0, -3) . ' ID';
225
                }
226
                $labels[$column->name] = $label;
227
            }
228
        }
229
        return $labels;
230
    }
231
    
232
    /**
233
     * @return \yii\db\Connection the DB connection as specified by [[db]].
234
     */
235
    protected function getDbConnection()
236
    {
237
        return Yii::$app->get('db', false);
238
    }
239
}
240