Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.18% covered (success)
95.18%
79 / 83
75.00% covered (success)
75.00%
6 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Alter
95.18% covered (success)
95.18%
79 / 83
75.00% covered (success)
75.00%
6 / 8
35
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
5
 modifyColumn
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 dropColumn
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 dropIndex
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 dropConstraint
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 after
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 render
97.06% covered (success)
97.06%
33 / 34
0.00% covered (danger)
0.00%
0 / 1
15
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Db\Sql\Schema;
15
16use Pop\Db\Adapter\AbstractAdapter;
17
18/**
19 * Schema ALTER table class
20 *
21 * @category   Pop
22 * @package    Pop\Db
23 * @author     Nick Sagona, III <dev@nolainteractive.com>
24 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
25 * @license    http://www.popphp.org/license     New BSD License
26 * @version    6.5.0
27 */
28class Alter extends AbstractStructure
29{
30
31    /**
32     * Existing columns in the table
33     * @var array
34     */
35    protected array $existingColumns = [];
36
37    /**
38     * Columns to be dropped
39     * @var array
40     */
41    protected array $dropColumns = [];
42
43    /**
44     * Indices to be dropped
45     * @var array
46     */
47    protected array $dropIndices = [];
48
49    /**
50     * Constraints to be dropped
51     * @var array
52     */
53    protected array $dropConstraints = [];
54
55    /**
56     * Constructor
57     *
58     * Instantiate the ALTER table object
59     *
60     * @param  string          $table
61     * @param  AbstractAdapter $db
62     */
63    public function __construct(string $table, $db)
64    {
65        parent::__construct($table, $db);
66
67        if (count($this->info['columns']) > 0) {
68            foreach ($this->info['columns'] as $name => $column) {
69                $size      = null;
70                $precision = null;
71                if (str_contains($column['type'], '(')) {
72                    $type = substr($column['type'], 0, strpos($column['type'], '('));
73                    if (str_contains($column['type'], ',')) {
74                        $size = substr($column['type'], (strpos($column['type'], '(') + 1));
75                        $size = substr($size, 0, strpos($size, ','));
76                        $precision = substr($column['type'], (strpos($column['type'], ',') + 1));
77                        $precision = trim(substr($precision, 0, strpos($precision, ')')));
78                    } else {
79                        $size = substr($column['type'], (strpos($column['type'], '(') + 1));
80                        $size = substr($size, 0, strpos($size, ')'));
81                    }
82                } else {
83                    $type = $column['type'];
84                }
85
86                $this->existingColumns[$name] = [
87                    'type'       => $type,
88                    'size'       => $size,
89                    'precision'  => $precision,
90                    'nullable'   => $column['null'],
91                    'default'    => null,
92                    'increment'  => false,
93                    'primary'    => $column['primary'],
94                    'unsigned'   => false,
95                    'attributes' => [],
96                    'modify'     => null
97                ];
98            }
99        }
100    }
101
102    /**
103     * Modify a column
104     *
105     * @param  string  $oldName
106     * @param  string  $newName
107     * @param  ?string $type
108     * @param  mixed   $size
109     * @param  mixed   $precision
110     * @return Alter
111     */
112    public function modifyColumn(string $oldName, string $newName, ?string $type = null, mixed $size = null, mixed $precision = null): Alter
113    {
114        if (isset($this->existingColumns[$oldName])) {
115            if ($type !== null) {
116                $this->existingColumns[$oldName]['type'] = $type;
117            }
118            if ($size !== null) {
119                $this->existingColumns[$oldName]['size'] = $size;
120            }
121            if ($precision !== null) {
122                $this->existingColumns[$oldName]['precision'] = $precision;
123            }
124
125            $this->existingColumns[$oldName]['modify'] = $newName;
126        }
127
128        return $this;
129    }
130
131    /**
132     * Drop a column
133     *
134     * @param  string $name
135     * @return Alter
136     */
137    public function dropColumn(string $name): Alter
138    {
139        if (!in_array($name, $this->dropColumns)) {
140            $this->dropColumns[] = $name;
141        }
142        return $this;
143    }
144
145    /**
146     * Drop an index
147     *
148     * @param  string $name
149     * @return Alter
150     */
151    public function dropIndex(string $name): Alter
152    {
153        if (!in_array($name, $this->dropIndices)) {
154            $this->dropIndices[] = $name;
155        }
156        return $this;
157    }
158
159    /**
160     * Drop a constraint
161     *
162     * @param  string $name
163     * @return Alter
164     */
165    public function dropConstraint(string $name): Alter
166    {
167        if (!in_array($name, $this->dropConstraints)) {
168            $this->dropConstraints[] = $name;
169        }
170        return $this;
171    }
172
173    /**
174     * Set the AFTER column (MySQL-only))
175     *
176     * @param  string $column
177     * @return Alter
178     */
179    public function after(string $column): Alter
180    {
181        if (($this->currentColumn !== null) && isset($this->columns[$this->currentColumn])) {
182            $this->columns[$this->currentColumn]['after'] = $column;
183        }
184        return $this;
185    }
186
187    /**
188     * Render the table schema
189     *
190     * @return string
191     */
192    public function render(): string
193    {
194        $schema = '';
195
196        // Modify existing columns
197        foreach ($this->existingColumns as $name => $column) {
198            if ($column['modify'] !== null) {
199                if ($this->isMysql()) {
200                    $schema .= 'ALTER TABLE ' . $this->quoteId($this->table) .
201                        ' CHANGE COLUMN ' . $this->quoteId($name) . ' ' .
202                        $this->getColumnSchema($column['modify'], $column) . ';' . PHP_EOL;
203                } else {
204                    if ($column['modify'] == $name) {
205                        $schema .= 'ALTER TABLE ' . $this->quoteId($this->table) . ' ALTER COLUMN ' .
206                            $this->getColumnSchema($column['modify'], $column) . ';' . PHP_EOL;
207                    } else {
208                        $schema .= 'ALTER TABLE ' . $this->quoteId($this->table) . ' RENAME COLUMN ' .
209                            $this->quoteId($name) . ' ' . $this->quoteId($column['modify']) . ';' . PHP_EOL;
210                    }
211                }
212            }
213        }
214
215        // Add new columns
216        foreach ($this->columns as $name => $column) {
217            $schema .= 'ALTER TABLE ' . $this->quoteId($this->table) . ' ADD ' . $this->getColumnSchema($name, $column);
218            if (($this->isMysql()) && !empty($column['after'])) {
219                $schema .= ' AFTER ' . $this->quoteId($column['after']);
220            }
221            $schema .= ';' . PHP_EOL;
222        }
223
224        // Drop columns
225        foreach ($this->dropColumns as $name => $column) {
226            $schema .= 'ALTER TABLE ' . $this->quoteId($this->table) . ' DROP COLUMN ' . $this->quoteId($column) . ';' . PHP_EOL;
227        }
228
229        // Drop indices
230        foreach ($this->dropIndices as $index) {
231            if ($this->isMysql()) {
232                $schema .= 'ALTER TABLE ' . $this->quoteId($this->table) . ' DROP INDEX ' . $this->quoteId($index) . ';' . PHP_EOL;
233            } else {
234                $schema .= 'DROP INDEX ' . $this->quoteId($this->table . '.' . $index) . ';' . PHP_EOL;
235            }
236        }
237
238        // Drop constraints
239        foreach ($this->dropConstraints as $constraint) {
240            if ($this->isMysql()) {
241                $schema .= 'ALTER TABLE ' . $this->quoteId($this->table) . ' DROP FOREIGN KEY ' .
242                    $this->quoteId($constraint) . ';' . PHP_EOL;
243            } else {
244                $schema .= 'ALTER TABLE ' . $this->quoteId($this->table) . ' DROP CONSTRAINT ' .
245                    $this->quoteId($constraint) . ';' . PHP_EOL;
246            }
247        }
248
249        // Add indices
250        if (count($this->indices) > 0) {
251            $schema .= Formatter\Table::createIndices($this->indices, $this->table, $this);
252        }
253
254        // Add constraints
255        if (count($this->constraints) > 0) {
256            $schema .= Formatter\Table::createConstraints($this->constraints, $this->table, $this);
257        }
258
259        return $schema . PHP_EOL;
260    }
261
262    /**
263     * Render the table schema to string
264     *
265     * @return string
266     */
267    public function __toString(): string
268    {
269        return $this->render();
270    }
271
272}