SchemaBuilder   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Test Coverage

Coverage 89.86%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 30
eloc 81
c 1
b 1
f 0
dl 0
loc 165
ccs 62
cts 69
cp 0.8986
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
C getDatabaseTypeString() 0 31 15
A buildConstraintOnDeleteCascade() 0 10 1
A buildCreateTableColumnEntry() 0 26 6
A buildConstraintOnDeleteSetNull() 0 10 1
A sortColumnStatements() 0 16 2
A buildCreateTableSQL() 0 29 5
1
<?php
2
3
/**
4
 * This file is part of the miBadger package.
5
 *
6
 * @author Michael Webbers <[email protected]>
7
 * @license http://opensource.org/licenses/Apache-2.0 Apache v2 License
8
 */
9
10
namespace miBadger\ActiveRecord;
11
12
use miBadger\Query\Query;
13
14
/**
15
 * The abstract Schema builder that helps with construction mysql tables
16
 *
17
 * @since 1.0.0
18
 */
19
class SchemaBuilder
20
{
21
	/**
22
	 * builds a MySQL constraint statement for the given parameters
23
	 * @param string $parentTable
24
	 * @param string $parentColumn
25
	 * @param string $childTable
26
	 * @param string $childColumn
27
	 * @return string The MySQL table constraint string
28
	 */
29 4
	public static function buildConstraintOnDeleteCascade($parentTable, $parentColumn, $childTable, $childColumn)
30
	{
31
		$template = <<<SQL
32 4
ALTER TABLE `%s`
33
ADD CONSTRAINT
34
FOREIGN KEY (`%s`)
35
REFERENCES `%s`(`%s`)
36
ON DELETE CASCADE;
37
SQL;
38 4
		return sprintf($template, $childTable, $childColumn, $parentTable, $parentColumn);
39
	}
40
 
41 1
	public static function buildConstraintOnDeleteSetNull($parentTable, $parentColumn, $childTable, $childColumn)
42
	{
43
		$template = <<<SQL
44 1
ALTER TABLE `%s`
45
ADD CONSTRAINT 
46
FOREIGN KEY (`%s`) 
47
REFERENCES `%s` (`%s`)
48
ON DELETE SET NULL
49
SQL;
50 1
  		return sprintf($template, $childTable, $childColumn, $parentTable, $parentColumn);
51
	}
52
53
	/**
54
	 * Returns the type string as it should appear in the mysql create table statement for the given column
55
	 * @return string The type string
56
	 */
57 39
	public static function getDatabaseTypeString($colName, $type, $length)
58
	{
59 39
		switch (strtoupper($type)) {
60 39
			case '':
61 1
				throw new ActiveRecordException(sprintf("Column %s has invalid type \"NULL\"", $colName));
62
			
63 38
			case 'BOOL';
64 38
			case 'BOOLEAN':
65 38
			case 'DATETIME':
66 38
			case 'DATE':
67 38
			case 'TIME':
68 38
			case 'TEXT':
69 38
			case 'INT UNSIGNED':
70 38
				return $type;
71
72 36
			case 'VARCHAR':
73 36
				if ($length === null) {
74
					throw new ActiveRecordException(sprintf("field type %s requires specified column field \"LENGTH\"", $colName));
75
				} else {
76 36
					return sprintf('%s(%d)', $type, $length);	
77
				}
78
79 17
			case 'INT':
80
			case 'TINYINT':
81
			case 'BIGINT':
82
			default: 	
83
				// Implicitly assuming that non-specified cases are correct without a length parameter
84 17
				if ($length === null) {
85
					return $type;
86
				} else {
87 17
					return sprintf('%s(%d)', $type, $length);	
88
				}
89
		}
90
	}
91
92
	/**
93
	 * Builds the part of a MySQL create table statement that corresponds to the supplied column
94
	 * @param string $colName 	Name of the database column
95
	 * @param string $type 		The type of the string
96
	 * @param int $properties 	The set of Column properties that apply to this column (See ColumnProperty for options)
97
	 * @return string
98
	 */
99 39
	public static function buildCreateTableColumnEntry($colName, $type, $length, $properties, $default)
100
	{
101 39
		$stmnt = sprintf('`%s` %s ', $colName, self::getDatabaseTypeString($colName, $type, $length));
102 38
		if ($properties & ColumnProperty::NOT_NULL) {
103 38
			$stmnt .= 'NOT NULL ';
104
		} else {
105 37
			$stmnt .= 'NULL ';
106
		}
107
108 38
		if ($default !== NULL) {
109 26
			$stmnt .= 'DEFAULT ' . var_export($default, true) . ' ';
110
		}
111
112 38
		if ($properties & ColumnProperty::AUTO_INCREMENT) {
113 38
			$stmnt .= 'AUTO_INCREMENT ';
114
		}
115
116 38
		if ($properties & ColumnProperty::UNIQUE) {
117 16
			$stmnt .= 'UNIQUE ';
118
		}
119
120 38
		if ($properties & ColumnProperty::PRIMARY_KEY) {
121 38
			$stmnt .= 'PRIMARY KEY ';
122
		}
123
124 38
		return $stmnt;
125
	}
126
127
	/**
128
	 * Sorts the column statement components in the order such that the id appears first, 
129
	 * 		followed by all other columns in alphabetical ascending order
130
	 * @param   Array $colStatements Array of column statements
131
	 * @return  Array
132
	 */
133 38
	private static function sortColumnStatements($colStatements)
134
	{
135
		// Find ID statement and put it first
136 38
		$sortedStatements = [];
137
138 38
		$sortedStatements[] = $colStatements[AbstractActiveRecord::COLUMN_NAME_ID];
139 38
		unset($colStatements[AbstractActiveRecord::COLUMN_NAME_ID]);
140
141
		// Sort remaining columns in alphabetical order
142 38
		$columns = array_keys($colStatements);
143 38
		sort($columns);
144 38
		foreach ($columns as $colName) {
145 38
			$sortedStatements[] = $colStatements[$colName];
146
		}
147
148 38
		return $sortedStatements;
149
	}
150
151
	/**
152
	 * Builds the MySQL Create Table statement for the internal table definition
153
	 * @return string
154
	 */
155 39
	public static function buildCreateTableSQL($tableName, $tableDefinition)
156
	{
157 39
		$columnStatements = [];
158 39
		foreach ($tableDefinition as $colName => $definition) {
159
			// Destructure column definition
160 39
			$type    = $definition['type'] ?? null;
161 39
			$default = $definition['default'] ?? null;
162 39
			$length  = $definition['length'] ?? null;
163 39
			$properties = $definition['properties'] ?? null;
164
165 39
			if (isset($definition['relation']) && $type !== null) {
166
				$msg = sprintf("Column \"%s\" on table \"%s\": ", $colName, $tableName);
167
				$msg .= "Relationship columns have an automatically inferred type, so type should be omitted";
168
				throw new ActiveRecordException($msg);
169 39
			} else if (isset($definition['relation'])) {
170 20
				$type = AbstractActiveRecord::COLUMN_TYPE_ID;
171
			}
172
173 39
			$columnStatements[$colName] = self::buildCreateTableColumnEntry($colName, $type, $length, $properties, $default);
174
		}
175
176
		// Sort table (first column is id, the remaining are alphabetically sorted)
177 38
		$columnStatements = self::sortColumnStatements($columnStatements);
178
179 38
		$sql = sprintf("CREATE TABLE %s (\n%s\n) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;", 
180
			$tableName, 
181 38
			implode(",\n", $columnStatements));
182
183 38
		return $sql;
184
	}
185
186
}
187