SchemaRelationTransformer::transform()   B
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 34
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 5

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 34
ccs 19
cts 19
cp 1
rs 8.439
cc 5
eloc 16
nc 6
nop 2
crap 5
1
<?php
2
3
/*
4
 * This file is part of the "RocketORM" package.
5
 *
6
 * https://github.com/RocketORM/ORM
7
 *
8
 * For the full license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Rocket\ORM\Generator\Schema\Transformer;
13
14
use Rocket\ORM\Generator\Schema\Column;
15
use Rocket\ORM\Generator\Schema\Relation;
16
use Rocket\ORM\Generator\Schema\Schema;
17
use Rocket\ORM\Generator\Schema\Table;
18
use Rocket\ORM\Generator\Utils\StringUtil;
19
use Rocket\ORM\Model\Map\TableMap;
20
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
21
22
/**
23
 * @author Sylvain Lorinet <[email protected]>
24
 */
25
class SchemaRelationTransformer implements SchemaRelationTransformerInterface
26
{
27
    /**
28
     * @param Table $table
29
     * @param array $schemas
30
     *
31
     * @throws InvalidConfigurationException
32
     */
33 4
    public function transform(Table $table, array $schemas)
34
    {
35 4
        $relations = $table->getRelations();
36 4
        foreach ($relations as $relation) {
37
            // Check if local column exists
38 4
            $localColumn = $table->getColumn($relation->local);
39 4
            if (null == $localColumn) {
40 1
                throw new InvalidConfigurationException('Invalid local column value "' . $relation->local . '" for relation "' . $relation->with . '"');
41
            }
42
43
            // Find the related relation in loaded schemas
44 3
            $relatedTable = $this->guessRelatedTable($relation->with, $schemas);
45 2
            $oldWith = $relation->with;
46 2
            $relation->with = $relatedTable->getSchema()->escapedNamespace . '\\\\' . $relatedTable->phpName;
47
48 2
            if (null == $relation->phpName) {
49 2
                $relation->phpName = $relatedTable->phpName;
50 2
            }
51
52
            // Check if foreign column exists
53 2
            $foreignColumn = $relatedTable->getColumn($relation->foreign);
54 2
            if (null == $foreignColumn) {
55 1
                throw new InvalidConfigurationException('Invalid foreign column value "' . $relation->foreign . '" for relation "' . $oldWith . '"');
56
            }
57
58
            // TODO check if the local column type == foreign column type
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
59
60
            // Relation type guessing
61 1
            $this->guessRelationType($localColumn, $relatedTable, $relation);
62
63
            // Then, save the related table for check if the related relation has been created
64 1
            $relation->setRelatedTable($relatedTable);
65 1
        }
66 1
    }
67
68
    /**
69
     * @param Table $table
70
     */
71 1
    public function transformRelatedRelations(Table $table)
72
    {
73
        // Create all related relations that are not already created
74 1
        foreach ($table->getRelations() as $i => $relation) {
75 1
            if (null != $relation->getRelatedTable()) {
76 1
                $this->createRelatedRelation($relation, $table, $relation->getRelatedTable());
77 1
            }
78 1
        }
79 1
    }
80
81
    /**
82
     * Relations can be named in three ways :
83
     *  - my_table
84
     *  - database.my_table
85
     *  - Example\Model\MyModel
86
     *
87
     * In some case, there can be more than one relation called with the same name.
88
     *
89
     * @param string $with    The relation
90
     * @param array  $schemas All loaded schemas
91
     *
92
     * @throws InvalidConfigurationException
93
     *
94
     * @return Table
95
     */
96 3
    protected function guessRelatedTable($with, array $schemas)
97
    {
98 3
        $tables = [];
99
        /** @var Schema $schema */
100 3
        foreach ($schemas as $schema) {
101 3
            $tables = array_merge($tables, $schema->findTables($with));
102 3
        }
103
104 3
        if (!isset($tables[0])) {
105 1
            throw new InvalidConfigurationException('Invalid relation "' . $with . '"');
106
        }
107
108 2
        if (1 < sizeof($tables)) {
109 1
            throw new InvalidConfigurationException('Too much table for the relation "' . $with . '", prefix it with the database or use the object namespace');
110
        }
111
112 2
        return $tables[0];
113
    }
114
115
    /**
116
     * @param Column   $local
117
     * @param Table    $relatedTable
118
     * @param Relation $relation
119
     */
120 1
    protected function guessRelationType(Column $local, Table $relatedTable, Relation $relation)
121
    {
122 1
        if (!$local->isPrimaryKey) {
123 1
            $relation->type = TableMap::RELATION_TYPE_ONE_TO_MANY;
124 1
        } else {
125 1
            if (1 < $relation->getLocalTable()->getPrimaryKeyCount()) {
126 1
                $relation->type = TableMap::RELATION_TYPE_ONE_TO_MANY;
127 1
            } elseif (1 < $relatedTable->getPrimaryKeyCount()) {
128 1
                $relation->type = TableMap::RELATION_TYPE_MANY_TO_ONE;
129 1
                $relation->phpName = StringUtil::pluralize($relation->phpName);
130 1
            } else {
131 1
                $relation->type = TableMap::RELATION_TYPE_ONE_TO_ONE;
132
            }
133
        }
134 1
    }
135
136
    /**
137
     * @param Relation $relation
138
     * @param Table    $table
139
     * @param Table    $relatedTable
140
     */
141 1
    protected function createRelatedRelation(Relation $relation, Table $table, Table $relatedTable)
142
    {
143
        // Inverse relation type
144 1
        $phpName = $table->phpName;
145 1
        if (TableMap::RELATION_TYPE_MANY_TO_ONE == $relation->type) {
146 1
            $relatedType = TableMap::RELATION_TYPE_ONE_TO_MANY;
147 1
        } elseif (TableMap::RELATION_TYPE_ONE_TO_MANY == $relation->type) {
148 1
            $relatedType = TableMap::RELATION_TYPE_MANY_TO_ONE;
149 1
            $phpName = StringUtil::pluralize($table->phpName);
150 1
        } else {
151 1
            $relatedType = TableMap::RELATION_TYPE_ONE_TO_ONE;
152
        }
153
154 1
        $relatedRelation = new Relation($table->getSchema()->escapedNamespace . '\\\\' . $table->phpName, [
155 1
            'local'    => $relation->foreign,
156 1
            'foreign'  => $relation->local,
157 1
            'type'     => $relatedType,
158 1
            'phpName'  => $phpName,
159 1
            'onUpdate' => $relation->onUpdate,
160 1
            'onDelete' => $relation->onDelete,
161 1
        ], false);
162 1
        $relatedRelation->setLocalTable($relatedTable);
163 1
        $relatedRelation->setRelatedTable($table);
164
165 1
        if (!$relatedTable->hasRelation($relatedRelation->with)) {
166 1
            $relatedTable->addRelation($relatedRelation);
167 1
        }
168 1
    }
169
}
170