Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
89 / 89
100.00% covered (success)
100.00%
18 / 18
CRAP
100.00% covered (success)
100.00%
1 / 1
Schema
100.00% covered (success)
100.00%
89 / 89
100.00% covered (success)
100.00%
18 / 18
51
100.00% covered (success)
100.00%
1 / 1
 create
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createIfNotExists
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 drop
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 dropIfExists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 alter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 rename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 truncate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 enableForeignKeyCheck
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 disableForeignKeyCheck
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 render
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
12
 reset
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
1 / 1
18
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCreateTable
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getDropTable
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getAlterTable
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getRenameTable
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getTruncateTable
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Db\Sql;
16
17/**
18 * Sql schema table class
19 *
20 * @category   Pop
21 * @package    Pop\Db
22 * @author     Nick Sagona, III <nick@popphp.org>
23 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
24 * @license    https://www.popphp.org/license     New BSD License
25 * @version    7.0.0
26 */
27class Schema extends AbstractSql
28{
29
30    /**
31     * DROP table schema objects
32     * @var array
33     */
34    protected array $drop = [];
35
36    /**
37     * CREATE table schema objects
38     * @var array
39     */
40    protected array $create = [];
41
42    /**
43     * ALTER table schema objects
44     * @var array
45     */
46    protected array $alter = [];
47
48    /**
49     * RENAME table schema objects
50     * @var array
51     */
52    protected array $rename = [];
53
54    /**
55     * TRUNCATE table schema objects
56     * @var array
57     */
58    protected array $truncate = [];
59
60    /**
61     * Foreign key check flag
62     * @var bool
63     */
64    protected bool $foreignKeyCheck = true;
65
66    /**
67     * Access the CREATE table object
68     *
69     * @param  string $table
70     * @return Schema\Create
71     */
72    public function create(string $table): Schema\Create
73    {
74        return $this->getCreateTable($table);
75    }
76
77    /**
78     * Access the CREATE table object, setting IF NOT EXISTS
79     *
80     * @param  string $table
81     * @return Schema\Create
82     */
83    public function createIfNotExists(string $table): Schema\Create
84    {
85        $this->getCreateTable($table)->ifNotExists();
86        return $this->getCreateTable($table);
87    }
88
89    /**
90     * Access the DROP table object
91     *
92     * @param  string $table
93     * @return Schema\Drop
94     */
95    public function drop(string $table): Schema\Drop
96    {
97        return $this->getDropTable($table);
98    }
99
100    /**
101     * Access the DROP table object, setting IF EXISTS
102     *
103     * @param  string $table
104     * @return Schema\Drop
105     */
106    public function dropIfExists(string $table): Schema\Drop
107    {
108        return $this->getDropTable($table)->ifExists();
109    }
110
111    /**
112     * Access the ALTER table object
113     *
114     * @param  string $table
115     * @return Schema\Alter
116     */
117    public function alter(string $table): Schema\Alter
118    {
119        return $this->getAlterTable($table);
120    }
121
122    /**
123     * Access the RENAME table object
124     *
125     * @param  string $table
126     * @return Schema\Rename
127     */
128    public function rename(string $table): Schema\Rename
129    {
130        return $this->getRenameTable($table);
131    }
132
133    /**
134     * Access the TRUNCATE table object
135     *
136     * @param  string $table
137     * @return Schema\Truncate
138     */
139    public function truncate(string $table): Schema\Truncate
140    {
141        return $this->getTruncateTable($table);
142    }
143
144    /**
145     * Enable the foreign key check
146     *
147     * @return Schema
148     */
149    public function enableForeignKeyCheck(): Schema
150    {
151        $this->foreignKeyCheck = true;
152        return $this;
153    }
154
155    /**
156     * Disable the foreign key check
157     *
158     * @return Schema
159     */
160    public function disableForeignKeyCheck(): Schema
161    {
162        $this->foreignKeyCheck = false;
163        return $this;
164    }
165
166    /**
167     * Render the schema
168     *
169     * Rendering is side effect free: the table objects that produced the string are left in
170     * place, so the same schema can be rendered again (logged and then executed, inspected
171     * while debugging, echoed by a caller it was returned to). Use reset() to explicitly
172     * clear the object before building an unrelated schema with it.
173     *
174     * @return string
175     */
176    public function render(): string
177    {
178        $sql = '';
179
180        if (!$this->foreignKeyCheck) {
181            if ($this->isMysql()) {
182                $sql .= 'SET foreign_key_checks = 0;' . PHP_EOL . PHP_EOL;
183            } else if ($this->isSqlite()) {
184                $sql .= 'PRAGMA foreign_keys=off;' . PHP_EOL . PHP_EOL;
185            }
186        }
187
188        // Render DROP tables
189        foreach ($this->drop as $drop) {
190            $sql .= $drop->render();
191        }
192
193        // Render CREATE tables
194        foreach ($this->create as $create) {
195            $sql .= $create->render();
196        }
197
198        // Render ALTER tables
199        foreach ($this->alter as $alter) {
200            $sql .= $alter->render();
201        }
202
203        // Render RENAME tables
204        foreach ($this->rename as $rename) {
205            $sql .= $rename->render();
206        }
207
208        // Render TRUNCATE tables
209        foreach ($this->truncate as $truncate) {
210            $sql .= $truncate->render();
211        }
212
213        if (!$this->foreignKeyCheck) {
214            if ($this->isMysql()) {
215                $sql .= 'SET foreign_key_checks = 1;' . PHP_EOL . PHP_EOL;
216            } else if ($this->isSqlite()) {
217                $sql .= 'PRAGMA foreign_keys=on;' . PHP_EOL . PHP_EOL;
218            }
219        }
220
221        return $sql;
222    }
223
224    /**
225     * Reset and clear the schema object
226     *
227     * @return Schema
228     */
229    public function reset(): Schema
230    {
231        $this->drop            = [];
232        $this->create          = [];
233        $this->alter           = [];
234        $this->rename          = [];
235        $this->truncate        = [];
236        $this->foreignKeyCheck = true;
237
238        return $this;
239    }
240
241    /**
242     * Execute the schema directly
243     *
244     * @param  bool $reset
245     * @return void
246     */
247    public function execute(bool $reset = true): void
248    {
249        if (!$this->foreignKeyCheck) {
250            if ($this->isMysql()) {
251                $this->db->query('SET foreign_key_checks = 0');
252            } else if ($this->isSqlite()) {
253                $this->db->query('PRAGMA foreign_keys=off');
254            }
255        }
256
257        // Execute DROP tables
258        foreach ($this->drop as $drop) {
259            $dropStatements = $drop->renderToStatements();
260            foreach ($dropStatements as $statement) {
261                $this->db->query($statement);
262            }
263        }
264
265        // Execute CREATE tables
266        foreach ($this->create as $create) {
267            $createStatements = $create->renderToStatements();
268            foreach ($createStatements as $statement) {
269                $this->db->query($statement);
270            }
271        }
272
273        // Execute ALTER tables
274        foreach ($this->alter as $alter) {
275            $alterStatements = $alter->renderToStatements();
276            foreach ($alterStatements as $statement) {
277                $this->db->query($statement);
278            }
279        }
280
281        // Execute RENAME tables
282        foreach ($this->rename as $rename) {
283            $renameStatements = $rename->renderToStatements();
284            foreach ($renameStatements as $statement) {
285                $this->db->query($statement);
286            }
287        }
288
289        // Execute TRUNCATE tables
290        foreach ($this->truncate as $truncate) {
291            $truncateStatements = $truncate->renderToStatements();
292            foreach ($truncateStatements as $statement) {
293                $this->db->query($statement);
294            }
295        }
296
297        if (!$this->foreignKeyCheck) {
298            if ($this->isMysql()) {
299                $this->db->query('SET foreign_key_checks = 1');
300            } else if ($this->isSqlite()) {
301                $this->db->query('PRAGMA foreign_keys=on');
302            }
303        }
304
305        if ($reset) {
306            $this->reset();
307        }
308    }
309
310    /**
311     * Render the schema to string
312     *
313     * @return string
314     */
315    public function __toString(): string
316    {
317        return $this->render();
318    }
319
320    /**
321     * Get the CREATE table object
322     *
323     * @param  string $table
324     * @return Schema\Create
325     */
326    protected function getCreateTable(string $table): Schema\Create
327    {
328        if (!isset($this->create[$table])) {
329            $this->create[$table] = new Schema\Create($table, $this->db);
330        }
331        return $this->create[$table];
332    }
333
334    /**
335     * Get the DROP table object
336     *
337     * @param  string $table
338     * @return Schema\Drop
339     */
340    protected function getDropTable(string $table): Schema\Drop
341    {
342        if (!isset($this->drop[$table])) {
343            $this->drop[$table] = new Schema\Drop($table, $this->db);
344        }
345        return $this->drop[$table];
346    }
347
348    /**
349     * Get the ALTER table object
350     *
351     * @param  string $table
352     * @return Schema\Alter
353     */
354    protected function getAlterTable(string $table): Schema\Alter
355    {
356        if (!isset($this->alter[$table])) {
357            $this->alter[$table] = new Schema\Alter($table, $this->db);
358        }
359        return $this->alter[$table];
360    }
361
362    /**
363     * Get the RENAME table object
364     *
365     * @param  string $table
366     * @return Schema\Rename
367     */
368    protected function getRenameTable(string $table): Schema\Rename
369    {
370        if (!isset($this->rename[$table])) {
371            $this->rename[$table] = new Schema\Rename($table, $this->db);
372        }
373        return $this->rename[$table];
374    }
375
376    /**
377     * Get the TRUNCATE table object
378     *
379     * @param  string $table
380     * @return Schema\Truncate
381     */
382    protected function getTruncateTable(string $table): Schema\Truncate
383    {
384        if (!isset($this->truncate[$table])) {
385            $this->truncate[$table] = new Schema\Truncate($table, $this->db);
386        }
387        return $this->truncate[$table];
388    }
389
390}