Test Failed
Push — main ( 706477...92dbda )
by Yaroslav
03:43
created

ScopedRequest::isFlexibleStructure()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 1
dl 0
loc 7
ccs 0
cts 5
cp 0
crap 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace NovaFlexibleContent\Http;
4
5
use Illuminate\Support\Collection;
6
use Laravel\Nova\Http\Requests\NovaRequest;
7
8
class ScopedRequest extends NovaRequest
9
{
10
    /**
11
     * The group's key.
12
     *
13
     * @var string|null
14
     */
15
    public ?string $group = null;
16
17
    /**
18
     * The original file input attributes.
19
     */
20
    protected ?Collection $fileAttributes = null;
21
22
    /**
23
     * Create a copy of the given request, only containing the group's input
24
     *
25
     * @param NovaRequest $from
26
     * @param  array  $attributes
27
     * @param  string  $group
28
     * @return static
29
     */
30
    public static function scopeFrom(NovaRequest $from, array $attributes, string $group): static
31
    {
32
        return parent::createFrom($from)->scopeInto($group, $attributes);
33
    }
34
35
    /**
36
     * Alter the request's input for given group key & attributes
37
     *
38
     * @param  string  $group
39
     * @param  array  $attributes
40
     * @return static
41
     */
42
    public function scopeInto(string $group, array $attributes): static
43
    {
44
        [$input, $files] = $this->getScopeState($group, $attributes);
45
46
        $input['_method']       = $this->input('_method');
47
        $input['_retrieved_at'] = $this->input('_retrieved_at');
48
49
        $this->handleScopeFiles($files, $input, $group);
50
51
        $this->replace($input);
52
        $this->files->replace($files);
53
54
        return $this;
55
    }
56
57
    /**
58
     * Get the target scope configuration array
59
     *
60
     * @param  string  $group
61
     * @param  array  $attributes
62
     * @return array
63
     */
64
    protected function getScopeState(string $group, array $attributes = []): array
65
    {
66
        $input = [];
67
        $files = [];
68
69
        foreach ($attributes as $attribute => $value) {
70
            $attribute = FlexibleAttribute::make($attribute, $group, is_array($value));
0 ignored issues
show
Bug introduced by
is_array($value) of type boolean is incompatible with the type null|string expected by parameter $key of NovaFlexibleContent\Http\FlexibleAttribute::make(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

70
            $attribute = FlexibleAttribute::make($attribute, $group, /** @scrutinizer ignore-type */ is_array($value));
Loading history...
71
72
            // Sub-objects could contain files that need to be kept
73
            if ($attribute->isAggregate()) {
74
                $files                   = array_merge($files, $this->getNestedFiles($value, $attribute->group));
75
                $input[$attribute->name] = $value;
76
77
                continue;
78
            }
79
80
            // Register Files
81
            if ($attribute->isFlexibleFile($value)) {
82
                $files[] = $attribute->getFlexibleFileAttribute($value);
83
84
                continue;
85
            }
86
87
            // Register regular attributes
88
            $input[$attribute->name] = $value;
89
        }
90
91
        return [$input, array_filter($files)];
92
    }
93
94
    /**
95
     * Get nested file attributes from given array
96
     *
97
     * @param  array  $iterable
98
     * @param  null|string  $group
99
     * @return array
100
     */
101
    protected function getNestedFiles(array $iterable, ?string $group = null): array
102
    {
103
        $files = [];
104
        $key   = $this->isFlexibleStructure($iterable) ? $iterable['key'] : $group;
105
106
        foreach ($iterable as $original => $fieldKeyName) {
107
            if (is_array($fieldKeyName)) {
108
                $files = array_merge($files, $this->getNestedFiles($fieldKeyName, $key));
109
110
                continue;
111
            }
112
113
            $attribute = FlexibleAttribute::make($original, $group);
114
115
            if (!$attribute->isFlexibleFile($fieldKeyName)) {
116
                continue;
117
            }
118
119
            $files[] = $attribute->getFlexibleFileAttribute($fieldKeyName);
120
        }
121
122
        return array_filter($files);
123
    }
124
125
    /**
126
     * Get all useful files from current files list
127
     *
128
     * @param  array  $files
129
     * @param  array  $input
130
     * @param  string  $group
131
     * @return void
132
     */
133
    protected function handleScopeFiles(&$files, &$input, $group): void
134
    {
135
        $attributes = collect($files)->keyBy('original');
0 ignored issues
show
Bug introduced by
$files of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

135
        $attributes = collect(/** @scrutinizer ignore-type */ $files)->keyBy('original');
Loading history...
136
137
        $this->fileAttributes = $attributes->mapWithKeys(function ($attribute, $key) {
138
            return [$attribute->name => $key];
139
        });
140
141
        $scope = [];
142
143
        foreach ($this->getFlattenedFiles() as $attribute => $file) {
144
            if (!($target = $attributes->get($attribute))) {
145
                continue;
146
            }
147
148
            if (!$target->group || $target->group !== $group) {
149
                $scope[$target->original] = $file;
150
151
                continue;
152
            }
153
154
            $target->setDataIn($scope, $file);
155
            $target->unsetDataIn($input);
156
        }
157
158
        $files = $scope;
159
    }
160
161
    /**
162
     * Get the request's files as a "flat" (1 dimension) array
163
     *
164
     * @param null $iterable
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $iterable is correct as it would always require null to be passed?
Loading history...
165
     * @param FlexibleAttribute|null $original
166
     * @return array<FlexibleAttribute>
167
     */
168
    protected function getFlattenedFiles($iterable = null, FlexibleAttribute $original = null): array
169
    {
170
        $files = [];
171
172
        foreach ($iterable ?? $this->files->all() as $key => $value) {
173
            $attribute = $original ? $original->nest($key) : FlexibleAttribute::make($key);
174
175
            if (!is_array($value)) {
176
                $files[$attribute->original] = $value;
177
178
                continue;
179
            }
180
181
            $files = array_merge($files, $this->getFlattenedFiles($value, $attribute));
182
        }
183
184
        return array_filter($files);
185
    }
186
187
    /**
188
     * Check if the given array represents a flexible group
189
     *
190
     * @param  array  $iterable
191
     * @return bool
192
     */
193
    protected function isFlexibleStructure(array $iterable): bool
194
    {
195
        $keys = array_keys($iterable);
196
197
        return  in_array('layout', $keys, true)
198
            && in_array('key', $keys, true)
199
            && in_array('attributes', $keys, true);
200
    }
201
202 1
    public function isFileAttribute($name): bool
203
    {
204 1
        return (bool) $this->fileAttributes?->has($name);
205
    }
206
207
    public function getFileAttribute($name): mixed
208
    {
209
        return $this->fileAttributes?->get($name);
210
    }
211
}
212