Completed
Branch feature/pre-split (7b42f5)
by Anton
03:44
created

AbstractReference   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 240
rs 10
c 0
b 0
f 0
wmc 20
lcom 2
cbo 5

15 Methods

Rating   Name   Duplication   Size   Complexity  
A declared() 0 9 3
A setName() 0 8 2
A getName() 0 8 2
A getColumn() 0 4 1
A getForeignTable() 0 4 1
A getForeignKey() 0 4 1
A getDeleteRule() 0 4 1
A getUpdateRule() 0 4 1
A column() 0 6 1
A references() 0 7 1
A onDelete() 0 6 1
A onUpdate() 0 6 1
A sqlStatement() 0 17 1
A compare() 0 7 1
A generateName() 0 11 2
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Spiral\Database\Schemas;
10
11
use Spiral\Database\Schemas\Prototypes\AbstractElement;
12
use Spiral\Database\Schemas\ReferenceInterface;
13
use Spiral\ODM\Exceptions\SchemaException;
14
15
/**
16
 * Abstract foreign schema with read (see ReferenceInterface) and write abilities. Must be
17
 * implemented by driver to support DBMS specific syntax and creation rules.
18
 */
19
abstract class AbstractReference extends AbstractElement implements ReferenceInterface
20
{
21
    /**
22
     * Local column name (key name).
23
     *
24
     * @var string
25
     */
26
    protected $column = '';
27
28
    /**
29
     * Referenced table name (including prefix).
30
     *
31
     * @var string
32
     */
33
    protected $foreignTable = '';
34
35
    /**
36
     * Linked foreign key name (foreign column).
37
     *
38
     * @var string
39
     */
40
    protected $foreignKey = '';
41
42
    /**
43
     * Action on foreign column value deletion.
44
     *
45
     * @var string
46
     */
47
    protected $deleteRule = self::NO_ACTION;
48
49
    /**
50
     * Action on foreign column value update.
51
     *
52
     * @var string
53
     */
54
    protected $updateRule = self::NO_ACTION;
55
56
    /**
57
     * Mark schema entity as declared, it will be kept in final diff.
58
     *
59
     * @param bool $declared
60
     *
61
     * @return $this
62
     */
63
    public function declared($declared = true)
64
    {
65
        if ($declared && $this->table->hasIndex([$this->column])) {
66
            //Some databases require index for each foreign key
67
            $this->table->index([$this->column])->declared(true);
68
        }
69
70
        return parent::declared($declared);
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     *
76
     * @param string $name
77
     *
78
     * @return $this
79
     */
80
    public function setName($name)
81
    {
82
        if (!empty($this->name)) {
83
            throw new SchemaException('Changing reference name is not allowed');
84
        }
85
86
        return parent::setName($name);
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     *
92
     * @param bool $quoted Quote name.
93
     */
94
    public function getName($quoted = false)
95
    {
96
        if (empty($this->name)) {
97
            $this->setName($this->generateName());
98
        }
99
100
        return parent::getName($quoted);
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    public function getColumn()
107
    {
108
        return $this->column;
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114
    public function getForeignTable()
115
    {
116
        return $this->foreignTable;
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122
    public function getForeignKey()
123
    {
124
        return $this->foreignKey;
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public function getDeleteRule()
131
    {
132
        return $this->deleteRule;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function getUpdateRule()
139
    {
140
        return $this->updateRule;
141
    }
142
143
    /**
144
     * Set local column name foreign key relates to. Make sure column type is the same as foreign
145
     * column one.
146
     *
147
     * @param string $column
148
     *
149
     * @return $this
150
     */
151
    public function column($column)
152
    {
153
        $this->column = $column;
154
155
        return $this;
156
    }
157
158
    /**
159
     * Set foreign table name and key local column must reference to. Make sure local and foreign
160
     * column types are identical.
161
     *
162
     * @param string $table  Foreign table name without database prefix (will be added
163
     *                       automatically).
164
     * @param string $column Foreign key name (id by default).
165
     *
166
     * @return $this
167
     */
168
    public function references($table, $column = 'id')
169
    {
170
        $this->foreignTable = $this->table->getPrefix() . $table;
171
        $this->foreignKey = $column;
172
173
        return $this;
174
    }
175
176
    /**
177
     * Set foreign key delete behaviour.
178
     *
179
     * @param string $rule Possible values: NO ACTION, CASCADE, etc (driver specific).
180
     *
181
     * @return $this
182
     */
183
    public function onDelete($rule = self::NO_ACTION)
184
    {
185
        $this->deleteRule = strtoupper($rule);
186
187
        return $this;
188
    }
189
190
    /**
191
     * Set foreign key update behaviour.
192
     *
193
     * @param string $rule Possible values: NO ACTION, CASCADE, etc (driver specific).
194
     *
195
     * @return $this
196
     */
197
    public function onUpdate($rule = self::NO_ACTION)
198
    {
199
        $this->updateRule = strtoupper($rule);
200
201
        return $this;
202
    }
203
204
    /**
205
     * Foreign key creation syntax.
206
     *
207
     * @return string
208
     */
209
    public function sqlStatement()
210
    {
211
        $statement = [];
212
213
        $statement[] = 'CONSTRAINT';
214
        $statement[] = $this->getName(true);
215
        $statement[] = 'FOREIGN KEY';
216
        $statement[] = '(' . $this->table->driver()->identifier($this->column) . ')';
217
218
        $statement[] = 'REFERENCES ' . $this->table->driver()->identifier($this->foreignTable);
219
        $statement[] = '(' . $this->table->driver()->identifier($this->foreignKey) . ')';
220
221
        $statement[] = "ON DELETE {$this->deleteRule}";
222
        $statement[] = "ON UPDATE {$this->updateRule}";
223
224
        return implode(' ', $statement);
225
    }
226
227
    /**
228
     * Compare two elements together.
229
     *
230
     * @param self $initial
231
     *
232
     * @return bool
233
     */
234
    public function compare(self $initial)
235
    {
236
        $normalized = clone $initial;
237
        $normalized->declared = $this->declared;
238
239
        return $this == $normalized;
240
    }
241
242
    /**
243
     * Generate unique foreign key name.
244
     *
245
     * @return string
246
     */
247
    protected function generateName()
248
    {
249
        $name = $this->table->getName() . '_foreign_' . $this->column . '_' . uniqid();
250
251
        if (strlen($name) > 64) {
252
            //Many dbs has limitations on identifier length
253
            $name = md5($name);
254
        }
255
256
        return $name;
257
    }
258
}
259