Completed
Push — master ( 13fedd...d8dd7c )
by Rasmus
04:33
created

SelectQuery::setFlag()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
ccs 4
cts 5
cp 0.8
rs 9.4285
cc 2
eloc 5
nc 2
nop 2
crap 2.032
1
<?php
2
3
namespace mindplay\sql\model\query;
4
5
use mindplay\sql\framework\Countable;
6
use mindplay\sql\framework\MapperProvider;
7
use mindplay\sql\model\components\Mappers;
8
use mindplay\sql\model\components\ReturnVars;
9
use mindplay\sql\model\Driver;
10
use mindplay\sql\model\schema\Column;
11
use mindplay\sql\model\schema\Table;
12
use mindplay\sql\model\schema\Type;
13
use mindplay\sql\model\TypeProvider;
14
use mindplay\sql\model\types\IntType;
15
16
/**
17
 * This class represents a SELECT query.
18
 *
19
 * This class implements `__toString()` magic, enabling the use of this query builder
20
 * in the SELECT, WHERE or ORDER BY clause of a parent SELECT (or other type of) query.
21
 *
22
 * Note that, when constructing nested queries, parameters must be bound against the
23
 * parent query - binding parameters or applying Mappers against a nested query has no effect.
24
 */
25
class SelectQuery extends ProjectionQuery implements MapperProvider, Countable
26
{
27
    use Mappers;
28
29
    /**
30
     * @var bool[] map where flag => true
31
     */
32
    private $flags = [];
33
34
    /**
35
     * @var ReturnVars
36
     */
37
    protected $return_vars;
38
39
    /**
40
     * @var string[] list of GROUP BY expressions
41
     */
42
    protected $group_by = [];
43
44
    /**
45
     * @var string[] list of HAVING expressions
46
     */
47
    protected $having = [];
48
49
    /**
50
     * @param Table        $root
51
     * @param Driver       $driver
52
     * @param TypeProvider $types
53
     */
54 1
    public function __construct(Table $root, Driver $driver, TypeProvider $types)
55
    {
56 1
        parent::__construct($root, $driver, $types);
57
        
58 1
        $this->return_vars = new ReturnVars($root, $driver, $types);
59 1
    }
60
61
    /**
62
     * Add all the Columns of a full Table to be selected and returned
63
     *
64
     * @param Table $table Table to select and return
65
     *
66
     * @return $this
67
     */
68 1
    public function table(Table $table)
69
    {
70 1
        $this->return_vars->addTable($table);
71
72 1
        return $this;
73
    }
74
75
    /**
76
     * Add one or more Columns to select and return
77
     *
78
     * @param Column|Column[] one or more Columns to select and return
79
     *
80
     * @return $this
81
     */
82 1
    public function columns($cols)
83
    {
84 1
        $this->return_vars->addColumns($cols);
85
86 1
        return $this;
87
    }
88
89
    /**
90
     * Add an SQL expression to select and return
91
     *
92
     * @param string           $expr return expression
93
     * @param string|null      $name return variable name (optional, but usually required)
94
     * @param Type|string|null $type optional Type (or Type class-name)
95
     *
96
     * @return $this
97
     */
98 1
    public function value($expr, $name = null, $type = null)
99
    {
100 1
        $this->return_vars->addValue($expr, $name, $type);
101
102 1
        return $this;
103
    }
104
105
    /**
106
     * Add an expression to apply to a GROUP BY clause
107
     *
108
     * @param Column|string $expr SQL expression (or Column object) to apply to the GROUP BY clause
109
     *
110
     * @return $this
111
     */
112 1
    public function groupBy($expr)
113
    {
114 1
        $this->group_by[] = (string) $expr;
115
116 1
        return $this;
117
    }
118
119
    /**
120
     * @param string|string[] $exprs one or more condition expressions to apply to the HAVING clause
121
     *
122
     * @return $this
123
     */
124 1
    public function having($exprs)
125
    {
126 1
        foreach ((array) $exprs as $expr) {
127 1
            $this->having[] = $expr;
128
        }
129
130 1
        return $this;
131
    }
132
133
    /**
134
     * @inheritdoc
135
     */
136 1
    public function getMappers()
137
    {
138 1
        return array_merge([$this->return_vars->createTypeMapper()], $this->mappers);
139
    }
140
141
    /**
142
     * @inheritdoc
143
     */
144 1
    public function getSQL()
145
    {
146 1
        $flags = $this->buildFlags();
147
148 1
        $select = "SELECT " . ($flags ? "{$flags} " : "")
149 1
            . $this->return_vars->buildReturnVars();
150
151 1
        $from = "\nFROM " . $this->buildNodes();
152
153 1
        $where = count($this->conditions)
154 1
            ? "\nWHERE " . $this->buildConditions()
155 1
            : ''; // no conditions present
156
        
157 1
        $group_by = count($this->group_by)
158 1
            ? "\nGROUP BY " . implode(", ", $this->group_by)
159 1
            : ""; // no group-by expressions
160
161 1
        $having = count($this->having)
162 1
            ? "\nHAVING " . $this->buildHaving()
163 1
            : ''; // no having clause present
164
        
165 1
        $order = count($this->order)
166 1
            ? "\nORDER BY " . $this->buildOrderTerms()
167 1
            : ''; // no order terms
168
169 1
        $limit = $this->limit !== null
170 1
            ? "\nLIMIT {$this->limit}"
171 1
            . ($this->offset !== null ? " OFFSET {$this->offset}" : '')
172 1
            : ''; // no limit or offset
173
174 1
        return "{$select}{$from}{$where}{$group_by}{$having}{$order}{$limit}";
175
    }
176
177
    /**
178
     * @internal do not call this method directly from client-code (see `Countable` interface)
179
     *
180
     * @ignore
181
     *
182
     * @see Connection::count()
183
     *
184
     * @return SelectQuery
185
     */
186 1
    public function createCountStatement()
187
    {
188 1
        $query = clone $this;
189
190 1
        $query->return_vars = new ReturnVars($this->root, $this->driver, $this->types);
191
192 1
        $query->return_vars->addValue("COUNT(*)", "count", IntType::class);
193
194 1
        $query->mappers = []; // remove existing mappers not applicable to the COUNT result
195
196 1
        $query->limit = null;
197 1
        $query->offset = null;
198
        
199 1
        $query->order = [];
200
        
201 1
        return $query;
202
    }
203
204
    /**
205
     * @ignore string magic (enables creation of nested SELECT queries)
206
     */
207 1
    public function __toString()
208
    {
209 1
        return "(" . $this->getSQL() . ")";
210
    }
211
212
    /**
213
     * @return string combined condition expression (for use in the WHERE clause of an SQL statement)
214
     */
215 1
    protected function buildHaving()
216
    {
217 1
        return implode(" AND ", $this->having);
218
    }
219
220
    /**
221
     * @param string $flag
222
     * @param bool   $state
223
     */
224 1
    protected function setFlag($flag, $state = true)
225
    {
226 1
        if ($state) {
227 1
            $this->flags[$flag] = true;
228
        } else {
229
            unset($this->flags[$flag]);
230
        }
231 1
    }
232
    
233
    /**
234
     * @return string query flags (such as "SQL_CALC_FOUND_ROWS" in a MySQL SELECT query)
235
     */
236 1
    protected function buildFlags()
237
    {
238 1
        return implode(" ", array_keys($this->flags));
239
    }
240
}
241