Issues (265)

src/Schema/AbstractForeignKey.php (1 issue)

Labels
Severity
1
<?php
2
3
/**
4
 * This file is part of Cycle ORM package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\Database\Schema;
13
14
use Cycle\Database\Driver\DriverInterface;
15
use Cycle\Database\ForeignKeyInterface;
16
use Cycle\Database\Schema\Traits\ElementTrait;
17
18
/**
19
 * Abstract foreign schema with read (see ReferenceInterface) and write abilities. Must be
20
 * implemented by driver to support DBMS specific syntax and creation rules.
21
 */
22
abstract class AbstractForeignKey implements ForeignKeyInterface, ElementInterface
23
{
24
    use ElementTrait;
25
26
    protected const EXCLUDE_FROM_COMPARE = ['index'];
27
28
    /**
29
     * Local column name (key name).
30
     */
31
    protected array $columns = [];
32
33
    /**
34
     * Referenced table name (including prefix).
35
     */
36
    protected string $foreignTable = '';
37
38
    /**
39
     * Linked foreign key name (foreign column).
40
     */
41
    protected array $foreignKeys = [];
42
43
    /**
44
     * Action on foreign column value deletion.
45
     */
46
    protected string $deleteRule = self::NO_ACTION;
47
48
    /**
49
     * Action on foreign column value update.
50
     */
51
    protected string $updateRule = self::NO_ACTION;
52
53
    /**
54
     * Create an index or not.
55
     */
56
    protected bool $index = true;
57 224
58
    /**
59
     * @psalm-param non-empty-string $table
60
     *
61
     * @psalm-param non-empty-string $name
62 224
     */
63
    public function __construct(
64 224
        protected string $table,
65
        protected string $tablePrefix,
66 224
        protected string $name,
67
    ) {}
68
69 134
    public function getColumns(): array
70
    {
71 134
        return $this->columns;
72
    }
73
74 184
    public function getForeignTable(): string
75
    {
76 184
        return $this->foreignTable;
77
    }
78
79
    public function getForeignKeys(): array
80
    {
81
        return $this->foreignKeys;
82 184
    }
83
84 184
    /**
85
     * @psalm-return non-empty-string
86
     */
87
    public function getDeleteRule(): string
88
    {
89
        return $this->deleteRule;
90 184
    }
91
92 184
    /**
93
     * @psalm-return non-empty-string
94
     */
95
    public function getUpdateRule(): string
96
    {
97
        return $this->updateRule;
98
    }
99 224
100
    /**
101 224
     * Set local column names foreign key relates to. Make sure column type is the same as foreign
102
     * column one.
103 224
     */
104
    public function columns(array $columns): self
105
    {
106
        $this->columns = $columns;
107
108
        return $this;
109
    }
110
111
    /**
112
     * Set foreign table name and key local column must reference to. Make sure local and foreign
113
     * column types are identical.
114
     *
115 224
     * @@psalm-param non-empty-string $table Foreign table name with or without database prefix (see 3rd argument).
116
     *
117
     * @param array $columns Foreign key names (id by default).
118
     * @param bool $forcePrefix When true foreign table will get same prefix as table being modified.
119
     */
120 224
    public function references(
121 224
        string $table,
122
        array $columns = ['id'],
123 224
        bool $forcePrefix = true,
124
    ): self {
125
        $this->foreignTable = ($forcePrefix ? $this->tablePrefix : '') . $table;
126
        $this->foreignKeys = $columns;
127
128
        return $this;
129
    }
130
131 80
    /**
132
     * Set foreign key delete behaviour.
133 80
     *
134
     * @psalm-param non-empty-string $rule Possible values: NO ACTION, CASCADE, etc (driver specific).
135 80
     */
136
    public function onDelete(string $rule = self::NO_ACTION): self
137
    {
138
        $this->deleteRule = \strtoupper($rule);
139
140
        return $this;
141
    }
142
143 80
    /**
144
     * Set foreign key update behaviour.
145 80
     *
146
     * @psalm-param non-empty-string $rule Possible values: NO ACTION, CASCADE, etc (driver specific).
147 80
     */
148
    public function onUpdate(string $rule = self::NO_ACTION): self
149
    {
150
        $this->updateRule = \strtoupper($rule);
151
152
        return $this;
153
    }
154
155 168
    public function setIndex(bool $index = true): static
156
    {
157 168
        $this->index = $index;
158
159 168
        return $this;
160 168
    }
161 168
162 168
    public function hasIndex(): bool
163
    {
164 168
        return $this->index;
165 168
    }
166
167 168
    /**
168 168
     * Foreign key creation syntax.
169
     *
170 168
     * @psalm-return non-empty-string
171
     */
172
    public function sqlStatement(DriverInterface $driver): string
173 138
    {
174
        $statement = [];
175
176 138
        $statement[] = 'CONSTRAINT';
177
        $statement[] = $driver->identifier($this->name);
0 ignored issues
show
The method identifier() does not exist on Cycle\Database\Driver\DriverInterface. It seems like you code against a sub-type of Cycle\Database\Driver\DriverInterface such as Cycle\Database\Driver\Driver. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

177
        /** @scrutinizer ignore-call */ 
178
        $statement[] = $driver->identifier($this->name);
Loading history...
178
        $statement[] = 'FOREIGN KEY';
179
        $statement[] = '(' . $this->packColumns($driver, $this->columns) . ')';
180
181
        $statement[] = 'REFERENCES ' . $driver->identifier($this->foreignTable);
182 224
        $statement[] = '(' . $this->packColumns($driver, $this->foreignKeys) . ')';
183
184 224
        $statement[] = "ON DELETE {$this->deleteRule}";
185
        $statement[] = "ON UPDATE {$this->updateRule}";
186
187
        return \implode(' ', $statement);
188
    }
189
190
    public function compare(self $initial): bool
191
    {
192
        foreach ($this as $name => $value) {
193
            if (\in_array($name, static::EXCLUDE_FROM_COMPARE, true)) {
194
                continue;
195
            }
196
            if ($value !== $initial->$name) {
197
                return false;
198
            }
199
        }
200
        return true;
201
    }
202
203
    /**
204
     * @psalm-return non-empty-string
205
     */
206
    protected function packColumns(DriverInterface $driver, array $columns): string
207
    {
208
        return \implode(', ', \array_map([$driver, 'identifier'], $columns));
209
    }
210
}
211