Test Failed
Push — v2 ( 00665d...c532d8 )
by Berend
04:26
created

SchemaBuilder::buildConstraint()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 8
c 1
b 1
f 0
dl 0
loc 10
rs 10
cc 1
nc 1
nop 4
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
	public static function buildConstraint($parentTable, $parentColumn, $childTable, $childColumn)
30
	{
31
		$template = <<<SQL
32
ALTER TABLE `%s`
33
ADD CONSTRAINT
34
FOREIGN KEY (`%s`)
35
REFERENCES `%s`(`%s`)
36
ON DELETE CASCADE;
37
SQL;
38
		return sprintf($template, $childTable, $childColumn, $parentTable, $parentColumn);
39
	}
40
41
	/**
42
	 * Returns the type string as it should appear in the mysql create table statement for the given column
43
	 * @return string The type string
44
	 */
45
	public static function getDatabaseTypeString($colName, $type, $length)
46
	{
47
		switch (strtoupper($type)) {
48
			case '':
49
				throw new ActiveRecordException(sprintf("Column %s has invalid type \"NULL\"", $colName));
50
			
51
			case 'BOOL';
52
			case 'BOOLEAN':
53
			case 'DATETIME':
54
			case 'DATE':
55
			case 'TIME':
56
			case 'TEXT':
57
			case 'INT UNSIGNED':
58
				return $type;
59
60
			case 'VARCHAR':
61
				if ($length === null) {
62
					throw new ActiveRecordException(sprintf("field type %s requires specified column field \"LENGTH\"", $colName));
63
				} else {
64
					return sprintf('%s(%d)', $type, $length);	
65
				}
66
67
			case 'INT':
68
			case 'TINYINT':
69
			case 'BIGINT':
70
			default: 	
71
				// Implicitly assuming that non-specified cases are correct without a length parameter
72
				if ($length === null) {
73
					return $type;
74
				} else {
75
					return sprintf('%s(%d)', $type, $length);	
76
				}
77
		}
78
	}
79
80
	/**
81
	 * Builds the part of a MySQL create table statement that corresponds to the supplied column
82
	 * @param string $colName 	Name of the database column
83
	 * @param string $type 		The type of the string
84
	 * @param int $properties 	The set of Column properties that apply to this column (See ColumnProperty for options)
85
	 * @return string
86
	 */
87
	public static function buildCreateTableColumnEntry($colName, $type, $length, $properties, $default)
88
	{
89
		$stmnt = sprintf('`%s` %s ', $colName, self::getDatabaseTypeString($colName, $type, $length));
90
		if ($properties & ColumnProperty::NOT_NULL) {
91
			$stmnt .= 'NOT NULL ';
92
		} else {
93
			$stmnt .= 'NULL ';
94
		}
95
96
		if ($default !== NULL) {
97
			$stmnt .= 'DEFAULT ' . var_export($default, true) . ' ';
98
		}
99
100
		if ($properties & ColumnProperty::AUTO_INCREMENT) {
101
			$stmnt .= 'AUTO_INCREMENT ';
102
		}
103
104
		if ($properties & ColumnProperty::UNIQUE) {
105
			$stmnt .= 'UNIQUE ';
106
		}
107
108
		if ($properties & ColumnProperty::PRIMARY_KEY) {
109
			$stmnt .= 'PRIMARY KEY ';
110
		}
111
112
		return $stmnt;
113
	}
114
115
	/**
116
	 * Sorts the column statement components in the order such that the id appears first, 
117
	 * 		followed by all other columns in alphabetical ascending order
118
	 * @param   Array $colStatements Array of column statements
119
	 * @return  Array
120
	 */
121
	private static function sortColumnStatements($colStatements)
122
	{
123
		// Find ID statement and put it first
124
		$sortedStatements = [];
125
126
		$sortedStatements[] = $colStatements[AbstractActiveRecord::COLUMN_NAME_ID];
127
		unset($colStatements[AbstractActiveRecord::COLUMN_NAME_ID]);
128
129
		// Sort remaining columns in alphabetical order
130
		$columns = array_keys($colStatements);
131
		sort($columns);
132
		foreach ($columns as $colName) {
133
			$sortedStatements[] = $colStatements[$colName];
134
		}
135
136
		return $sortedStatements;
137
	}
138
139
	/**
140
	 * Builds the MySQL Create Table statement for the internal table definition
141
	 * @return string
142
	 */
143
	public static function buildCreateTableSQL($tableName, $tableDefinition)
144
	{
145
		$columnStatements = [];
146
		foreach ($tableDefinition as $colName => $definition) {
147
			// Destructure column definition
148
			$type    = $definition['type'] ?? null;
149
			$default = $definition['default'] ?? null;
150
			$length  = $definition['length'] ?? null;
151
			$properties = $definition['properties'] ?? null;
152
153
			if (isset($definition['relation']) && $type !== null) {
154
				$msg = sprintf("Column \"%s\" on table \"%s\": ", $colName, $tableName);
155
				$msg .= "Relationship columns have an automatically inferred type, so type should be omitted";
156
				throw new ActiveRecordException($msg);
157
			} else if (isset($definition['relation'])) {
158
				$type = AbstractActiveRecord::COLUMN_TYPE_ID;
159
			}
160
161
			$columnStatements[$colName] = self::buildCreateTableColumnEntry($colName, $type, $length, $properties, $default);
162
		}
163
164
		// Sort table (first column is id, the remaining are alphabetically sorted)
165
		$columnStatements = self::sortColumnStatements($columnStatements);
166
167
		$sql = sprintf("CREATE TABLE %s (\n%s\n);", 
168
			$tableName, 
169
			implode(",\n", $columnStatements));
170
171
		return $sql;
172
	}
173
174
}
175