Completed
Push — master ( d58822...10e381 )
by Jonas
15:56
created

RetrievesIntermediateTables::withIntermediate()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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