Completed
Pull Request — master (#138)
by
unknown
04:49
created

FieldGenerator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php namespace Xethron\MigrationsGenerator\Generators;
2
3
use DB;
4
5
class FieldGenerator {
6
7
	/**
8
	 * Convert dbal types to Laravel Migration Types
9
	 * @var array
10
	 */
11
	protected $fieldTypeMap = [
12
		'tinyint'  => 'tinyInteger',
13
		'smallint' => 'smallInteger',
14
		'bigint'   => 'bigInteger',
15
		'datetime' => 'dateTime',
16
		'blob'     => 'binary',
17
	];
18
19
	/**
20
	 * @var string
21
	 */
22
	protected $database;
23
24
    /**
25
     * @var bool
26
     */
27
	private $unusedTimestamps;
28
29
    /**
30
     * @param bool $unusedTimestamps
31
     */
32
    public function __construct($unusedTimestamps)
33
    {
34
        $this->unusedTimestamps = $unusedTimestamps;
35
    }
36
37
	/**
38
	 * Create array of all the fields for a table
39
	 *
40
	 * @param string                                      $table Table Name
41
	 * @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema
42
	 * @param string                                      $database
43
	 * @param bool                                        $ignoreIndexNames
44
	 *
45
	 * @return array|bool
46
	 */
47
	public function generate($table, $schema, $database, $ignoreIndexNames)
48
	{
49
		$this->database = $database;
50
		$columns = $schema->listTableColumns( $table );
51
		if ( empty( $columns ) ) return false;
52
53
		$indexGenerator = new IndexGenerator($table, $schema, $ignoreIndexNames);
54
		$fields = $this->setEnum($this->getFields($columns, $indexGenerator), $table);
55
		$indexes = $this->getMultiFieldIndexes($indexGenerator);
56
		return array_merge($fields, $indexes);
57
	}
58
59
	/**
60
	 * Return all enum columns for a given table
61
	 * @param string $table
62
	 * @return array
63
	 */
64
	protected function getEnum($table)
65
	{
66
		try {
67
			$result = DB::table('information_schema.columns')
68
				->where('table_schema', $this->database)
69
				->where('table_name', $table)
70
				->where('data_type', 'enum')
71
				->get(['column_name','column_type']);
72
			if ($result)
73
				return $result;
74
			else
75
				return [];
76
		} catch (\Exception $e){
77
			return [];
78
		}
79
	}
80
81
	/**
82
	 * @param array $fields
83
	 * @param string $table
84
	 * @return array
85
	 */
86
	protected function setEnum(array $fields, $table)
87
	{
88
		foreach ($this->getEnum($table) as $column) {
89
			$fields[$column->column_name]['type'] = 'enum';
90
			$fields[$column->column_name]['args'] = str_replace('enum(', 'array(', $column->column_type);
91
		}
92
		return $fields;
93
	}
94
95
	/**
96
	 * @param \Doctrine\DBAL\Schema\Column[] $columns
97
	 * @param IndexGenerator $indexGenerator
98
	 * @return array
99
	 */
100
	protected function getFields($columns, IndexGenerator $indexGenerator)
101
	{
102
		$fields = array();
103
		foreach ($columns as $column) {
104
			$name = $column->getName();
105
			$type = $column->getType()->getName();
106
			$length = $column->getLength();
107
			$default = $column->getDefault();
108
			if (is_bool($default))
109
				$default = $default === true ? 1 : 0;
110
			$nullable = (!$column->getNotNull());
111
			$index = $indexGenerator->getIndex($name);
112
			$comment = $column->getComment();
113
			$decorators = null;
114
			$args = null;
115
116
			if (isset($this->fieldTypeMap[$type])) {
117
				$type = $this->fieldTypeMap[$type];
118
			}
119
120
			// Different rules for different type groups
121
			if (in_array($type, ['tinyInteger', 'smallInteger', 'integer', 'bigInteger'])) {
122
				// Integer
123
				if ($type == 'integer' and $column->getUnsigned() and $column->getAutoincrement()) {
124
					$type = 'increments';
125
					$index = null;
126
				} else {
127
					if ($column->getUnsigned()) {
128
						$decorators[] = 'unsigned';
129
					}
130
					if ($column->getAutoincrement()) {
131
						$args = 'true';
132
						$index = null;
133
					}
134
				}
135
			} elseif ($type == 'dateTime') {
136
				if ($name == 'deleted_at' and $nullable) {
137
					$nullable = false;
138
					$type = 'softDeletes';
139
					$name = '';
140
				} elseif (($name == 'created_at' and isset($fields['updated_at'])) and !$this->unusedTimestamps) {
141
					$fields['updated_at'] = ['field' => '', 'type' => 'timestamps'];
142
					continue;
143
				} elseif (($name == 'updated_at' and isset($fields['created_at'])) and !$this->unusedTimestamps) {
144
					$fields['created_at'] = ['field' => '', 'type' => 'timestamps'];
145
					continue;
146
				}
147
			} elseif (in_array($type, ['decimal', 'float', 'double'])) {
148
				// Precision based numbers
149
				$args = $this->getPrecision($column->getPrecision(), $column->getScale());
150
				if ($column->getUnsigned()) {
151
					$decorators[] = 'unsigned';
152
				}
153
			} else {
154
				// Probably not a number (string/char)
155
				if ($type === 'string' && $column->getFixed()) {
156
					$type = 'char';
157
				}
158
				$args = $this->getLength($length);
159
			}
160
161
			if ($nullable) $decorators[] = 'nullable';
162
			if ($default !== null) $decorators[] = $this->getDefault($default, $type);
163
			if ($index) $decorators[] = $this->decorate($index->type, $index->name);
164
			if ($comment) $decorators[] = "comment('" . addcslashes($comment, "\\'") . "')";
0 ignored issues
show
Bug Best Practice introduced by
The expression $comment of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null 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...
165
166
			$field = ['field' => $name, 'type' => $type];
167
			if ($decorators) $field['decorators'] = $decorators;
168
			if ($args) $field['args'] = $args;
169
			$fields[$name] = $field;
170
		}
171
		return $fields;
172
	}
173
174
	/**
175
	 * @param int $length
176
	 * @return int|void
177
	 */
178
	protected function getLength($length)
179
	{
180
		if ($length and $length !== 255) {
181
			return $length;
182
		}
183
	}
184
185
	/**
186
	 * @param string $default
187
	 * @param string $type
188
	 * @return string
189
	 */
190
	protected function getDefault($default, &$type)
191
	{
192
		if (in_array($default, ['CURRENT_TIMESTAMP'], true)) {
193
			if ($type == 'dateTime')
194
				$type = 'timestamp';
195
			$default = $this->decorate('DB::raw', $default);
196
		} elseif (in_array($type, ['string', 'text']) or !is_numeric($default)) {
197
			$default = $this->argsToString($default);
198
		}
199
		return $this->decorate('default', $default, '');
200
	}
201
202
	/**
203
	 * @param int $precision
204
	 * @param int $scale
205
	 * @return string|void
206
	 */
207
	protected function getPrecision($precision, $scale)
208
	{
209
		if ($precision != 8 or $scale != 2) {
210
			$result = $precision;
211
			if ($scale != 2) {
212
				$result .= ', ' . $scale;
213
			}
214
			return $result;
215
		}
216
	}
217
218
	/**
219
	 * @param string|array $args
220
	 * @param string       $quotes
221
	 * @return string
222
	 */
223
	protected function argsToString($args, $quotes = '\'')
224
	{
225
		if ( is_array( $args ) ) {
226
			$separator = $quotes .', '. $quotes;
227
			$args = implode($separator, str_replace($quotes, '\\'.$quotes, $args));
228
		} else {
229
			$args = str_replace($quotes, '\\'.$quotes, $args);
230
		}
231
232
		return $quotes . $args . $quotes;
233
	}
234
235
	/**
236
	 * Get Decorator
237
	 * @param string       $function
238
	 * @param string|array $args
239
	 * @param string       $quotes
240
	 * @return string
241
	 */
242
	protected function decorate($function, $args, $quotes = '\'')
243
	{
244
		if ( ! is_null( $args ) ) {
245
			$args = $this->argsToString($args, $quotes);
246
			return $function . '(' . $args . ')';
247
		} else {
248
			return $function;
249
		}
250
	}
251
252
	/**
253
	 * @param IndexGenerator $indexGenerator
254
	 * @return array
255
	 */
256
	protected function getMultiFieldIndexes(IndexGenerator $indexGenerator)
257
	{
258
		$indexes = array();
259
		foreach ($indexGenerator->getMultiFieldIndexes() as $index) {
260
			$indexArray = [
261
				'field' => $index->columns,
262
				'type' => $index->type,
263
			];
264
			if ($index->name) {
265
				$indexArray['args'] = $this->argsToString($index->name);
266
			}
267
			$indexes[] = $indexArray;
268
		}
269
		return $indexes;
270
	}
271
}
272