Completed
Pull Request — master (#10)
by Joao
07:08
created

Query::rightJoin()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 2
c 0
b 0
f 0
nc 2
nop 3
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 2
rs 10
1
<?php
2
3
namespace ByJG\MicroOrm;
4
5
use ByJG\AnyDataset\Db\DbDriverInterface;
6
use ByJG\MicroOrm\Exception\InvalidArgumentException;
7
use ByJG\Serializer\BinderObject;
8
9
class Query
10
{
11
    protected $fields = [];
12
    protected $table = "";
13
    protected $alias = "";
14
    protected $where = [];
15
    protected $groupBy = [];
16
    protected $orderBy = [];
17
    protected $join = [];
18
    protected $limitStart = null;
19
    protected $limitEnd = null;
20
    protected $top = null;
21
    protected $dbDriver = null;
22
23
    protected $forUpdate = false;
24
25
    public static function getInstance()
26
    {
27
        return new Query();
28 6
    }
29
30 6
    /**
31
     * Example:
32
     *   $query->fields(['name', 'price']);
33
     *
34
     * @param array $fields
35
     * @return $this
36
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
37
     */
38
    public function fields(array $fields)
39
    {
40
        foreach ($fields as $field) {
41 5
            if ($field instanceof Mapper) {
42
                $this->addFieldFromMapper($field);
43 5
                continue;
44 5
            }
45 1
            $this->fields[] = $field;
46 1
        }
47
48 4
        return $this;
49 5
    }
50
51 5
    /**
52
     * @param \ByJG\MicroOrm\Mapper $mapper
53
     * @throws \ByJG\Serializer\Exception\InvalidArgumentException
54
     */
55
    private function addFieldFromMapper(Mapper $mapper)
56
    {
57
        $entityClass = $mapper->getEntity();
58 1
        $entity = new $entityClass();
59
        $serialized = BinderObject::toArrayFrom($entity);
60 1
61 1
        foreach (array_keys($serialized) as $fieldName) {
62 1
            $mapField = $mapper->getFieldMap($fieldName, Mapper::FIELDMAP_FIELD);
63
            if (empty($mapField)) {
64 1
                $mapField = $fieldName;
65 1
            }
66 1
67 1
            $alias = $mapper->getFieldAlias($mapField);
68 1
            if (!empty($alias)) {
69
                $alias = ' as ' . $alias;
0 ignored issues
show
Bug introduced by
Are you sure $alias of type array|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

69
                $alias = ' as ' . /** @scrutinizer ignore-type */ $alias;
Loading history...
70 1
            }
71 1
72 1
            $this->fields[] = $mapper->getTable() . '.' . $mapField . $alias;
73 1
        }
74
    }
75 1
76 1
    /**
77 1
     * Example
78
     *    $query->table('product');
79
     *
80
     * @param string $table
81
     * @param null $alias
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $alias is correct as it would always require null to be passed?
Loading history...
82
     * @return $this
83
     */
84
    public function table($table, $alias = null)
85
    {
86 24
        $this->table = $table;
87
        $this->alias = $alias;
88 24
89
        return $this;
90 24
    }
91
92
    /**
93
     * Example:
94
     *    $query->join('sales', 'product.id = sales.id');
95
     *
96
     * @param string $table
97
     * @param string $filter
98
     * @param null $alias
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $alias is correct as it would always require null to be passed?
Loading history...
99
     * @return $this
100
     */
101 1
    public function join($table, $filter, $alias = null)
102
    {
103 1
        $this->join[] = [ 'table'=>$table, 'filter'=>$filter, 'type' => 'INNER', 'alias' => empty($alias) ? $table : $alias];
104 1
        return $this;
105
    }
106
107
    /**
108
     * Example:
109
     *    $query->leftJoin('sales', 'product.id = sales.id');
110
     *
111
     * @param string $table
112
     * @param string $filter
113
     * @param null $alias
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $alias is correct as it would always require null to be passed?
Loading history...
114
     * @return $this
115 1
     */
116
    public function leftJoin($table, $filter, $alias = null)
117 1
    {
118 1
        $this->join[] = [ 'table'=>$table, 'filter'=>$filter, 'type' => 'LEFT', 'alias' => empty($alias) ? $table : $alias];
119
        return $this;
120
    }
121
122
    /**
123
     * Example:
124
     *    $query->rightJoin('sales', 'product.id = sales.id');
125
     *
126
     * @param string $table
127
     * @param string $filter
128
     * @param null $alias
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $alias is correct as it would always require null to be passed?
Loading history...
129 22
     * @return $this
130
     */
131 22
    public function rightJoin($table, $filter, $alias = null)
132 22
    {
133
        $this->join[] = [ 'table'=>$table, 'filter'=>$filter, 'type' => 'RIGHT', 'alias' => empty($alias) ? $table : $alias];
134
        return $this;
135
    }
136
137
    /**
138
     * Example:
139
     *    $query->filter('price > [[amount]]', [ 'amount' => 1000] );
140
     *
141
     * @param string $filter
142 1
     * @param array $params
143
     * @return $this
144 1
     */
145
    public function where($filter, array $params = [])
146 1
    {
147
        $this->where[] = [ 'filter' => $filter, 'params' => $params  ];
148
        return $this;
149
    }
150
151
    /**
152
     * Example:
153
     *    $query->groupBy(['name']);
154
     *
155
     * @param array $fields
156 4
     * @return $this
157
     */
158 4
    public function groupBy(array $fields)
159
    {
160 4
        $this->groupBy = array_merge($this->groupBy, $fields);
161
    
162
        return $this;
163
    }
164
165
    /**
166
     * Example:
167
     *     $query->orderBy(['price desc']);
168
     *
169
     * @param array $fields
170 1
     * @return $this
171
     */
172 1
    public function orderBy(array $fields)
173
    {
174
        $this->orderBy = array_merge($this->orderBy, $fields);
175 1
176 1
        return $this;
177 1
    }
178
179
    public function forUpdate()
180 1
    {
181
        $this->forUpdate = true;
182 1
        
183
        return $this;
184
    }
185 1
186 1
    /**
187
     * @param $start
188
     * @param $end
189 24
     * @return $this
190
     * @throws \ByJG\MicroOrm\Exception\InvalidArgumentException
191 24
     */
192 20
    public function limit($start, $end)
193
    {
194
        if (!is_null($this->top)) {
195 5
            throw new InvalidArgumentException('You cannot mix TOP and LIMIT');
196
        }
197
        $this->limitStart = $start;
198 24
        $this->limitEnd = $end;
199
        return $this;
200 24
    }
201 24
202 2
    /**
203 24
     * @param $top
204 24
     * @return $this
205
     * @throws \ByJG\MicroOrm\Exception\InvalidArgumentException
206
     */
207 24
    public function top($top)
208
    {
209 24
        if (!is_null($this->limitStart)) {
210 24
            throw new InvalidArgumentException('You cannot mix TOP and LIMIT');
211
        }
212 24
        $this->top = $top;
213 22
        return $this;
214 22
    }
215 24
216
    protected function getFields()
217 24
    {
218 3
        if (empty($this->fields)) {
219
            return ' * ';
220
        }
221 22
222
        return ' ' . implode(', ', $this->fields) . ' ';
223
    }
224
225
    /**
226
     * @return string
227
     * @throws InvalidArgumentException
228 24
     */
229
    protected function getJoin()
230
    {
231 24
        $joinStr = $this->table . (!empty($this->alias) ? " as " . $this->alias : "");
232 24
        foreach ($this->join as $item) {
233
            $table = $item['table'];
234 24
            if ($table instanceof Query) {
235 24
                $subQuery = $table->build($this->dbDriver);
236 24
                if (!empty($subQuery["params"])) {
237 22
                    throw new InvalidArgumentException("SubQuery does not support filters");
238 22
                }
239 22
                if ($item["alias"] instanceof Query) {
240
                    throw new InvalidArgumentException("SubQuery requires you define an alias");
241 24
                }
242
                $table = "(${subQuery["sql"]})";
243 24
            }
244
            $alias = $item['table'] == $item['alias'] ? "" : " as ". $item['alias'];
245 24
            $joinStr .= ' ' . $item['type'] . " JOIN $table$alias ON " . $item['filter'];
246
        }
247 24
        return $joinStr;
248
    }
249 24
    
250
    protected function getWhere()
251 24
    {
252
        $whereStr = [];
253 24
        $params = [];
254
255
        foreach ($this->where as $item) {
256 24
            $whereStr[] = $item['filter'];
257
            $params = array_merge($params, $item['params']);
258 24
        }
259 21
        
260
        if (empty($whereStr)) {
261 4
            return null;
262
        }
263
        
264 24
        return [ implode(' AND ', $whereStr), $params ];
265
    }
266 24
267 24
    /**
268
     * @param \ByJG\AnyDataset\Db\DbDriverInterface|null $dbDriver
269 1
     * @return array
270
     * @throws \ByJG\MicroOrm\Exception\InvalidArgumentException
271
     */
272
    public function build(DbDriverInterface $dbDriver = null)
273
    {
274
        $this->dbDriver = $dbDriver;
275
276
        $sql = "SELECT " .
277 24
            $this->getFields() .
278
            "FROM " . $this->getJoin();
279 24
        
280 24
        $whereStr = $this->getWhere();
281
        $params = null;
282
        if (!is_null($whereStr)) {
283
            $sql .= ' WHERE ' . $whereStr[0];
284
            $params = $whereStr[1];
285
        }
286
287
        $sql .= $this->addGroupBy();
288
289
        $sql .= $this->addOrderBy();
290
291
        $sql = $this->addforUpdate($dbDriver, $sql);
292
293
        $sql = $this->addTop($dbDriver, $sql);
294
295 24
        $sql = $this->addLimit($dbDriver, $sql);
296
297 24
        $sql = ORMHelper::processLiteral($sql, $params);
298 23
299
        return [ 'sql' => $sql, 'params' => $params ];
300
    }
301 1
302
    private function addOrderBy()
303
    {
304
        if (empty($this->orderBy)) {
305 1
            return "";
306
        }
307
        return ' ORDER BY ' . implode(', ', $this->orderBy);
308
    }
309
310
    private function addGroupBy()
311
    {
312
        if (empty($this->groupBy)) {
313 24
            return "";
314
        }
315 24
        return ' GROUP BY ' . implode(', ', $this->groupBy);
316 23
    }
317
318
    /**
319 1
     * @param DbDriverInterface $dbDriver
320
     * @param string $sql
321
     * @return string
322
     * @throws \ByJG\MicroOrm\Exception\InvalidArgumentException
323 1
     */
324
    private function addforUpdate($dbDriver, $sql)
325
    {
326
        if (empty($this->forUpdate)) {
327
            return $sql;
328
        }
329
330
        if (is_null($dbDriver)) {
331
            throw new InvalidArgumentException('To get FOR UPDATE working you have to pass the DbDriver');
332
        }
333
334
        return $dbDriver->getDbHelper()->forUpdate($sql);
335
    }
336
337
    /**
338
     * @param DbDriverInterface $dbDriver
339
     * @param string $sql
340
     * @return string
341
     * @throws \ByJG\MicroOrm\Exception\InvalidArgumentException
342
     */
343
    private function addTop($dbDriver, $sql)
344
    {
345
        if (empty($this->top)) {
346
            return $sql;
347
        }
348
349
        if (is_null($dbDriver)) {
350
            throw new InvalidArgumentException('To get Limit and Top working you have to pass the DbDriver');
351
        }
352
353
        return $dbDriver->getDbHelper()->top($sql, $this->top);
354
    }
355
356
    /**
357
     * @param DbDriverInterface $dbDriver
358
     * @param string $sql
359
     * @return string
360
     * @throws \ByJG\MicroOrm\Exception\InvalidArgumentException
361
     */
362
    private function addLimit($dbDriver, $sql)
363
    {
364
        if (empty($this->limitStart) && ($this->limitStart !== 0)) {
365
            return $sql;
366
        }
367
368
        if (is_null($dbDriver)) {
369
            throw new InvalidArgumentException('To get Limit and Top working you have to pass the DbDriver');
370
        }
371
372
        return $dbDriver->getDbHelper()->limit($sql, $this->limitStart, $this->limitEnd);
373
    }
374
}
375