| Code Coverage | ||||||||||
| Lines | Functions and Methods | Classes and Traits | ||||||||
| Total |  | 100.00% | 35 / 35 |  | 100.00% | 8 / 8 | CRAP |  | 100.00% | 1 / 1 | 
| With |  | 100.00% | 35 / 35 |  | 100.00% | 8 / 8 | 15 |  | 100.00% | 1 / 1 | 
| renderOptions |  | 100.00% | 9 / 9 |  | 100.00% | 1 / 1 | 4 | |||
| reference |  | 100.00% | 5 / 5 |  | 100.00% | 1 / 1 | 1 | |||
| renderReference |  | 100.00% | 7 / 7 |  | 100.00% | 1 / 1 | 3 | |||
| renderAsSelect |  | 100.00% | 1 / 1 |  | 100.00% | 1 / 1 | 1 | |||
| select |  | 100.00% | 2 / 2 |  | 100.00% | 1 / 1 | 1 | |||
| renderSelect |  | 100.00% | 3 / 3 |  | 100.00% | 1 / 1 | 2 | |||
| sql |  | 100.00% | 7 / 7 |  | 100.00% | 1 / 1 | 2 | |||
| run |  | 100.00% | 1 / 1 |  | 100.00% | 1 / 1 | 1 | |||
| 1 | <?php declare(strict_types=1); | 
| 2 | /* | 
| 3 | * This file is part of Aplus Framework Database Library. | 
| 4 | * | 
| 5 | * (c) Natan Felles <natanfelles@gmail.com> | 
| 6 | * | 
| 7 | * For the full copyright and license information, please view the LICENSE | 
| 8 | * file that was distributed with this source code. | 
| 9 | */ | 
| 10 | namespace Framework\Database\Manipulation; | 
| 11 | |
| 12 | use Closure; | 
| 13 | use Framework\Database\Result; | 
| 14 | use InvalidArgumentException; | 
| 15 | use LogicException; | 
| 16 | |
| 17 | /** | 
| 18 | * Class With. | 
| 19 | * | 
| 20 | * @see https://mariadb.com/kb/en/with/ | 
| 21 | * | 
| 22 | * @package database | 
| 23 | */ | 
| 24 | class With extends Statement | 
| 25 | { | 
| 26 | /** | 
| 27 | * @see https://mariadb.com/kb/en/recursive-common-table-expressions-overview/ | 
| 28 | * | 
| 29 | * @var string | 
| 30 | */ | 
| 31 | public const OPT_RECURSIVE = 'RECURSIVE'; | 
| 32 | |
| 33 | protected function renderOptions() : ?string | 
| 34 | { | 
| 35 | if ( ! $this->hasOptions()) { | 
| 36 | return null; | 
| 37 | } | 
| 38 | $options = $this->sql['options']; | 
| 39 | foreach ($options as &$option) { | 
| 40 | $input = $option; | 
| 41 | $option = \strtoupper($option); | 
| 42 | if ($option !== static::OPT_RECURSIVE) { | 
| 43 | throw new InvalidArgumentException("Invalid option: {$input}"); | 
| 44 | } | 
| 45 | } | 
| 46 | return \implode(' ', $options); | 
| 47 | } | 
| 48 | |
| 49 | /** | 
| 50 | * Adds a table reference. | 
| 51 | * | 
| 52 | * @param Closure|string $table | 
| 53 | * @param Closure $alias | 
| 54 | * | 
| 55 | * @see https://mariadb.com/kb/en/non-recursive-common-table-expressions-overview/ | 
| 56 | * @see https://mariadb.com/kb/en/recursive-common-table-expressions-overview/ | 
| 57 | * | 
| 58 | * @return static | 
| 59 | */ | 
| 60 | public function reference(Closure | string $table, Closure $alias) : static | 
| 61 | { | 
| 62 | $this->sql['references'][] = [ | 
| 63 | 'table' => $table, | 
| 64 | 'alias' => $alias, | 
| 65 | ]; | 
| 66 | return $this; | 
| 67 | } | 
| 68 | |
| 69 | protected function renderReference() : string | 
| 70 | { | 
| 71 | if ( ! isset($this->sql['references'])) { | 
| 72 | throw new LogicException('References must be set'); | 
| 73 | } | 
| 74 | $references = []; | 
| 75 | foreach ($this->sql['references'] as $reference) { | 
| 76 | $references[] = $this->renderIdentifier($reference['table']) | 
| 77 | . ' AS ' . $this->renderAsSelect($reference['alias']); | 
| 78 | } | 
| 79 | return \implode(', ', $references); | 
| 80 | } | 
| 81 | |
| 82 | private function renderAsSelect(Closure $subquery) : string | 
| 83 | { | 
| 84 | return '(' . $subquery(new Select($this->database)) . ')'; | 
| 85 | } | 
| 86 | |
| 87 | /** | 
| 88 | * Sets the SELECT statement part. | 
| 89 | * | 
| 90 | * @param Closure $select | 
| 91 | * | 
| 92 | * @return static | 
| 93 | */ | 
| 94 | public function select(Closure $select) : static | 
| 95 | { | 
| 96 | $this->sql['select'] = $select(new Select($this->database)); | 
| 97 | return $this; | 
| 98 | } | 
| 99 | |
| 100 | protected function renderSelect() : string | 
| 101 | { | 
| 102 | if ( ! isset($this->sql['select'])) { | 
| 103 | throw new LogicException('SELECT must be set'); | 
| 104 | } | 
| 105 | return $this->sql['select']; | 
| 106 | } | 
| 107 | |
| 108 | /** | 
| 109 | * Renders the WITH statement. | 
| 110 | * | 
| 111 | * @return string | 
| 112 | */ | 
| 113 | public function sql() : string | 
| 114 | { | 
| 115 | $sql = 'WITH' . \PHP_EOL; | 
| 116 | $part = $this->renderOptions(); | 
| 117 | if ($part) { | 
| 118 | $sql .= $part . \PHP_EOL; | 
| 119 | } | 
| 120 | $sql .= $this->renderReference() . \PHP_EOL; | 
| 121 | $sql .= $this->renderSelect(); | 
| 122 | return $sql; | 
| 123 | } | 
| 124 | |
| 125 | /** | 
| 126 | * Runs the WITH statement. | 
| 127 | * | 
| 128 | * @return Result | 
| 129 | */ | 
| 130 | public function run() : Result | 
| 131 | { | 
| 132 | return $this->database->query($this->sql()); | 
| 133 | } | 
| 134 | } |