Completed
Push — master ( a31142...a4e704 )
by Jonas
05:01
created

CreatesMergeViews::removeConstraints()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 9
ccs 7
cts 7
cp 1
crap 2
rs 10
1
<?php
2
3
namespace Staudenmeir\LaravelMergedRelations\Schema\Builders;
4
5
use Illuminate\Database\Eloquent\Relations\BelongsTo;
6
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
7
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
8
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
9
use Illuminate\Database\Eloquent\Relations\Relation;
10
use RuntimeException;
11
12
trait CreatesMergeViews
13
{
14
    /**
15
     * Create a view that merges relationships.
16
     *
17
     * @param string $name
18
     * @param \Illuminate\Database\Eloquent\Relations\Relation[] $relations
19
     * @param bool $duplicates
20
     * @param bool $orReplace
21
     * @return void
22
     */
23 24
    public function createMergeView($name, array $relations, $duplicates = true, $orReplace = false)
24
    {
25 24
        $this->removeConstraints($relations);
26
27 24
        $union = $duplicates ? 'unionAll' : 'union';
28
29 24
        $query = $this->getQuery($relations, $union);
30
31 24
        $this->createView($name, $query, null, $orReplace);
0 ignored issues
show
Bug introduced by
The method createView() does not exist on Staudenmeir\LaravelMerge...lders\CreatesMergeViews. Did you maybe mean createMergeView()? ( Ignorable by Annotation )

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

31
        $this->/** @scrutinizer ignore-call */ 
32
               createView($name, $query, null, $orReplace);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
32 24
    }
33
34
    /**
35
     * Create a view that merges relationships without duplicates.
36
     *
37
     * @param string $name
38
     * @param \Illuminate\Database\Eloquent\Relations\Relation[] $relations
39
     * @return void
40
     */
41 3
    public function createMergeViewWithoutDuplicates($name, array $relations)
42
    {
43 3
        $this->createMergeView($name, $relations, false);
44 3
    }
45
46
    /**
47
     * Create a view that merges relationships or replace an existing one.
48
     *
49
     * @param string $name
50
     * @param array $relations
51
     * @param bool $duplicates
52
     * @return void
53
     */
54 6
    public function createOrReplaceMergeView($name, array $relations, $duplicates = true)
55
    {
56 6
        $this->createMergeView($name, $relations, $duplicates, true);
57 6
    }
58
59
    /**
60
     * Create a view that merges relationships or replace an existing one without duplicates.
61
     *
62
     * @param string $name
63
     * @param array $relations
64
     * @return void
65
     */
66 3
    public function createOrReplaceMergeViewWithoutDuplicates($name, array $relations)
67
    {
68 3
        $this->createOrReplaceMergeView($name, $relations, false);
69 3
    }
70
71
    /**
72
     * Remove the foreign key constraints from the relationships.
73
     *
74
     * @param \Illuminate\Database\Eloquent\Relations\Relation[] $relations
75
     * @return void
76
     */
77 24
    protected function removeConstraints(array $relations)
78
    {
79 24
        foreach ($relations as $relation) {
80 24
            $foreignKey = $this->getForeignKey($relation);
81
82 24
            $relation->getQuery()->getQuery()->wheres = collect($relation->getQuery()->getQuery()->wheres)
83 24
                ->reject(function ($where) use ($foreignKey) {
84 24
                    return $where['column'] === $foreignKey;
85 24
                })->values()->all();
86
        }
87 24
    }
88
89
    /**
90
     * Get the merge query.
91
     *
92
     * @param \Illuminate\Database\Eloquent\Relations\Relation[] $relations
93
     * @param string $union
94
     * @return \Illuminate\Database\Eloquent\Builder
95
     */
96 24
    protected function getQuery(array $relations, $union)
97
    {
98 24
        $grammar = $this->connection->getQueryGrammar();
99
100 24
        $pdo = $this->connection->getPdo();
101
102 24
        $columns = $this->getColumns($relations);
103
104 24
        $allColumns = array_unique(array_merge(...array_values($columns)));
105
106 24
        $query = null;
107
108 24
        foreach ($relations as $i => $relation) {
109 24
            $relationQuery = $relation->getQuery();
110
111 24
            $from = $relationQuery->getQuery()->from;
112
113 24
            $foreignKey = $this->getForeignKey($relation);
114
115 24
            $placeholders = [];
116
117 24
            foreach ($allColumns as $column) {
118 24
                if (in_array($column, $columns[$from])) {
119 24
                    $relationQuery->addSelect($from.'.'.$column);
120
                } else {
121 6
                    $relationQuery->selectRaw('null as '.$grammar->wrap($column));
122
123 6
                    $placeholders[] = $column;
124
                }
125
            }
126
127 24
            $relationQuery->selectRaw($grammar->wrap($foreignKey).' as laravel_foreign_key')
128 24
                ->selectRaw($pdo->quote(get_class($relation->getRelated())).' as laravel_model')
129 24
                ->selectRaw($pdo->quote(implode(',', $placeholders)).' as laravel_placeholders')
130 24
                ->selectRaw($pdo->quote(implode(',', array_keys($relationQuery->getEagerLoads()))).' as laravel_with');
131
132 24
            if (!$query) {
133 24
                $query = $relationQuery;
134
            } else {
135 21
                $query->$union($relationQuery);
136
            }
137
        }
138
139 24
        return $query;
140
    }
141
142
    /**
143
     * Get the columns of all relationship tables.
144
     *
145
     * @param \Illuminate\Database\Eloquent\Relations\Relation[] $relations
146
     * @return array
147
     */
148 24
    protected function getColumns(array $relations)
149
    {
150 24
        $columns = [];
151
152 24
        foreach ($relations as $relation) {
153 24
            $table = $relation->getQuery()->getQuery()->from;
154
155 24
            if (!isset($columns[$table])) {
156 24
                $listing = $relation->getRelated()->getConnection()->getSchemaBuilder()->getColumnListing($table);
157
158 24
                $columns[$table] = $listing;
159
            }
160
        }
161
162 24
        return $columns;
163
    }
164
165
    /**
166
     * Get the foreign key of a relationship.
167
     *
168
     * @param \Illuminate\Database\Eloquent\Relations\Relation $relation
169
     * @return string
170
     */
171 24
    protected function getForeignKey(Relation $relation)
172
    {
173 24
        if ($relation instanceof BelongsTo) {
174 3
            return $relation->getQualifiedOwnerKeyName();
175
        }
176
177 21
        if ($relation instanceof BelongsToMany) {
178 6
            return $relation->getQualifiedForeignPivotKeyName();
179
        }
180
181 15
        if ($relation instanceof HasManyThrough) {
182 15
            return $relation->getQualifiedFirstKeyName();
183
        }
184
185 15
        if ($relation instanceof HasOneOrMany) {
186 15
            return $relation->getQualifiedForeignKeyName();
187
        }
188
189
        throw new RuntimeException('This type of relationship is not supported.'); // @codeCoverageIgnore
190
    }
191
}
192