Completed
Pull Request — master (#34)
by Rasmus
05:13
created

SelectQuery::getIndexer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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