Skip to content

Commit 9a710ba

Browse files
committed
Huge refactoring of SQL generator command, preparation for check constraint attribute
1 parent b73f255 commit 9a710ba

File tree

3 files changed

+83
-118
lines changed

3 files changed

+83
-118
lines changed

src/Command/SqlGeneratorCommand.php

Lines changed: 71 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,33 @@ public function __construct()
1414
parent::__construct(self::$defaultName);
1515
}
1616

17-
public static function isForeignKeyColumn(\ReflectionProperty $property) : bool
17+
public static function getForeignKeyReference(\ReflectionProperty $property) : ?array
1818
{
1919
if (!\str_contains($property->getName(), '_')) {
20-
return false;
20+
return null;
2121
}
2222

2323
$type = $property->getType();
2424

2525
if (!$type instanceof \ReflectionNamedType || $type->isBuiltin()) {
26-
return false;
26+
return null;
2727
}
2828

29-
$typeReflection = new \ReflectionClass($type->getName());
29+
$class = new \ReflectionClass($type->getName());
3030

31-
return $typeReflection->isSubclassOf(\CoolBeans\Contract\PrimaryKey::class);
32-
}
31+
if (!$class->isSubclassOf(\CoolBeans\Contract\PrimaryKey::class)) {
32+
return null;
33+
}
3334

34-
public static function getForeignKeyFromName(string $columnName) : array
35-
{
36-
$parts = \explode('_', $columnName);
35+
$foreignKeyAttribute = $property->getAttributes(\CoolBeans\Attribute\ForeignKey::class);
36+
37+
if (\count($foreignKeyAttribute) > 0) {
38+
$foreignKey = $foreignKeyAttribute[0]->newInstance();
39+
40+
return [$foreignKey->table, $foreignKey->column];
41+
}
42+
43+
$parts = \explode('_', $property->getName());
3744
$column = \array_pop($parts);
3845

3946
return [\implode('_', $parts), $column];
@@ -98,57 +105,69 @@ private function generateBean(string $className) : string
98105
$this->validateBean($bean);
99106

100107
$beanName = \Infinityloop\Utils\CaseConverter::toSnakeCase($bean->getShortName());
101-
$toReturn = 'CREATE TABLE `' . $beanName . '`(' . \PHP_EOL;
102-
$foreignKeys = [];
103-
$unique = [];
104-
$data = [];
105-
106-
$classUnique = $this->getClassUnique($bean);
107-
$classIndex = $this->getClassIndex($bean);
108+
$columns = [];
109+
$indexes = $this->getClassIndex($bean);
110+
$foreignKeyConstraints = [];
111+
$uniqueConstraints = $this->getClassUnique($bean);
112+
$checkConstraints = $this->getClassCheck($bean);
108113

109114
foreach ($bean->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
110115
if (!$property->getType() instanceof \ReflectionNamedType) {
111116
continue;
112117
}
113118

114-
$data[] = [
115-
'name' => $this->getPropertyName($property),
119+
$columns[] = [
120+
'name' => '`' . $property->getName() . '`',
116121
'dataType' => $this->getDataType($property),
117-
'notNull' => $this->getNotNull($property),
122+
'notNull' => $property->getType()->allowsNull() === false
123+
? 'NOT NULL'
124+
: ' ',
118125
'default' => $this->getDefault($property, $bean),
119126
'comment' => $this->getComment($property),
120127
];
121128

122-
$foreignKey = $this->getForeignKey($property, $bean);
129+
$foreignKey = $this->getForeignKey($property);
130+
131+
if (\is_string($foreignKey)) {
132+
$foreignKeyConstraints[] = $foreignKey;
133+
}
134+
123135
$uniqueConstraint = $this->getUnique($property, $beanName);
124136

125137
if (\is_string($uniqueConstraint)) {
126-
$unique[] = $uniqueConstraint;
138+
$uniqueConstraints[] = $uniqueConstraint;
127139
}
128140

129-
if (\is_string($foreignKey)) {
130-
$foreignKeys[] = $foreignKey;
141+
$checkConstraint = $this->getCheck($property, $beanName);
142+
143+
if (\is_string($checkConstraint)) {
144+
$checkConstraints[] = $checkConstraint;
131145
}
132146
}
133147

134-
$toReturn .= $this->buildTable($data);
135-
$toReturn .= $this->printSection($classIndex);
136-
$toReturn .= $this->printSection($classUnique);
137-
$toReturn .= $this->printSection($unique);
138-
$toReturn .= $this->printSection($foreignKeys);
139-
$toReturn .= \PHP_EOL . ')' . \PHP_EOL;
140-
$toReturn .= self::INDENTATION . $this->getTableCharset($bean) . \PHP_EOL;
141-
$toReturn .= self::INDENTATION . $this->getTableCollation($bean);
142-
$toReturn .= $this->getTableComment($bean);
143-
$toReturn .= ';';
144-
145-
return $toReturn;
148+
return 'CREATE TABLE `' . $beanName . '`(' . \PHP_EOL
149+
. $this->buildTable($columns)
150+
. $this->printSection($indexes)
151+
. $this->printSection($foreignKeyConstraints)
152+
. $this->printSection($uniqueConstraints)
153+
. $this->printSection($checkConstraints). \PHP_EOL
154+
. ')' . \PHP_EOL
155+
. self::INDENTATION . $this->getTableCharset($bean) . \PHP_EOL
156+
. self::INDENTATION . $this->getTableCollation($bean)
157+
. $this->getTableComment($bean)
158+
. ';';
146159
}
147160

148161
private function buildTable(array $data) : string
149162
{
150-
$longestNameLength = $this->getLongestByType($data, 'name');
151-
$longestDataTypeLength = $this->getLongestByType($data, 'dataType');
163+
$longestNameLength = 0;
164+
$longestDataTypeLength = 0;
165+
166+
foreach ($data as $row) {
167+
$longestNameLength = \max(\mb_strlen($row['name']), $longestNameLength);
168+
$longestDataTypeLength = \max(\mb_strlen($row['dataType']), $longestDataTypeLength);
169+
}
170+
152171
$toReturn = [];
153172

154173
foreach ($data as $row) {
@@ -165,21 +184,6 @@ private function buildTable(array $data) : string
165184
return \implode(',' . \PHP_EOL, $toReturn);
166185
}
167186

168-
private function getLongestByType(array $data, string $type) : int
169-
{
170-
$maxLength = 0;
171-
172-
foreach ($data as $row) {
173-
$length = \mb_strlen($row[$type]);
174-
175-
if ($length > $maxLength) {
176-
$maxLength = $length;
177-
}
178-
}
179-
180-
return $maxLength;
181-
}
182-
183187
private function getComment(\ReflectionProperty $property) : string
184188
{
185189
$commentAttribute = $property->getAttributes(\CoolBeans\Attribute\Comment::class);
@@ -285,13 +289,6 @@ private function getDefault(\ReflectionProperty $property, \ReflectionClass $bea
285289
};
286290
}
287291

288-
private function getNotNull(\ReflectionProperty $property) : string
289-
{
290-
return $property->getType()->allowsNull() === false
291-
? 'NOT NULL'
292-
: ' ';
293-
}
294-
295292
private function isBuiltInEnum(\ReflectionProperty $property) : bool
296293
{
297294
if (!$property->getType()->isBuiltin()) {
@@ -347,21 +344,16 @@ private function getDataType(\ReflectionProperty $property) : string
347344
? $typeOverride[0]->newInstance()->getType()
348345
: match ($type->getName()) {
349346
'string' => 'VARCHAR(255)',
350-
\Infinityloop\Utils\Json::class => 'JSON',
351347
'int' => 'INT(11)',
352348
'float' => 'DOUBLE',
353349
'bool' => 'TINYINT(1)',
350+
\Infinityloop\Utils\Json::class => 'JSON',
354351
\CoolBeans\PrimaryKey\IntPrimaryKey::class => 'INT(11) UNSIGNED',
355352
\DateTime::class, \Nette\Utils\DateTime::class => 'DATETIME',
356353
default => throw new \CoolBeans\Exception\DataTypeNotSupported('Data type ' . $type->getName() . ' is not supported.'),
357354
};
358355
}
359356

360-
private function getPropertyName(\ReflectionProperty $property) : string
361-
{
362-
return '`' . $property->getName() . '`';
363-
}
364-
365357
private function getClassUnique(\ReflectionClass $bean) : array
366358
{
367359
if (\count($bean->getAttributes(\CoolBeans\Attribute\ClassUniqueConstraint::class)) === 0) {
@@ -400,6 +392,11 @@ private function getClassUnique(\ReflectionClass $bean) : array
400392
return $constrains;
401393
}
402394

395+
private function getClassCheck(\ReflectionClass $bean) : array
396+
{
397+
return [];
398+
}
399+
403400
private function getClassIndex(\ReflectionClass $bean) : array
404401
{
405402
if (\count($bean->getAttributes(\CoolBeans\Attribute\ClassIndex::class)) === 0) {
@@ -481,6 +478,11 @@ private function getUnique(\ReflectionProperty $property, string $beanName) : ?s
481478
: null;
482479
}
483480

481+
private function getCheck(\ReflectionProperty $property, string $beanName) : ?string
482+
{
483+
return null;
484+
}
485+
484486
private function printSection(array $data) : string
485487
{
486488
if (\count($data) === 0) {
@@ -522,24 +524,16 @@ private function validateBean(\ReflectionClass $bean) : void
522524
}
523525
}
524526

525-
private function getForeignKey(\ReflectionProperty $property, \ReflectionClass $bean) : ?string
527+
private function getForeignKey(\ReflectionProperty $property) : ?string
526528
{
527-
if (!self::isForeignKeyColumn($property)) {
528-
return null;
529-
}
530-
531-
$hasPrimaryKeyAttribute = self::hasPrimaryKeyAttribute($bean);
532-
$attributeColumns = $hasPrimaryKeyAttribute
533-
? $bean->getAttributes(\CoolBeans\Attribute\PrimaryKey::class)[0]->newInstance()->columns
534-
: [];
529+
$reference = self::getForeignKeyReference($property);
535530

536-
if ($property->getName() === 'id' || ($this->hasPrimaryKeyAttribute($bean) && \in_array($property->getName(), $attributeColumns, true))) {
531+
if (!\is_array($reference)) {
537532
return null;
538533
}
539534

540-
$foreignKeyAttribute = $property->getAttributes(\CoolBeans\Attribute\ForeignKey::class);
535+
[$table, $column] = $reference;
541536
$foreignKeyConstraintAttribute = $property->getAttributes(\CoolBeans\Attribute\ForeignKeyConstraint::class);
542-
543537
$foreignKeyConstraintResult = '';
544538

545539
if (\count($foreignKeyConstraintAttribute) > 0) {
@@ -557,17 +551,6 @@ private function getForeignKey(\ReflectionProperty $property, \ReflectionClass $
557551
$foreignKeyConstraintResult .= ' ON DELETE ' . \CoolBeans\Attribute\Types\ForeignKeyConstraintType::getDefault();
558552
}
559553

560-
if (\count($foreignKeyAttribute) > 0) {
561-
$foreignKey = $foreignKeyAttribute[0]->newInstance();
562-
563-
$table = $foreignKey->table;
564-
$column = $foreignKey->column;
565-
} elseif (\str_contains($property->getName(), '_')) {
566-
[$table, $column] = self::getForeignKeyFromName($property->getName());
567-
} else {
568-
return null;
569-
}
570-
571554
return self::INDENTATION . 'FOREIGN KEY (`' . $property->getName() . '`) REFERENCES `' . $table . '`(`' . $column . '`)'
572555
. $foreignKeyConstraintResult;
573556
}

src/Utils/TableSorter.php

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ public function sort() : array
4343
$beanName = \Infinityloop\Utils\CaseConverter::toSnakeCase($bean->getShortName());
4444

4545
foreach ($dependencies as $classNameTmp => $dependencyTmp) {
46-
if (\in_array($beanName, $dependencyTmp)) {
46+
if (\in_array($beanName, $dependencyTmp, true)) {
4747
$dependencies[$classNameTmp] = \array_diff($dependencies[$classNameTmp], [$beanName]);
4848
}
4949
}
5050
}
5151

5252
if (!$tableOutputted) {
53-
throw new \Exception('Cycle detected');
53+
throw new \RuntimeException('Cycle detected');
5454
}
5555
}
5656

@@ -62,16 +62,14 @@ private function getForeignKeyTables(\ReflectionClass $bean) : array
6262
$toReturn = [];
6363

6464
foreach ($bean->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
65-
if (!\CoolBeans\Command\SqlGeneratorCommand::isForeignKeyColumn($property)) {
66-
continue;
67-
}
68-
69-
$foreignKeyTarget = $this->getForeignKeyDependency($property);
65+
$reference = \CoolBeans\Command\SqlGeneratorCommand::getForeignKeyReference($property);
7066

71-
if ($foreignKeyTarget === null) {
67+
if (!\is_array($reference)) {
7268
continue;
7369
}
7470

71+
[$foreignKeyTarget] = $reference;
72+
7573
if ($foreignKeyTarget === \Infinityloop\Utils\CaseConverter::toSnakeCase($bean->getShortName())) {
7674
continue; // self dependency
7775
}
@@ -81,19 +79,4 @@ private function getForeignKeyTables(\ReflectionClass $bean) : array
8179

8280
return $toReturn;
8381
}
84-
85-
private function getForeignKeyDependency(\ReflectionProperty $property) : ?string
86-
{
87-
$foreignKeyAttribute = $property->getAttributes(\CoolBeans\Attribute\ForeignKey::class);
88-
89-
if (\count($foreignKeyAttribute) > 0) {
90-
$foreignKey = $foreignKeyAttribute[0]->newInstance();
91-
92-
return $foreignKey->table;
93-
}
94-
95-
[$table, $column] = \CoolBeans\Command\SqlGeneratorCommand::getForeignKeyFromName($property->getName());
96-
97-
return $table;
98-
}
9982
}

tests/Unit/Command/SqlGeneratorCommandTest.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,16 @@ public function testSimple() : void
8888
INDEX `attribute_bean_col6_index` (`col6` ASC),
8989
INDEX `attribute_bean_col8_index` (`col8` DESC),
9090
INDEX `attribute_bean_col4_col5_col6_index` (`col4`,`col5` DESC,`col6` ASC),
91-
91+
92+
FOREIGN KEY (`simple_bean_2_id`) REFERENCES `simple_bean_2`(`id`),
93+
FOREIGN KEY (`col9_id`) REFERENCES `simple_bean_2`(`id`) ON DELETE RESTRICT,
94+
FOREIGN KEY (`col10_id`) REFERENCES `simple_bean_2`(`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
95+
9296
CONSTRAINT `unique_attribute_bean_col2_col3` UNIQUE (`col2`,`col3`),
9397
CONSTRAINT `unique_attribute_bean_col4_col5_col6` UNIQUE (`col4`,`col5`,`col6`),
94-
9598
CONSTRAINT `unique_attribute_bean_col4` UNIQUE (`col4`),
9699
CONSTRAINT `unique_attribute_bean_col5` UNIQUE (`col5`),
97-
CONSTRAINT `unique_attribute_bean_col6` UNIQUE (`col6`),
98-
99-
FOREIGN KEY (`simple_bean_2_id`) REFERENCES `simple_bean_2`(`id`),
100-
FOREIGN KEY (`col9_id`) REFERENCES `simple_bean_2`(`id`) ON DELETE RESTRICT,
101-
FOREIGN KEY (`col10_id`) REFERENCES `simple_bean_2`(`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
100+
CONSTRAINT `unique_attribute_bean_col6` UNIQUE (`col6`)
102101
)
103102
CHARSET = `utf8mb4`
104103
COLLATE = `utf8mb4_general_ci`

0 commit comments

Comments
 (0)