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 | 5 | public static function scopeFrom(NovaRequest $from, array $attributes, string $group): static |
|||
31 | { |
||||
32 | 5 | 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 | 5 | public function scopeInto(string $group, array $attributes): static |
|||
43 | { |
||||
44 | 5 | [$input, $files] = $this->getScopeState($group, $attributes); |
|||
45 | |||||
46 | 5 | $input['_method'] = $this->input('_method'); |
|||
47 | 5 | $input['_retrieved_at'] = $this->input('_retrieved_at'); |
|||
48 | |||||
49 | 5 | $this->handleScopeFiles($files, $input, $group); |
|||
50 | |||||
51 | 5 | $this->replace($input); |
|||
52 | 5 | $this->files->replace($files); |
|||
53 | |||||
54 | 5 | 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 | 5 | protected function getScopeState(string $group, array $attributes = []): array |
|||
65 | { |
||||
66 | 5 | $input = []; |
|||
67 | 5 | $files = []; |
|||
68 | |||||
69 | 5 | foreach ($attributes as $attribute => $value) { |
|||
70 | 5 | $attribute = FlexibleAttribute::make($attribute, $group, is_array($value)); |
|||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
71 | |||||
72 | // Sub-objects could contain files that need to be kept |
||||
73 | 5 | if ($attribute->isAggregate()) { |
|||
74 | 1 | $files = array_merge($files, $this->getNestedFiles($value, $attribute->group)); |
|||
75 | 1 | $input[$attribute->name] = $value; |
|||
76 | |||||
77 | 1 | continue; |
|||
78 | } |
||||
79 | |||||
80 | // Register Files |
||||
81 | 5 | if ($attribute->isFlexibleFile($value)) { |
|||
82 | 2 | $files[] = $attribute->getFlexibleFileAttribute($value); |
|||
83 | |||||
84 | 2 | continue; |
|||
85 | } |
||||
86 | |||||
87 | // Register regular attributes |
||||
88 | 5 | $input[$attribute->name] = $value; |
|||
89 | } |
||||
90 | |||||
91 | 5 | 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 | 1 | protected function getNestedFiles(array $iterable, ?string $group = null): array |
|||
102 | { |
||||
103 | 1 | $files = []; |
|||
104 | 1 | $key = $this->isFlexibleStructure($iterable) ? $iterable['key'] : $group; |
|||
105 | |||||
106 | 1 | foreach ($iterable as $original => $fieldKeyName) { |
|||
107 | 1 | if (is_array($fieldKeyName)) { |
|||
108 | 1 | $files = array_merge($files, $this->getNestedFiles($fieldKeyName, $key)); |
|||
109 | |||||
110 | 1 | continue; |
|||
111 | } |
||||
112 | |||||
113 | 1 | $attribute = FlexibleAttribute::make($original, $group); |
|||
114 | |||||
115 | 1 | if (!$attribute->isFlexibleFile($fieldKeyName)) { |
|||
116 | 1 | continue; |
|||
117 | } |
||||
118 | |||||
119 | 1 | $files[] = $attribute->getFlexibleFileAttribute($fieldKeyName); |
|||
120 | } |
||||
121 | |||||
122 | 1 | 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 | 5 | protected function handleScopeFiles(&$files, &$input, $group): void |
|||
134 | { |
||||
135 | 5 | $attributes = collect($files)->keyBy('original'); |
|||
0 ignored issues
–
show
$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
![]() |
|||||
136 | |||||
137 | 5 | $this->fileAttributes = $attributes->mapWithKeys(function ($attribute, $key) { |
|||
138 | 2 | return [$attribute->name => $key]; |
|||
139 | 5 | }); |
|||
140 | |||||
141 | 5 | $scope = []; |
|||
142 | |||||
143 | 5 | foreach ($this->getFlattenedFiles() as $attribute => $file) { |
|||
144 | 2 | if (!($target = $attributes->get($attribute))) { |
|||
145 | 1 | continue; |
|||
146 | } |
||||
147 | |||||
148 | 2 | if (!$target->group || $target->group !== $group) { |
|||
149 | 1 | $scope[$target->original] = $file; |
|||
150 | |||||
151 | 1 | continue; |
|||
152 | } |
||||
153 | |||||
154 | 2 | $target->setDataIn($scope, $file); |
|||
155 | 2 | $target->unsetDataIn($input); |
|||
156 | } |
||||
157 | |||||
158 | 5 | $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
|
|||||
165 | * @param FlexibleAttribute|null $original |
||||
166 | * @return array<FlexibleAttribute> |
||||
167 | */ |
||||
168 | 5 | protected function getFlattenedFiles($iterable = null, FlexibleAttribute $original = null): array |
|||
169 | { |
||||
170 | 5 | $files = []; |
|||
171 | |||||
172 | 5 | foreach ($iterable ?? $this->files->all() as $key => $value) { |
|||
173 | 2 | $attribute = $original ? $original->nest($key) : FlexibleAttribute::make($key); |
|||
174 | |||||
175 | 2 | if (!is_array($value)) { |
|||
176 | 2 | $files[$attribute->original] = $value; |
|||
177 | |||||
178 | 2 | continue; |
|||
179 | } |
||||
180 | |||||
181 | 1 | $files = array_merge($files, $this->getFlattenedFiles($value, $attribute)); |
|||
182 | } |
||||
183 | |||||
184 | 5 | 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 | 1 | protected function isFlexibleStructure(array $iterable): bool |
|||
194 | { |
||||
195 | 1 | $keys = array_keys($iterable); |
|||
196 | |||||
197 | 1 | return in_array('layout', $keys, true) |
|||
198 | 1 | && in_array('key', $keys, true) |
|||
199 | 1 | && in_array('attributes', $keys, true); |
|||
200 | } |
||||
201 | |||||
202 | 6 | public function isFileAttribute($name): bool |
|||
203 | { |
||||
204 | 6 | return (bool) $this->fileAttributes?->has($name); |
|||
205 | } |
||||
206 | |||||
207 | 2 | public function getFileAttribute($name): mixed |
|||
208 | { |
||||
209 | 2 | return $this->fileAttributes?->get($name); |
|||
210 | } |
||||
211 | } |
||||
212 |