Passed
Push — master ( a22e59...74f1f1 )
by Ryosuke
17:33 queued 15:53
created

Paginator::orderByAsc()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Lampager\Idiorm;
4
5
use Lampager\AbstractProcessor;
6
use Lampager\Concerns\HasProcessor;
7
use Lampager\Contracts\Cursor;
8
use Lampager\Contracts\Formatter;
9
use Lampager\Idiorm\Concerns\HasSnakeAliases;
10
use Lampager\Paginator as BasePaginator;
11
use Lampager\Query;
12
use Lampager\Query\Select;
13
use Lampager\Query\SelectOrUnionAll;
14
use Lampager\Query\UnionAll;
15
use ORM;
16
17
/**
18
 * Class Paginator
19
 *
20
 * @method $this order_by(string $column, null|string $order) Add cursor parameter name for ORDER BY statement.
21
 * @method $this order_by_asc(string $column) Add cursor parameter name for ORDER BY statement.
22
 * @method $this order_by_desc(string $column) Add cursor parameter name for ORDER BY statement.
23
 * @method $this clear_order_by() Clear all cursor parameters.
24
 * @method $this from_array(bool[]|int[]|string[] $options) Define options from an associative array.
25
 * @method $this use_formatter(callable|Formatter|string $formatter) Use custom formatter.
26
 * @method $this restore_formatter() Restore default formatter.
27
 * @method $this use_processor(AbstractProcessor|string $processor) Use custom processor.
28
 *
29
 * @see BasePaginator, HasProcessor
30
 */
31
class Paginator extends BasePaginator
32
{
33
    use HasProcessor, HasSnakeAliases;
34
35
    public static $temporaryTableName = 'temporary_table';
36
37
    /**
38
     * @var \ReflectionClass
39
     */
40
    protected $reflector;
41
42
    /**
43
     * Paginator constructor wrapper.
44
     *
45
     * @param  ORM    $builder
46
     * @return static
47
     */
48 34
    public static function create(ORM $builder)
49
    {
50 34
        return new static($builder);
51
    }
52
53
    /**
54
     * Paginator constructor.
55
     *
56
     * @param ORM $builder
57
     */
58 34
    public function __construct(ORM $builder)
59
    {
60 34
        $this->builder = $builder;
61 34
        $this->processor = new Processor();
62 34
        $this->reflector = new \ReflectionClass(ORM::class);
63 34
    }
64
65
    /**
66
     * Add cursor parameter name for ORDER BY statement.
67
     *
68
     * @param  string $column
69
     * @return $this
70
     */
71 20
    public function orderByAsc($column)
72
    {
73 20
        return $this->orderBy($column);
74
    }
75
76
    /**
77
     * Build ORM instance from Query config.
78
     *
79
     * @param  Query $query
80
     * @return ORM
81
     */
82 34
    public function transform(Query $query)
83
    {
84 34
        return $this->compileSelectOrUnionAll($query->selectOrUnionAll());
85
    }
86
87
    /**
88
     * Configure -> Transform.
89
     *
90
     * @param  Cursor|int[]|string[]
91
     * @param  mixed $cursor
92
     * @return ORM
93
     */
94 13
    public function build($cursor = [])
95
    {
96 13
        return $this->transform($this->configure($cursor));
97
    }
98
99
    /**
100
     * Execute query and paginate them.
101
     *
102
     * @param  Cursor|int[]|string[] $cursor
103
     * @return PaginationResult
104
     */
105 21
    public function paginate($cursor = [])
106
    {
107 21
        $query = $this->configure($cursor);
108 21
        return $this->process($query, $this->transform($query)->findMany());
109
    }
110
111
    /**
112
     * @param  string              $name
113
     * @return \ReflectionProperty
114
     */
115 34
    protected function ormProperty($name)
116
    {
117 34
        $property = $this->reflector->getProperty($name);
118 34
        $property->setAccessible(true);
119 34
        return $property;
120
    }
121
122
    /**
123
     * @param  SelectOrUnionAll $selectOrUnionAll
124
     * @return ORM
125
     */
126 34
    protected function compileSelectOrUnionAll(SelectOrUnionAll $selectOrUnionAll)
127
    {
128 34
        if ($selectOrUnionAll instanceof Select) {
129 12
            return $this->compileSelect($selectOrUnionAll);
130
        }
131 22
        if ($selectOrUnionAll instanceof UnionAll) {
132 22
            return $this->compileSelect($selectOrUnionAll->supportQuery(), $selectOrUnionAll->mainQuery());
133
        }
134
        // @codeCoverageIgnoreStart
135
        throw new \LogicException('Unreachable here');
136
        // @codeCoverageIgnoreEnd
137
    }
138
139
    /**
140
     * @param  Select[] $selects
141
     * @return ORM
142
     */
143 34
    protected function compileSelect(Select ...$selects)
144
    {
145 34
        $bindings = [];
146 34
        $expressions = [];
147 34
        $property = $this->ormProperty('_values');
148 34
        foreach ($selects as $select) {
149 34
            $builder = clone $this->builder;
150
            $this
151 34
                ->compileWhere($builder, $select)
152 34
                ->compileOrderBy($builder, $select)
153 34
                ->compileLimit($builder, $select);
154 34
            $expressions[] = $builder->_buildSelect();
155 34
            $bindings = array_merge($bindings, $property->getValue($builder));
156
        }
157 34
        $builder = clone $this->builder;
158 34
        $table = $builder->_quoteIdentifier(static::$temporaryTableName);
159 34
        return $builder->rawQuery(
160 34
            count($expressions) > 1
161 22
            ? 'SELECT * FROM (' . implode(") $table UNION ALL SELECT * FROM (", $expressions) . ") $table"
162 34
            : current($expressions),
163
            $bindings
164
        );
165
    }
166
167
    /**
168
     * @param $builder ORM
169
     * @param  Select $select
170
     * @return $this
171
     */
172 34
    protected function compileWhere($builder, Select $select)
173
    {
174 34
        $bindings = [];
175 34
        $expressionGroups = [];
176 34
        foreach ($select->where() as $i => $group) {
177 22
            $expressions = [];
178 22
            foreach ($group as $condition) {
179 22
                $expressions[] = "{$builder->_quoteIdentifier($condition->left())} {$condition->comparator()} ?";
180 22
                $bindings[] = $condition->right();
181
            }
182 22
            $expressionGroups[] = implode(' AND ', $expressions);
183
        }
184 34
        if ($expressionGroups) {
185 22
            $builder->whereRaw('(' . implode(' OR ', $expressionGroups) . ')', $bindings);
186
        }
187 34
        return $this;
188
    }
189
190
    /**
191
     * @param $builder ORM
192
     * @param  Select $select
193
     * @return $this
194
     */
195 34
    protected function compileOrderBy($builder, Select $select)
196
    {
197 34
        foreach ($select->orders() as $order) {
198 34
            $builder->{'orderBy' . ucfirst($order->order())}($order->column());
199
        }
200 34
        return $this;
201
    }
202
203
    /**
204
     * @param $builder ORM
205
     * @param  Select $select
206
     * @return $this
207
     */
208 34
    protected function compileLimit($builder, Select $select)
209
    {
210 34
        $builder->limit($select->limit()->toInteger());
211 34
        return $this;
212
    }
213
}
214