1 | <?php |
||||||
2 | |||||||
3 | declare(strict_types=1); |
||||||
4 | |||||||
5 | namespace Cycle\ORM\Command\Database; |
||||||
6 | |||||||
7 | use Cycle\Database\DatabaseInterface; |
||||||
8 | use Cycle\Database\Query\ReturningInterface; |
||||||
9 | use Cycle\ORM\Command\StoreCommand; |
||||||
10 | use Cycle\ORM\Command\Traits\ErrorTrait; |
||||||
11 | use Cycle\ORM\Command\Traits\MapperTrait; |
||||||
12 | use Cycle\ORM\Heap\State; |
||||||
13 | use Cycle\ORM\MapperInterface; |
||||||
14 | use Cycle\ORM\Schema\GeneratedField; |
||||||
15 | |||||||
16 | /** |
||||||
17 | * Insert data into associated table and provide lastInsertID promise. |
||||||
18 | */ |
||||||
19 | final class Insert extends StoreCommand |
||||||
20 | { |
||||||
21 | use ErrorTrait; |
||||||
0 ignored issues
–
show
introduced
by
Loading history...
|
|||||||
22 | use MapperTrait; |
||||||
23 | 1710 | ||||||
24 | /** |
||||||
25 | * @param non-empty-string $table |
||||||
0 ignored issues
–
show
|
|||||||
26 | * @param string[] $primaryKeys |
||||||
27 | * @param non-empty-string|null $pkColumn |
||||||
28 | * @param array<non-empty-string, int> $generated |
||||||
29 | */ |
||||||
30 | public function __construct( |
||||||
31 | DatabaseInterface $db, |
||||||
32 | 1710 | string $table, |
|||||
33 | 1710 | State $state, |
|||||
34 | ?MapperInterface $mapper, |
||||||
35 | private array $primaryKeys = [], |
||||||
36 | 2 | private ?string $pkColumn = null, |
|||||
37 | private array $generated = [], |
||||||
38 | 2 | ) { |
|||||
39 | parent::__construct($db, $table, $state); |
||||||
40 | $this->mapper = $mapper; |
||||||
41 | 1700 | } |
|||||
42 | |||||||
43 | 1700 | public function isReady(): bool |
|||||
44 | { |
||||||
45 | return true; |
||||||
46 | } |
||||||
47 | |||||||
48 | public function hasData(): bool |
||||||
49 | { |
||||||
50 | return match (true) { |
||||||
51 | $this->columns !== [], |
||||||
52 | $this->state->getData() !== [], |
||||||
53 | $this->hasGeneratedFields() => true, |
||||||
54 | default => false, |
||||||
55 | }; |
||||||
56 | } |
||||||
57 | |||||||
58 | public function getStoreData(): array |
||||||
59 | 1698 | { |
|||||
60 | if ($this->appendix !== []) { |
||||||
61 | 1698 | $this->state->setData($this->appendix); |
|||||
62 | $this->appendix = []; |
||||||
63 | 1698 | } |
|||||
64 | 56 | $data = $this->state->getData(); |
|||||
65 | return array_merge($this->columns, $this->mapper?->mapColumns($data) ?? $data); |
||||||
66 | } |
||||||
67 | 1698 | ||||||
68 | /** |
||||||
69 | * Insert data into associated table. |
||||||
70 | 1698 | */ |
|||||
71 | 1698 | public function execute(): void |
|||||
72 | 1402 | { |
|||||
73 | $state = $this->state; |
||||||
74 | $returningFields = []; |
||||||
75 | 1698 | ||||||
76 | if ($this->appendix !== []) { |
||||||
77 | 1698 | $state->setData($this->appendix); |
|||||
78 | 1698 | } |
|||||
79 | 1698 | ||||||
80 | $uncasted = $data = $state->getData(); |
||||||
81 | 1698 | ||||||
82 | 370 | // filter PK null values |
|||||
83 | foreach ($this->primaryKeys as $key) { |
||||||
84 | if (!isset($uncasted[$key])) { |
||||||
85 | 1698 | unset($uncasted[$key]); |
|||||
86 | } |
||||||
87 | 1682 | } |
|||||
88 | 1466 | // unset db-generated fields if they are null |
|||||
89 | 1466 | foreach ($this->generated as $column => $mode) { |
|||||
90 | 1386 | if (($mode & GeneratedField::ON_INSERT) === 0x0) { |
|||||
91 | continue; |
||||||
92 | 1386 | } |
|||||
93 | |||||||
94 | $returningFields[$column] = $mode; |
||||||
95 | if (!isset($uncasted[$column])) { |
||||||
96 | unset($uncasted[$column]); |
||||||
97 | 1682 | } |
|||||
98 | } |
||||||
99 | 1682 | $uncasted = $this->prepareData($uncasted); |
|||||
100 | |||||||
101 | $insert = $this->db |
||||||
102 | ->insert($this->table) |
||||||
0 ignored issues
–
show
It seems like
$this->table can also be of type null ; however, parameter $table of Cycle\Database\DatabaseInterface::insert() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||||
103 | ->values(\array_merge($this->columns, $uncasted)); |
||||||
104 | |||||||
105 | if ($this->pkColumn !== null && $returningFields === []) { |
||||||
106 | $returningFields[$this->primaryKeys[0]] ??= $this->pkColumn; |
||||||
107 | } |
||||||
108 | |||||||
109 | if ($insert instanceof ReturningInterface && $returningFields !== []) { |
||||||
110 | // Map generated fields to columns |
||||||
111 | $returning = $this->mapper->mapColumns($returningFields); |
||||||
0 ignored issues
–
show
The method
mapColumns() does not exist on null .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed.
Loading history...
|
|||||||
112 | // Array of [field name => column name] |
||||||
113 | $returning = \array_combine(\array_keys($returningFields), \array_keys($returning)); |
||||||
114 | |||||||
115 | $insert->returning(...\array_values($returning)); |
||||||
116 | |||||||
117 | $insertID = $insert->run(); |
||||||
118 | |||||||
119 | if (\count($returning) === 1) { |
||||||
120 | $field = \array_key_first($returning); |
||||||
121 | $state->register( |
||||||
122 | $field, |
||||||
123 | $this->mapper === null ? $insertID : $this->mapper->cast([$field => $insertID])[$field], |
||||||
124 | ); |
||||||
125 | } else { |
||||||
126 | foreach ($this->mapper->cast($insertID) as $field => $value) { |
||||||
127 | $state->register($field, $value); |
||||||
128 | } |
||||||
129 | } |
||||||
130 | } else { |
||||||
131 | $insertID = $insert->run(); |
||||||
132 | |||||||
133 | if ($insertID !== null && \count($this->primaryKeys) === 1) { |
||||||
134 | $fpk = $this->primaryKeys[0]; // first PK |
||||||
135 | if (!isset($data[$fpk])) { |
||||||
136 | $state->register( |
||||||
137 | $fpk, |
||||||
138 | $this->mapper === null ? $insertID : $this->mapper->cast([$fpk => $insertID])[$fpk] |
||||||
139 | ); |
||||||
140 | } |
||||||
141 | } |
||||||
142 | } |
||||||
143 | |||||||
144 | $state->updateTransactionData(); |
||||||
145 | |||||||
146 | parent::execute(); |
||||||
147 | } |
||||||
148 | |||||||
149 | public function register(string $key, mixed $value): void |
||||||
150 | { |
||||||
151 | $this->state->register($key, $value); |
||||||
152 | } |
||||||
153 | |||||||
154 | /** |
||||||
155 | * Has fields that weren't provided but will be generated by the database or PHP. |
||||||
156 | */ |
||||||
157 | private function hasGeneratedFields(): bool |
||||||
158 | { |
||||||
159 | if ($this->generated === []) { |
||||||
160 | return false; |
||||||
161 | } |
||||||
162 | |||||||
163 | $data = $this->state->getData(); |
||||||
164 | |||||||
165 | foreach ($this->generated as $field => $mode) { |
||||||
166 | if (($mode & (GeneratedField::ON_INSERT | GeneratedField::BEFORE_INSERT)) === 0x0) { |
||||||
167 | continue; |
||||||
168 | } |
||||||
169 | |||||||
170 | if (!isset($data[$field])) { |
||||||
171 | return true; |
||||||
172 | } |
||||||
173 | } |
||||||
174 | |||||||
175 | return false; |
||||||
176 | } |
||||||
177 | } |
||||||
178 |