Passed
Push — master ( e29b00...905a6f )
by Jonas
13:26
created

intermediateRelation()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 6
nop 3
dl 0
loc 22
ccs 11
cts 11
cp 1
crap 4
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
namespace Staudenmeir\EloquentHasManyDeep\Eloquent\Relations\Traits;
4
5
use Illuminate\Database\Eloquent\Model;
6
use Illuminate\Database\Eloquent\Relations\Pivot;
7
use Illuminate\Support\Str;
8
9
trait RetrievesIntermediateTables
10
{
11
    /**
12
     * The intermediate tables to retrieve.
13
     *
14
     * @var array
15
     */
16
    protected $intermediateTables = [];
17
18
    /**
19
     * Set the columns on an intermediate table to retrieve.
20
     *
21
     * @param string $class
22
     * @param array $columns
23
     * @param string|null $accessor
24
     * @return $this
25
     */
26 10
    public function withIntermediate($class, array $columns = ['*'], $accessor = null)
27
    {
28
        /** @var \Illuminate\Database\Eloquent\Model $instance */
29 10
        $instance = new $class();
30
31 10
        $accessor = $accessor ?: Str::snake(class_basename($class));
32
33 10
        return $this->withPivot($instance->getTable(), $columns, $class, $accessor);
34
    }
35
36
    /**
37
     * Set the columns on a pivot table to retrieve.
38
     *
39
     * @param string $table
40
     * @param array $columns
41
     * @param string $class
42
     * @param string|null $accessor
43
     * @param callable|null $postProcessor
44
     * @return $this
45
     */
46 19
    public function withPivot(
47
        $table,
48
        array $columns = ['*'],
49
        $class = Pivot::class,
50
        $accessor = null,
51
        callable $postProcessor = null
52
    ) {
53 19
        if ($columns === ['*']) {
54 10
            $columns = $this->query->getConnection()->getSchemaBuilder()->getColumnListing($table);
55
        }
56
57 19
        $accessor = $accessor ?: $table;
58
59 19
        if (isset($this->intermediateTables[$accessor])) {
60 2
            $columns = array_merge($columns, $this->intermediateTables[$accessor]['columns']);
61
        }
62
63 19
        $this->intermediateTables[$accessor] = compact('table', 'columns', 'class', 'postProcessor');
64
65 19
        return $this;
66
    }
67
68
    /**
69
     * Get the intermediate columns for the relation.
70
     *
71
     * @return array
72
     */
73 181
    protected function intermediateColumns()
74
    {
75 181
        $columns = [];
76
77 181
        foreach ($this->intermediateTables as $accessor => $intermediateTable) {
78 18
            $prefix = $this->prefix($accessor);
79
80 18
            foreach ($intermediateTable['columns'] as $column) {
81 18
                $columns[] = $intermediateTable['table'].'.'.$column.' as '.$prefix.$column;
82
            }
83
        }
84
85 181
        return array_unique($columns);
86
    }
87
88
    /**
89
     * Hydrate the intermediate table relationship on the models.
90
     *
91
     * @param array $models
92
     * @return void
93
     */
94 181
    protected function hydrateIntermediateRelations(array $models)
95
    {
96 181
        $intermediateTables = $this->intermediateTables;
97
98 181
        ksort($intermediateTables);
99
100 181
        foreach ($intermediateTables as $accessor => $intermediateTable) {
101 18
            $prefix = $this->prefix($accessor);
102
103 18
            if (str_contains($accessor, '.')) {
104 2
                [$path, $key] = preg_split('/\.(?=[^.]*$)/', $accessor);
105
            } else {
106 18
                [$path, $key] = [null, $accessor];
107
            }
108
109 18
            foreach ($models as $model) {
110 18
                $relation = $this->intermediateRelation($model, $intermediateTable, $prefix);
111
112 18
                data_get($model, $path)->setRelation($key, $relation);
113
            }
114
        }
115
    }
116
117
    /**
118
     * Get the intermediate relationship from the query.
119
     *
120
     * @param \Illuminate\Database\Eloquent\Model $model
121
     * @param array $intermediateTable
122
     * @param string $prefix
123
     * @return \Illuminate\Database\Eloquent\Model
124
     */
125 18
    protected function intermediateRelation(Model $model, array $intermediateTable, $prefix)
126
    {
127 18
        $attributes = $this->intermediateAttributes($model, $prefix);
128
129 18
        if ($intermediateTable['postProcessor']) {
130 4
            $attributes = $intermediateTable['postProcessor']($model, $attributes);
131
        }
132
133 18
        $class = $intermediateTable['class'];
134
135 18
        if ($class === Pivot::class) {
136 6
            return $class::fromAttributes($model, $attributes, $intermediateTable['table'], true);
137
        }
138
139 12
        if (is_subclass_of($class, Pivot::class)) {
140 2
            return $class::fromRawAttributes($model, $attributes, $intermediateTable['table'], true);
141
        }
142
143
        /** @var \Illuminate\Database\Eloquent\Model $instance */
144 10
        $instance = new $class();
145
146 10
        return $instance->newFromBuilder($attributes);
147
    }
148
149
    /**
150
     * Get the intermediate attributes from a model.
151
     *
152
     * @param \Illuminate\Database\Eloquent\Model $model
153
     * @param string $prefix
154
     * @return array
155
     */
156 18
    protected function intermediateAttributes(Model $model, $prefix)
157
    {
158 18
        $attributes = [];
159
160 18
        foreach ($model->getAttributes() as $key => $value) {
161 18
            if (strpos($key, $prefix) === 0) {
162 18
                $attributes[substr($key, strlen($prefix))] = $value;
163
164 18
                unset($model->$key);
165
            }
166
        }
167
168 18
        return $attributes;
169
    }
170
171
    /**
172
     * Get the intermediate column alias prefix.
173
     *
174
     * @param string $accessor
175
     * @return string
176
     */
177 18
    protected function prefix($accessor)
178
    {
179 18
        return '__'.$accessor.'__';
180
    }
181
182
    /**
183
     * Get the intermediate tables.
184
     *
185
     * @return array
186
     */
187
    public function getIntermediateTables(): array
188
    {
189
        return $this->intermediateTables;
190
    }
191
}
192