Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
With
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
8 / 8
15
100.00% covered (success)
100.00%
1 / 1
 renderOptions
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 reference
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 renderReference
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 renderAsSelect
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 select
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 renderSelect
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 sql
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 run
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
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 */
10namespace Framework\Database\Manipulation;
11
12use Closure;
13use Framework\Database\Result;
14use InvalidArgumentException;
15use LogicException;
16
17/**
18 * Class With.
19 *
20 * @see https://mariadb.com/kb/en/with/
21 *
22 * @package database
23 */
24class 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}