Passed
Push — master ( 1b22d3...272168 )
by Jonas
10:52
created

CreatesMergeViews::getForeignKey()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 9
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 19
ccs 9
cts 9
cp 1
crap 5
rs 9.6111
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 48
    public function createMergeView($name, array $relations, $duplicates = true, $orReplace = false)
24
    {
25 48
        $this->removeConstraints($relations);
26
27 48
        $union = $duplicates ? 'unionAll' : 'union';
28
29 48
        $query = $this->getQuery($relations, $union);
30
31 48
        $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 48
    }
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 4
    public function createMergeViewWithoutDuplicates($name, array $relations)
42
    {
43 4
        $this->createMergeView($name, $relations, false);
44 4
    }
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 8
    public function createOrReplaceMergeView($name, array $relations, $duplicates = true)
55
    {
56 8
        $this->createMergeView($name, $relations, $duplicates, true);
57 8
    }
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 4
    public function createOrReplaceMergeViewWithoutDuplicates($name, array $relations)
67
    {
68 4
        $this->createOrReplaceMergeView($name, $relations, false);
69 4
    }
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 48
    protected function removeConstraints(array $relations)
78
    {
79 48
        foreach ($relations as $relation) {
80 48
            $foreignKey = $this->getOriginalForeignKey($relation);
81
82 48
            $relation->getQuery()->getQuery()->wheres = collect($relation->getQuery()->getQuery()->wheres)
83 48
                ->reject(function ($where) use ($foreignKey) {
84 48
                    return $where['column'] === $foreignKey;
85 48
                })->values()->all();
86
        }
87 48
    }
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 48
    protected function getQuery(array $relations, $union)
97
    {
98 48
        $grammar = $this->connection->getQueryGrammar();
99
100 48
        $pdo = $this->connection->getPdo();
101
102 48
        $columns = $this->getColumns($relations);
103
104 48
        $allColumns = array_unique(array_merge(...array_values($columns)));
105
106 48
        $query = null;
107
108 48
        foreach ($relations as $relation) {
109 48
            $relationQuery = $relation->getQuery();
110
111 48
            if ($relation instanceof BelongsTo) {
112 4
                $relation->distinct()
113 4
                         ->join(
114 4
                             $relation->getParent()->getTable(),
115 4
                             $relation->getQualifiedForeignKeyName(),
116 4
                             '=',
117 4
                             $relation->getQualifiedOwnerKeyName()
118
                         );
119
            }
120
121 48
            $from = $relationQuery->getQuery()->from;
122
123 48
            $foreignKey = $this->getMergedForeignKey($relation);
124
125 48
            $placeholders = [];
126
127 48
            foreach ($allColumns as $column) {
128 48
                if (in_array($column, $columns[$from])) {
129 48
                    $relationQuery->addSelect($from.'.'.$column);
130
                } else {
131 24
                    $relationQuery->selectRaw('null as '.$grammar->wrap($column));
132
133 24
                    $placeholders[] = $column;
134
                }
135
            }
136
137 48
            $relationQuery->selectRaw($grammar->wrap($foreignKey).' as laravel_foreign_key')
138 48
                ->selectRaw($pdo->quote(get_class($relation->getRelated())).' as laravel_model')
139 48
                ->selectRaw($pdo->quote(implode(',', $placeholders)).' as laravel_placeholders')
140 48
                ->selectRaw($pdo->quote(implode(',', array_keys($relationQuery->getEagerLoads()))).' as laravel_with');
141
142 48
            if (!$query) {
143 48
                $query = $relationQuery;
144
            } else {
145 44
                $query->$union($relationQuery);
146
            }
147
        }
148
149 48
        return $query;
150
    }
151
152
    /**
153
     * Get the columns of all relationship tables.
154
     *
155
     * @param \Illuminate\Database\Eloquent\Relations\Relation[] $relations
156
     * @return array
157
     */
158 48
    protected function getColumns(array $relations)
159
    {
160 48
        $columns = [];
161
162 48
        foreach ($relations as $relation) {
163 48
            $table = $relation->getQuery()->getQuery()->from;
164
165 48
            if (!isset($columns[$table])) {
166 48
                $listing = $relation->getRelated()->getConnection()->getSchemaBuilder()->getColumnListing($table);
167
168 48
                $columns[$table] = $listing;
169
            }
170
        }
171
172 48
        return $columns;
173
    }
174
175
    /**
176
     * Get the foreign key of the original relationship.
177
     *
178
     * @param \Illuminate\Database\Eloquent\Relations\Relation $relation
179
     * @return string
180
     */
181 48
    protected function getOriginalForeignKey(Relation $relation)
182
    {
183 48
        if ($relation instanceof BelongsTo) {
184 4
            return $relation->getQualifiedOwnerKeyName();
185
        }
186
187 44
        if ($relation instanceof BelongsToMany) {
188 24
            return $relation->getQualifiedForeignPivotKeyName();
189
        }
190
191 20
        if ($relation instanceof HasManyThrough) {
192 20
            return $relation->getQualifiedFirstKeyName();
193
        }
194
195 20
        if ($relation instanceof HasOneOrMany) {
196 20
            return $relation->getQualifiedForeignKeyName();
197
        }
198
199
        throw new RuntimeException('This type of relationship is not supported.'); // @codeCoverageIgnore
200
    }
201
202
    /**
203
     * Get the foreign key for the merged relationship.
204
     *
205
     * @param \Illuminate\Database\Eloquent\Relations\Relation $relation
206
     * @return string
207
     */
208 48
    protected function getMergedForeignKey(Relation $relation)
209
    {
210 48
        if ($relation instanceof BelongsTo) {
211 4
            return $relation->getQualifiedParentKeyName();
212
        }
213
214 44
        return $this->getOriginalForeignKey($relation);
215
    }
216
}
217