Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
94.44% |
17 / 18 |
CRAP | |
99.03% |
102 / 103 |
| MySQLTableCreator | |
0.00% |
0 / 1 |
|
94.44% |
17 / 18 |
40 | |
99.03% |
102 / 103 |
| __construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| buildCreateTable | |
100.00% |
1 / 1 |
2 | |
100.00% |
5 / 5 |
|||
| buildCreateTableForTable | |
100.00% |
1 / 1 |
1 | |
100.00% |
9 / 9 |
|||
| buildCreateDelimitTable | |
100.00% |
1 / 1 |
1 | |
100.00% |
8 / 8 |
|||
| buildColumnSQL | |
100.00% |
1 / 1 |
5 | |
100.00% |
8 / 8 |
|||
| buildColumnSQLSnippet | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
| buildPrimaryKey | |
100.00% |
1 / 1 |
4 | |
100.00% |
8 / 8 |
|||
| buildPrimaryKeyForDelimiter | |
100.00% |
1 / 1 |
3 | |
100.00% |
5 / 5 |
|||
| buildIndexList | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
| buildIndex | |
100.00% |
1 / 1 |
4 | |
100.00% |
10 / 10 |
|||
| buildIndexPart | |
100.00% |
1 / 1 |
2 | |
100.00% |
5 / 5 |
|||
| buildForeignConstraintList | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
| buildForeignKeyConstraint | |
100.00% |
1 / 1 |
2 | |
100.00% |
14 / 14 |
|||
| buildEngineDefinition | |
100.00% |
1 / 1 |
3 | |
100.00% |
6 / 6 |
|||
| buildCollateDefinition | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
| buildCharsetDefinition | |
0.00% |
0 / 1 |
2.06 | |
75.00% |
3 / 4 |
|||
| getDatabaseSpecific | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| quote | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| <?php | |
| declare(strict_types = 1); | |
| namespace Siesta\Driver\MySQL; | |
| use Siesta\Driver\MySQL\MetaData\ConstraintRule; | |
| use Siesta\Model\Attribute; | |
| use Siesta\Model\DelimitAttributeList; | |
| use Siesta\Model\Entity; | |
| use Siesta\Model\Index; | |
| use Siesta\Model\IndexPart; | |
| use Siesta\Model\Reference; | |
| use Siesta\Util\ArrayUtil; | |
| /** | |
| * @author Gregor Müller | |
| */ | |
| class MySQLTableCreator | |
| { | |
| const CREATE_TABLE_SNIPPET = "CREATE TABLE IF NOT EXISTS "; | |
| const COLUMN_SNIPPET = "%s %s %s"; | |
| const PRIMARY_KEY_SNIPPET = ", PRIMARY KEY (%s)"; | |
| const DEFAULT_CHARSET_SNIPPET = " DEFAULT CHARACTER SET %s "; | |
| const COLALTE_SNIPPET = " COLLATE "; | |
| const ENGINE_SNIPPET = " ENGINE = "; | |
| const FOREIGN_KEY_SNIPPET = ", CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s) ON DELETE %s ON UPDATE %s"; | |
| const MYSQL_ENGINE_ATTRIBUTE = "engine"; | |
| const MYSQL_COLLATE_ATTRIBUTE = "collate"; | |
| const MYSQL_CHARSET_ATTRIBUTE = "charset"; | |
| const REFERENCE_OPTION_CASCADE = "CASCADE"; | |
| const REFERENCE_OPTION_RESTRICT = "RESTRICT"; | |
| const REFERENCE_OPTION_SET_NULL = "SET NULL"; | |
| const REFERENCE_OPTION_NO_ACTION = "NO ACTION"; | |
| const UNIQUE_SUFFIX = "_UNIQUE"; | |
| const INDEX_SUFFIX = "_INDEX"; | |
| const FOREIGN_KEY_SUFFIX = "_FOREIGN_KEY"; | |
| const FOREIGN_KEY_INDEX_SUFFIX = "_FK_INDEX"; | |
| /** | |
| * @var Entity | |
| */ | |
| protected $entity; | |
| /** | |
| * @var bool | |
| */ | |
| protected $replication; | |
| /** | |
| * @param Entity $entity | |
| */ | |
| public function __construct(Entity $entity) | |
| { | |
| $this->entity = $entity; | |
| } | |
| /** | |
| * @return string[] | |
| */ | |
| public function buildCreateTable() | |
| { | |
| $tableName = $this->entity->getTableName(); | |
| $result = [ | |
| $this->buildCreateTableForTable($tableName, false) | |
| ]; | |
| if ($this->entity->getIsReplication()) { | |
| $result[] = $this->buildCreateTableForTable($this->entity->getReplicationTableName(), true); | |
| } | |
| return $result; | |
| } | |
| /** | |
| * @param string $tableName | |
| * @param bool $replication | |
| * | |
| * @return string | |
| */ | |
| public function buildCreateTableForTable($tableName, $replication) | |
| { | |
| $sql = self::CREATE_TABLE_SNIPPET . $this->quote($tableName); | |
| $sql .= " (" . $this->buildColumnSQL(); | |
| $sql .= $this->buildPrimaryKey(); | |
| $sql .= $this->buildIndexList(); | |
| $sql .= $this->buildForeignConstraintList() . ")"; | |
| $sql .= $this->buildEngineDefinition($replication); | |
| $sql .= $this->buildCollateDefinition(); | |
| $sql .= $this->buildCharsetDefinition(); | |
| return $sql; | |
| } | |
| /** | |
| * @return string | |
| */ | |
| public function buildCreateDelimitTable() | |
| { | |
| $delimiterAttributes = DelimitAttributeList::getDelimitAttributes($this->entity); | |
| $sql = self::CREATE_TABLE_SNIPPET . $this->quote($this->entity->getDelimitTableName()); | |
| $sql .= "(" . $this->buildColumnSQL($delimiterAttributes); | |
| $sql .= $this->buildPrimaryKeyForDelimiter($delimiterAttributes) . ")"; | |
| $sql .= $this->buildEngineDefinition(); | |
| $sql .= $this->buildCollateDefinition(); | |
| $sql .= $this->buildCharsetDefinition(); | |
| return $sql; | |
| } | |
| /** | |
| * @param Attribute[] $additionalColumns | |
| * | |
| * @return string | |
| */ | |
| private function buildColumnSQL(array $additionalColumns = []) : string | |
| { | |
| $columnList = []; | |
| // used for delimiter functionality | |
| foreach ($additionalColumns as $attribute) { | |
| if (!$attribute->getIsTransient()) { | |
| $columnList[] = $this->buildColumnSQLSnippet($attribute); | |
| } | |
| } | |
| // iterate all attributes | |
| foreach ($this->entity->getAttributeList() as $attribute) { | |
| if (!$attribute->getIsTransient()) { | |
| $columnList[] = $this->buildColumnSQLSnippet($attribute); | |
| } | |
| } | |
| return implode(", ", $columnList); | |
| } | |
| /** | |
| * @param Attribute $attribute | |
| * | |
| * @return string | |
| */ | |
| private function buildColumnSQLSnippet(Attribute $attribute) : string | |
| { | |
| $not = ($attribute->getIsRequired()) ? "NOT NULL" : "NULL"; | |
| $attributeName = $this->quote($attribute->getDBName()); | |
| $attributeType = $attribute->getDbType(); | |
| return sprintf(self::COLUMN_SNIPPET, $attributeName, $attributeType, $not); | |
| } | |
| /** | |
| * @return string | |
| */ | |
| private function buildPrimaryKey() : string | |
| { | |
| $pkColumnList = []; | |
| foreach ($this->entity->getAttributeList() as $attribute) { | |
| if (!$attribute->getIsPrimaryKey()) { | |
| continue; | |
| } | |
| $pkColumnList[] = $this->quote($attribute->getDBName()); | |
| } | |
| if (sizeof($pkColumnList) === 0) { | |
| return ""; | |
| } | |
| return sprintf(self::PRIMARY_KEY_SNIPPET, implode(",", $pkColumnList)); | |
| } | |
| /** | |
| * @param Attribute[] $delimiterAttributes | |
| * | |
| * @return string | |
| */ | |
| private function buildPrimaryKeyForDelimiter(array $delimiterAttributes) | |
| { | |
| $sqlColumnList = []; | |
| foreach ($delimiterAttributes as $attribute) { | |
| if ($attribute->getIsPrimaryKey()) { | |
| $sqlColumnList[] = $this->quote($attribute->getDBName()); | |
| } | |
| } | |
| return sprintf(self::PRIMARY_KEY_SNIPPET, implode(",", $sqlColumnList)); | |
| } | |
| /** | |
| * @return string | |
| */ | |
| private function buildIndexList() : string | |
| { | |
| $sql = ""; | |
| foreach ($this->entity->getIndexList() as $index) { | |
| $sql .= "," . $this->buildIndex($index); | |
| } | |
| return $sql; | |
| } | |
| /** | |
| * @param Index $index | |
| * | |
| * @return string | |
| */ | |
| private function buildIndex(Index $index) | |
| { | |
| // check if unique index or index | |
| $sql = $index->getIsUnique() ? " UNIQUE INDEX " : " INDEX "; | |
| // add index name | |
| $sql .= $this->quote($index->getName()); | |
| // check if an index type has been set | |
| $indexType = $index->getIndexType(); | |
| if ($indexType !== null) { | |
| $sql .= " USING " . $indexType; | |
| } | |
| // open columns | |
| $indexPartList = []; | |
| foreach ($index->getIndexPartList() as $indexPart) { | |
| $indexPartList[] = $this->buildIndexPart($indexPart); | |
| } | |
| $sql .= " (" . implode(", ", $indexPartList) . ")"; | |
| return $sql; | |
| } | |
| /** | |
| * @param IndexPart $indexPart | |
| * | |
| * @return string | |
| */ | |
| private function buildIndexPart(IndexPart $indexPart) | |
| { | |
| $sql = $this->quote($indexPart->getColumnName()); | |
| if ($indexPart->getLength()) { | |
| $sql .= " (" . $indexPart->getLength() . ")"; | |
| } | |
| $sql .= " " . $indexPart->getSortOrder(); | |
| return $sql; | |
| } | |
| /** | |
| * @return string | |
| */ | |
| private function buildForeignConstraintList() : string | |
| { | |
| $sql = ""; | |
| foreach ($this->entity->getReferenceList() as $reference) { | |
| $sql .= $this->buildForeignKeyConstraint($reference); | |
| } | |
| return $sql; | |
| } | |
| /** | |
| * @param Reference $reference | |
| * | |
| * @return string | |
| */ | |
| private function buildForeignKeyConstraint(Reference $reference) : string | |
| { | |
| $columnList = []; | |
| $foreignColumnList = []; | |
| foreach ($reference->getReferenceMappingList() as $referenceMapping) { | |
| $foreignAttribute = $referenceMapping->getForeignAttribute(); | |
| $localAttribute = $referenceMapping->getLocalAttribute(); | |
| $columnList[] = $this->quote($localAttribute->getDBName()); | |
| $foreignColumnList[] = $this->quote($foreignAttribute->getDBName()); | |
| } | |
| $constraintName = $this->quote($reference->getConstraintName()); | |
| $columnNames = implode(",", $columnList); | |
| $foreignTable = $this->quote($reference->getForeignTable()); | |
| $foreignColumNames = implode(",", $foreignColumnList); | |
| $onDelete = ConstraintRule::schemaToMySQL($reference->getOnDelete()); | |
| $onUpdate = ConstraintRule::schemaToMySQL($reference->getOnUpdate()); | |
| return sprintf(self::FOREIGN_KEY_SNIPPET, $constraintName, $columnNames, $foreignTable, $foreignColumNames, $onDelete, $onUpdate); | |
| } | |
| /** | |
| * @param bool $replication | |
| * | |
| * @return string | |
| */ | |
| private function buildEngineDefinition($replication = false) | |
| { | |
| if ($replication) { | |
| return self::ENGINE_SNIPPET . "MEMORY"; | |
| } | |
| $engine = $this->getDatabaseSpecific(self::MYSQL_ENGINE_ATTRIBUTE); | |
| if ($engine !== null) { | |
| return self::ENGINE_SNIPPET . $engine; | |
| } | |
| return ""; | |
| } | |
| /** | |
| * @return string | |
| */ | |
| private function buildCollateDefinition() | |
| { | |
| $collate = $this->getDatabaseSpecific(self::MYSQL_COLLATE_ATTRIBUTE); | |
| if ($collate !== null) { | |
| return self::COLALTE_SNIPPET . $collate; | |
| } | |
| return ""; | |
| } | |
| /** | |
| * @return string | |
| */ | |
| private function buildCharsetDefinition() : string | |
| { | |
| $charset = $this->getDatabaseSpecific(self::MYSQL_CHARSET_ATTRIBUTE); | |
| if ($charset !== null) { | |
| return sprintf(self::DEFAULT_CHARSET_SNIPPET, $charset); | |
| } | |
| return ""; | |
| } | |
| /** | |
| * @param string $key | |
| * | |
| * @return string | |
| */ | |
| private function getDatabaseSpecific(string $key) | |
| { | |
| $dbSpecific = $this->entity->getDatabaseSpecificAttributeList(MySQLDriver::MYSQL_DRIVER_NAME); | |
| return ArrayUtil::getFromArray($dbSpecific, $key); | |
| } | |
| /** | |
| * @param string $name | |
| * | |
| * @return string | |
| */ | |
| private function quote($name) | |
| { | |
| return MySQLDriver::quote($name); | |
| } | |
| } |