HandleBrowsers::getFormFieldsForRelatedBrowser()   A
last analyzed

Complexity

Conditions 4
Paths 1

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 1
nop 3
dl 0
loc 15
ccs 0
cts 2
cp 0
crap 20
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace A17\Twill\Repositories\Behaviors;
4
5
use A17\Twill\Models\Behaviors\HasMedias;
6
use Illuminate\Database\Eloquent\Model as EloquentModel;
7
use Illuminate\Database\Eloquent\Relations\BelongsTo;
8
use Illuminate\Database\Eloquent\Relations\HasMany;
9
use Illuminate\Database\Eloquent\Relations\HasOne;
10
use Illuminate\Support\Arr;
11
use Illuminate\Support\Str;
12
13
trait HandleBrowsers
14
{
15
    /**
16
     * All browsers used in the model, as an array of browser names:
17
     * [
18
     *     'books',
19
     *     'publications'
20
     * ]
21
     *
22
     * When only the browser name is given, the rest of the parameters are inferred from the name.
23
     * The parameters can also be overridden with an array:
24
     * [
25
     *     'books',
26
     *     'publication' => [
27
     *         'routePrefix' => 'collections',
28
     *         'titleKey' => 'name'
29
     *     ]
30
     * ]
31
     *
32
     * @var array
33
     */
34
    protected $browsers = [];
35
36 31
    /**
37
     * @param \A17\Twill\Models\Model $object
38 31
     * @param array $fields
39
     * @return void
40
     */
41 31
    public function afterSaveHandleBrowsers($object, $fields)
42
    {
43
        foreach ($this->getBrowsers() as $browser) {
44
            $this->updateBrowser($object, $fields, $browser['relation'], $browser['positionAttribute'], $browser['browserName']);
45
        }
46
    }
47
48 6
    /**
49
     * @param \A17\Twill\Models\Model $object
50 6
     * @param array $fields
51
     * @return array
52
     */
53
    public function getFormFieldsHandleBrowsers($object, $fields)
54 6
    {
55
        foreach ($this->getBrowsers() as $browser) {
56
            $relation = $browser['relation'];
57
            if (collect($object->$relation)->isNotEmpty()) {
58
                $fields['browsers'][$browser['browserName']] = $this->getFormFieldsForBrowser($object, $relation, $browser['routePrefix'], $browser['titleKey'], $browser['moduleName']);
59
            }
60
        }
61
62
        return $fields;
63
    }
64
65
    /**
66
     * @param \A17\Twill\Models\Model $object
67
     * @param array $fields
68
     * @param string $relationship
69
     * @param string $positionAttribute
70
     * @param string|null $browserName
71
     * @param array $pivotAttributes
72
     * @return void
73
     */
74
    public function updateBrowser($object, $fields, $relationship, $positionAttribute = 'position', $browserName = null, $pivotAttributes = [])
75
    {
76
        $browserName = $browserName ?? $relationship;
77
        $fieldsHasElements = isset($fields['browsers'][$browserName]) && !empty($fields['browsers'][$browserName]);
78
        $relatedElements = $fieldsHasElements ? $fields['browsers'][$browserName] : [];
79
80
        $relatedElementsWithPosition = [];
81
        $position = 1;
82
83
        foreach ($relatedElements as $relatedElement) {
84
            $relatedElementsWithPosition[$relatedElement['id']] = [$positionAttribute => $position++] + $pivotAttributes;
85
        }
86
87
        if ($object->$relationship() instanceof BelongsTo) {
88
            $foreignKey = $object->$relationship()->getForeignKeyName();
89
            $id = Arr::get($relatedElements, '0.id', null);
90
            $object->update([$foreignKey => $id]);
91
        } elseif ($object->$relationship() instanceof HasOne ||
92
                  $object->$relationship() instanceof HasMany
93
        ) {
94
            $this->updateBelongsToInverseBrowser($object, $relationship, $relatedElements);
95
        } else {
96
            $object->$relationship()->sync($relatedElementsWithPosition);
97
        }
98
    }
99
100
    private function updateBelongsToInverseBrowser($object, $relationship, $updatedElements)
101
    {
102
        $foreignKey = $object->$relationship()->getForeignKeyName();
103
        $relatedModel = $object->$relationship()->getRelated();
104
        $related = $this->getRelatedElementsAsCollection($object, $relationship);
105
106
        $relatedModel
107
            ->whereIn('id', $related->pluck('id'))
108
            ->update([$foreignKey => null]);
109
110
        $updated = $relatedModel
111
            ->whereIn('id', collect($updatedElements)->pluck('id'))
112
            ->get();
113
114
        if ($updated->isNotEmpty()) {
115
            $object->$relationship()->saveMany($updated);
116
        }
117
    }
118
119
    /**
120
     * @param \A17\Twill\Models\Model $object
121
     * @param array $fields
122
     * @param string $relationship
123
     * @param string $positionAttribute
124
     * @return void
125
     */
126
    public function updateOrderedBelongsTomany($object, $fields, $relationship, $positionAttribute = 'position')
127
    {
128
        $this->updateBrowser($object, $fields, $relationship, $positionAttribute);
129
    }
130
131
    /**
132
     * @param \A17\Twill\Models\Model $object
133
     * @param array $fields
134
     * @param string $browserName
135
     * @return void
136
     */
137
    public function updateRelatedBrowser($object, $fields, $browserName)
138
    {
139
        $object->saveRelated($fields['browsers'][$browserName] ?? [], $browserName);
140
    }
141
142
    /**
143
     * @param \A17\Twill\Models\Model $object
144
     * @param string $relation
145
     * @param string|null $routePrefix
146
     * @param string $titleKey
147
     * @param string|null $moduleName
148
     * @return array
149
     */
150
    public function getFormFieldsForBrowser($object, $relation, $routePrefix = null, $titleKey = 'title', $moduleName = null)
151
    {
152
        $fields = $this->getRelatedElementsAsCollection($object, $relation);
153 32
154
        if ($fields->isNotEmpty()) {
155 32
            return $fields->map(function ($relatedElement) use ($titleKey, $routePrefix, $relation, $moduleName) {
156
                return [
157
                    'id' => $relatedElement->id,
158
                    'name' => $relatedElement->titleInBrowser ?? $relatedElement->$titleKey,
159
                    'edit' => moduleRoute($moduleName ?? $relation, $routePrefix ?? '', 'edit', $relatedElement->id),
160
                    'endpointType' => $relatedElement->getMorphClass(),
161
                ] + (classHasTrait($relatedElement, HasMedias::class) ? [
162
                    'thumbnail' => $relatedElement->defaultCmsImage(['w' => 100, 'h' => 100]),
163
                ] : []);
164
            })->toArray();
165
        }
166
167
        return [];
168 32
    }
169
170
    /**
171
     * @param \A17\Twill\Models\Model $object
172
     * @param string $relation
173
     * @return array
174
     */
175
    public function getFormFieldsForRelatedBrowser($object, $relation, $titleKey = 'title')
176
    {
177
        return $object->getRelated($relation)->map(function ($relatedElement) use ($titleKey) {
178
            return ($relatedElement != null) ? [
179
                'id' => $relatedElement->id,
180
                'name' => $relatedElement->titleInBrowser ?? $relatedElement->$titleKey,
181
                'endpointType' => $relatedElement->getMorphClass(),
182
            ] + (empty($relatedElement->adminEditUrl) ? [] : [
183
                'edit' => $relatedElement->adminEditUrl,
184
            ]) + (classHasTrait($relatedElement, HasMedias::class) ? [
185
                'thumbnail' => $relatedElement->defaultCmsImage(['w' => 100, 'h' => 100]),
186
            ] : []) : [];
187
        })->reject(function ($item) {
188
            return empty($item);
189
        })->values()->toArray();
190
    }
191
192
    /**
193
     * Get all browser' detail info from the $browsers attribute.
194
     * The missing information will be inferred by convention of Twill.
195
     *
196
     * @return \Illuminate\Support\Collection
197
     */
198
    protected function getBrowsers()
199
    {
200
        return collect($this->browsers)->map(function ($browser, $key) {
201
            $browserName = is_string($browser) ? $browser : $key;
202
            $moduleName = !empty($browser['moduleName']) ? $browser['moduleName'] : $this->inferModuleNameFromBrowserName($browserName);
203
204
            return [
205
                'relation' => !empty($browser['relation']) ? $browser['relation'] : $this->inferRelationFromBrowserName($browserName),
206
                'routePrefix' => isset($browser['routePrefix']) ? $browser['routePrefix'] : null,
207
                'titleKey' => !empty($browser['titleKey']) ? $browser['titleKey'] : 'title',
208
                'moduleName' => $moduleName,
209
                'model' => !empty($browser['model']) ? $browser['model'] : $this->inferModelFromModuleName($moduleName),
210
                'positionAttribute' => !empty($browser['positionAttribute']) ? $browser['positionAttribute'] : 'position',
211
                'browserName' => $browserName,
212
            ];
213
        })->values();
214
    }
215
216
    /**
217
     * Guess the browser's relation name (shoud be lower camel case, ex. userGroup, contactOffice).
218
     *
219
     * @param string $browserName
220
     * @return string
221
     */
222
    protected function inferRelationFromBrowserName(string $browserName): string
223
    {
224
        return Str::camel($browserName);
225
    }
226
227
    /**
228
     * Guess the module's model name (should be singular upper camel case, ex. User, ArticleType).
229
     *
230
     * @param string $moduleName
231
     * @return string
232
     */
233
    protected function inferModelFromModuleName(string $moduleName): string
234
    {
235
        return Str::studly(Str::singular($moduleName));
236
    }
237
238
    /**
239
     * Guess the browser's module name (should be plural lower camel case, ex. userGroups, contactOffices).
240
     *
241
     * @param string $browserName
242
     * @return string
243
     */
244
    protected function inferModuleNameFromBrowserName(string $browserName): string
245
    {
246
        return Str::camel(Str::plural($browserName));
247
    }
248
249
    private function getRelatedElementsAsCollection($object, $relation)
250
    {
251
        return collect(
252
            $object->$relation instanceof EloquentModel ? [$object->$relation] : $object->$relation
253
        );
254
    }
255
}
256