Issues (12)

src/Eloquent/Relations/MergedRelation.php (2 issues)

1
<?php
2
3
namespace Staudenmeir\LaravelMergedRelations\Eloquent\Relations;
4
5
use Illuminate\Database\Eloquent\Collection;
6
use Illuminate\Database\Eloquent\Relations\HasMany;
7
use Illuminate\Database\Eloquent\Relations\Pivot;
8
9
class MergedRelation extends HasMany
10
{
11
    /**
12
     * Get the results of the relationship.
13
     *
14
     * @return mixed
15
     */
16
    public function getResults()
17
    {
18
        $results = !is_null($this->getParentKey())
19
            ? $this->get()
20
            : $this->related->newCollection();
21
22
        foreach ($results as $result) {
23
            unset($result->laravel_foreign_key);
24
        }
25
26
        return $results;
27
    }
28
29
    /**
30
     * Execute the query as a "select" statement.
31
     *
32
     * @param array $columns
33
     * @return \Illuminate\Database\Eloquent\Collection
34
     */
35
    public function get($columns = ['*'])
36
    {
37
        $builder = $this->prepareQueryBuilder($columns);
38
39
        $models = $builder->getModels();
40
41
        if (count($models) > 0) {
42
            $models = $builder->eagerLoadRelations($models);
43
        }
44
45
        $this->hydratePivotRelations($models);
46
47
        return $this->related->newCollection($models);
48
    }
49
50
    /**
51
     * Execute the query and get the first related model.
52
     *
53
     * @param array $columns
54
     * @return mixed
55
     */
56
    public function first($columns = ['*'])
57
    {
58
        $results = $this->take(1)->get($columns);
59
60
        return count($results) > 0 ? $results->first() : null;
61
    }
62
63
    /**
64
     * Get a paginator for the "select" statement.
65
     *
66
     * @param int|null $perPage
67
     * @param array $columns
68
     * @param string $pageName
69
     * @param int $page
70
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
71
     */
72
    public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
73
    {
74
        $this->query->addSelect(
75
            $this->shouldSelect($columns)
76
        );
77
78
        $paginator = $this->query->paginate($perPage, $columns, $pageName, $page);
79
80
        $this->hydratePivotRelations(
81
            $paginator->items()
82
        );
83
84
        return $paginator;
85
    }
86
87
    /**
88
     * Prepare the query builder for query execution.
89
     *
90
     * @param array $columns
91
     * @return \Illuminate\Database\Eloquent\Builder
92
     */
93
    protected function prepareQueryBuilder($columns = ['*'])
94
    {
95
        $builder = $this->query->applyScopes();
96
97
        $columns = $builder->getQuery()->columns ? [] : $columns;
98
99
        return $builder->addSelect(
0 ignored issues
show
Bug Best Practice introduced by
The expression return $builder->addSele...shouldSelect($columns)) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
100
            $this->shouldSelect($columns)
101
        );
102
    }
103
104
    /**
105
     * Get the select columns for the relation query.
106
     *
107
     * @param array $columns
108
     * @return array
109
     */
110
    protected function shouldSelect(array $columns = ['*'])
111
    {
112
        if ($columns === ['*']) {
113
            return $columns;
114
        }
115
116
        return array_merge(
117
            $columns,
118
            ['laravel_foreign_key', 'laravel_model', 'laravel_placeholders', 'laravel_with']
119
        );
120
    }
121
122
    /**
123
     * Hydrate the pivot table relationships on the models.
124
     *
125
     * @param array $models
126
     * @return void
127
     */
128
    protected function hydratePivotRelations(array $models): void
129
    {
130
        if (!$models) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $models of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
131
            return;
132
        }
133
134
        $pivotTables = $this->getPivotTables($models);
135
136
        if (!$pivotTables) {
137
            return;
138
        }
139
140
        foreach ($models as $model) {
141
            $attributes = $model->getAttributes();
142
143
            foreach ($pivotTables as $accessor => $table) {
144
                $pivotAttributes = [];
145
146
                foreach ($table['columns'] as $column) {
147
                    $key = "__{$table['table']}__{$accessor}__$column";
148
149
                    $pivotAttributes[$column] = $attributes[$key];
150
151
                    unset($model->$key);
152
                }
153
154
                $relation = Pivot::fromAttributes($model, $pivotAttributes, $table['table'], true);
155
156
                $model->setRelation($accessor, $relation);
157
            }
158
        }
159
    }
160
161
    /**
162
     * Get the pivot tables from the models.
163
     *
164
     * @param array $models
165
     * @return array
166
     */
167
    protected function getPivotTables(array $models): array
168
    {
169
        $tables = [];
170
171
        foreach (array_keys($models[0]->getAttributes()) as $key) {
172
            if (str_starts_with($key, '__')) {
173
                [, $table, $accessor, $column] = explode('__', $key);
174
175
                if (isset($tables[$accessor])) {
176
                    $tables[$accessor]['columns'][] = $column;
177
                } else {
178
                    $tables[$accessor] = [
179
                        'columns' => [$column],
180
                        'table' => $table,
181
                    ];
182
                }
183
            }
184
        }
185
186
        return $tables;
187
    }
188
189
    /**
190
     * Match the eagerly loaded results to their parents.
191
     *
192
     * @param array $models
193
     * @param \Illuminate\Database\Eloquent\Collection $results
194
     * @param string $relation
195
     * @return array
196
     */
197
    public function match(array $models, Collection $results, $relation)
198
    {
199
        $models = parent::match($models, $results, $relation);
200
201
        foreach ($results as $result) {
202
            unset($result->laravel_foreign_key);
203
        }
204
205
        return $models;
206
    }
207
}
208