Completed
Push — master ( efafaa...1ce8a3 )
by Avtandil
05:26
created

ManyToManyPreload::scopeLimitPerGroupViaSubQuery()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 2
dl 0
loc 46
ccs 0
cts 27
cp 0
crap 12
rs 9.1781
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Longman\LaravelLodash\Eloquent;
6
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Database\Eloquent\Model;
9
10
use function array_unshift;
11
use function explode;
12
use function str_replace;
13
14
/**
15
 * @method $this limitPerGroupViaSubQuery(int $limit = 10)
16
 * @method $this limitPerGroupViaUnion(int $limit = 10, array $pivotColumns = [])
17
 */
18
trait ManyToManyPreload
19
{
20
    /**
21
     * Get the table associated with the model.
22
     *
23
     * @return string
24
     */
25
    abstract public function getTable();
26
27
    /**
28
     * Get the database connection for the model.
29
     *
30
     * @return \Illuminate\Database\Connection
31
     */
32
    abstract public function getConnection();
33
34
    /**
35
     * Get a new query builder that doesn't have any global scopes.
36
     *
37
     * @return \Illuminate\Database\Eloquent\Builder|static
38
     */
39
    abstract public function newQueryWithoutScopes();
40
41
    public function scopeLimitPerGroupViaSubQuery(Builder $query, int $limit = 10): Model
42
    {
43
        $table = $this->getTable();
44
        $queryKeyColumn = $query->getQuery()->wheres[0]['column'];
45
        $join = $query->getQuery()->joins;
46
        $newQuery = $this->newQueryWithoutScopes();
47
        $connection = $this->getConnection();
48
49
        // Initialize MySQL variables inline
50
        $newQuery->from($connection->raw('(select @num:=0, @group:=0) as `vars`, ' . $this->quoteColumn($table)));
51
52
        // If no columns already selected, let's select *
53
        if (! $query->getQuery()->columns) {
54
            $newQuery->select($table . '.*');
55
        }
56
57
        // Make sure column aliases are unique
58
        $groupAlias = $table . '_grp';
59
        $numAlias = $table . '_rn';
60
61
        // Apply mysql variables
62
        $newQuery->addSelect($connection->raw(
63
            "@num := if(@group = {$this->quoteColumn($queryKeyColumn)}, @num+1, 1) as `{$numAlias}`, @group := {$this->quoteColumn($queryKeyColumn)} as `{$groupAlias}`",
64
        ));
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected ')'
Loading history...
65
66
        // Make sure first order clause is the group order
67
        $newQuery->getQuery()->orders = (array) $query->getQuery()->orders;
68
        array_unshift($newQuery->getQuery()->orders, [
69
            'column'    => $queryKeyColumn,
70
            'direction' => 'asc',
71
        ]);
72
73
        if ($join) {
74
            $leftKey = explode('.', $queryKeyColumn)[1];
75
            $leftKeyColumn = "`{$table}`.`{$leftKey}`";
76
            $newQuery->addSelect($queryKeyColumn);
77
            $newQuery->mergeBindings($query->getQuery());
78
            $newQuery->getQuery()->joins = (array) $query->getQuery()->joins;
79
            $query->whereRaw("{$leftKeyColumn} = {$this->quoteColumn($queryKeyColumn)}");
80
        }
81
82
        $query->from($connection->raw("({$newQuery->toSql()}) as `{$table}`"))
83
            ->where($numAlias, '<=', $limit);
84
85
        return $this;
86
    }
87
88
    public function scopeLimitPerGroupViaUnion(Builder $query, int $limit = 10, array $pivotColumns = []): Model
89
    {
90
        $table = $this->getTable();
91
        $queryKeyColumn = $query->getQuery()->wheres[0]['column'];
92
        $joins = $query->getQuery()->joins;
93
        $connection = $this->getConnection();
94
95
        $queryKeyValues = $query->getQuery()->wheres[0]['values'];
96
        $pivotTable = explode('.', $queryKeyColumn)[0];
97
98
        $joinLeftColumn = $joins[0]->wheres[0]['first'];
99
        $joinRightColumn = $joins[0]->wheres[0]['second'];
100
        $joinOperator = $joins[0]->wheres[0]['operator'];
101
102
        // Remove extra wheres
103
        $wheres = $query->getQuery()->wheres;
104
        $bindings = $query->getQuery()->bindings;
105
        foreach ($wheres as $key => $where) {
106
            if (! isset($where['column']) || $where['column'] !== $queryKeyColumn) {
107
                continue;
108
            }
109
110
            //$count = count($where['values']);
111
            unset($wheres[$key]);
112
        }
113
        $groups = $query->getQuery()->groups;
114
        $orders = $query->getQuery()->orders;
115
116
        foreach ($queryKeyValues as $value) {
117
            if (! isset($unionQuery1)) {
118
                $unionQuery1 = $connection->table($pivotTable)
119
                    ->select([$table . '.*'])
120
                    ->join($table, $joinLeftColumn, $joinOperator, $joinRightColumn)
121
                    ->where($queryKeyColumn, '=', $value)
122
                    ->limit($limit);
123
                if (! empty($groups)) {
124
                    foreach ($groups as $group) {
125
                        $unionQuery1->groupBy($group);
126
                    }
127
                }
128
129
                if (! empty($orders)) {
130
                    foreach ($orders as $order) {
131
                        $unionQuery1->orderBy($order['column'], $order['direction']);
132
                    }
133
                }
134
135
                // Merge wheres
136
                $unionQuery1->mergeWheres($wheres, $bindings);
137
            } else {
138
                $select = [
139
                    $table . '.*',
140
                ];
141
142
                foreach ($pivotColumns as $pivotColumn) {
143
                    $select[] = $pivotTable . '.' . $pivotColumn . ' as pivot_' . $pivotColumn;
144
                }
145
146
                $unionQuery2 = $connection->table($pivotTable)
147
                    ->select($select)
148
                    ->join($table, $joinLeftColumn, $joinOperator, $joinRightColumn)
149
                    ->where($queryKeyColumn, '=', $value)
150
                    ->limit($limit);
151
                if (! empty($groups)) {
152
                    foreach ($groups as $group) {
153
                        $unionQuery2->groupBy($group);
154
                    }
155
                }
156
157
                if (! empty($orders)) {
158
                    foreach ($orders as $order) {
159
                        $unionQuery2->orderBy($order['column'], $order['direction']);
160
                    }
161
                }
162
163
                // Merge wheres
164
                $unionQuery2->mergeWheres($wheres, $bindings);
165
166
                $unionQuery1->unionAll($unionQuery2);
167
            }
168
        }
169
170
        if (! isset($unionQuery1)) {
171
            throw new InvalidArgumentException('Union query does not found');
172
        }
173
174
        $query->setQuery($unionQuery1);
175
176
        return $this;
177
    }
178
179
    private function quoteColumn(string $column): string
180
    {
181
182
        return '`' . str_replace('.', '`.`', $column) . '`';
183
    }
184
}
185