Passed
Push — master ( ee6bc9...8b4b12 )
by Jonas
02:07
created

RetrievesIntermediateTables::withPivot()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 4
dl 0
loc 15
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Staudenmeir\EloquentHasManyDeep;
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
    public function withIntermediate($class, array $columns = ['*'], $accessor = null)
27
    {
28
        $table = (new $class)->getTable();
29
30
        $accessor = $accessor ?: snake_case(class_basename($class));
31
32
        return $this->withPivot($table, $columns, $class, $accessor);
33
    }
34
35
    /**
36
     * Set the columns on a pivot table to retrieve.
37
     *
38
     * @param  string  $table
39
     * @param  array  $columns
40
     * @param  string  $class
41
     * @param  string|null  $accessor
42
     * @return $this
43
     */
44
    public function withPivot($table, array $columns = ['*'], $class = Pivot::class, $accessor = null)
45
    {
46
        if ($columns === ['*']) {
47
            $columns = $this->query->getConnection()->getSchemaBuilder()->getColumnListing($table);
48
        }
49
50
        $accessor = $accessor ?: $table;
51
52
        if (isset($this->intermediateTables[$accessor])) {
53
            $columns = array_merge($columns, $this->intermediateTables[$accessor]['columns']);
54
        }
55
56
        $this->intermediateTables[$accessor] = compact('table', 'columns', 'class');
57
58
        return $this;
59
    }
60
61
    /**
62
     * Get the intermediate columns for the relation.
63
     *
64
     * @return array
65
     */
66
    protected function intermediateColumns()
67
    {
68
        $columns = [];
69
70
        foreach ($this->intermediateTables as $accessor => $intermediateTable) {
71
            $prefix = $this->prefix($accessor);
72
73
            foreach ($intermediateTable['columns'] as $column) {
74
                $columns[] = $intermediateTable['table'].'.'.$column.' as '.$prefix.$column;
75
            }
76
        }
77
78
        return array_unique($columns);
79
    }
80
81
    /**
82
     * Hydrate the intermediate table relationship on the models.
83
     *
84
     * @param  array  $models
85
     * @return void
86
     */
87
    protected function hydrateIntermediateRelations(array $models)
88
    {
89
        $intermediateTables = $this->intermediateTables;
90
91
        ksort($intermediateTables);
92
93
        foreach ($intermediateTables as $accessor => $intermediateTable) {
94
            $prefix = $this->prefix($accessor);
95
96
            if (Str::contains($accessor, '.')) {
97
                list($path, $key) = preg_split('/\.(?=[^.]*$)/', $accessor);
98
            } else {
99
                list($path, $key) = [null, $accessor];
100
            }
101
102
            foreach ($models as $model) {
103
                $relation = $this->intermediateRelation($model, $intermediateTable, $prefix);
104
105
                data_get($model, $path)->setRelation($key, $relation);
106
            }
107
        }
108
    }
109
110
    /**
111
     * Get the intermediate relationship from the query.
112
     *
113
     * @param  \Illuminate\Database\Eloquent\Model  $model
114
     * @param  array  $intermediateTable
115
     * @param  string  $prefix
116
     * @return \Illuminate\Database\Eloquent\Model
117
     */
118
    protected function intermediateRelation(Model $model, array $intermediateTable, $prefix)
119
    {
120
        $attributes = $this->intermediateAttributes($model, $prefix);
121
122
        $class = $intermediateTable['class'];
123
124
        if ($class === Pivot::class) {
125
            return $class::fromAttributes($model, $attributes, $intermediateTable['table'], true);
126
        }
127
128
        if (is_subclass_of($class, Pivot::class)) {
129
            return $class::fromRawAttributes($model, $attributes, $intermediateTable['table'], true);
130
        }
131
132
        return (new $class)->newFromBuilder($attributes);
133
    }
134
135
    /**
136
     * Get the intermediate attributes from a model.
137
     *
138
     * @param  \Illuminate\Database\Eloquent\Model  $model
139
     * @param  string  $prefix
140
     * @return array
141
     */
142
    protected function intermediateAttributes(Model $model, $prefix)
143
    {
144
        $attributes = [];
145
146
        foreach ($model->getAttributes() as $key => $value) {
147
            if (strpos($key, $prefix) === 0) {
148
                $attributes[substr($key, strlen($prefix))] = $value;
149
150
                unset($model->$key);
151
            }
152
        }
153
154
        return $attributes;
155
    }
156
157
    /**
158
     * Get the intermediate column alias prefix.
159
     *
160
     * @param  string  $accessor
161
     * @return string
162
     */
163
    protected function prefix($accessor)
164
    {
165
        return '__'.$accessor.'__';
166
    }
167
}
168