We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.
1 | <?php |
||||||
2 | |||||||
3 | namespace Backpack\CRUD\app\Library; |
||||||
4 | |||||||
5 | use Backpack\CRUD\app\Exceptions\BackpackProRequiredException; |
||||||
6 | use Backpack\CRUD\ViewNamespaces; |
||||||
7 | use Illuminate\Support\Fluent; |
||||||
8 | |||||||
9 | /** |
||||||
10 | * Adds fluent syntax to Backpack Widgets. |
||||||
11 | */ |
||||||
12 | class Widget extends Fluent |
||||||
13 | { |
||||||
14 | protected $attributes = []; |
||||||
15 | |||||||
16 | public function __construct($attributes) |
||||||
17 | { |
||||||
18 | $this->attributes = $attributes; |
||||||
19 | |||||||
20 | $this->save(); |
||||||
21 | } |
||||||
22 | |||||||
23 | /** |
||||||
24 | * Add a new widget to the widgets collection in the Laravel Service Container. |
||||||
25 | * If a widget with the same name exists, it will update the attributes of that one |
||||||
26 | * instead of creating a new one. |
||||||
27 | * |
||||||
28 | * @param string|array $attributes Either the name of the widget, or an array with the attributes the new widget should hold, including the name attribute. |
||||||
29 | * @return Widget |
||||||
30 | */ |
||||||
31 | public static function add($attributes = null) |
||||||
32 | { |
||||||
33 | // make sure the widget has a name |
||||||
34 | $attributes = is_string($attributes) ? ['name' => $attributes] : $attributes; |
||||||
35 | $attributes['name'] = $attributes['name'] ?? 'widget_'.rand(1, 999999999); |
||||||
36 | |||||||
37 | // if that widget name already exists in the widgets collection |
||||||
38 | // then pick up all widget attributes from that entry |
||||||
39 | // and overwrite them with the ones passed in $attributes |
||||||
40 | if ($existingItem = self::collection()->filter(function ($item) use ($attributes) { |
||||||
41 | return $item->attributes['name'] === $attributes['name']; |
||||||
42 | })->first()) { |
||||||
43 | $attributes = array_merge($existingItem->attributes, $attributes); |
||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
44 | } |
||||||
45 | |||||||
46 | // set defaults for other mandatory attributes |
||||||
47 | $attributes['section'] = $attributes['section'] ?? 'before_content'; |
||||||
48 | $attributes['type'] = $attributes['type'] ?? 'card'; |
||||||
49 | |||||||
50 | return new static($attributes); |
||||||
51 | } |
||||||
52 | |||||||
53 | /** |
||||||
54 | * Return the widget attribute value or null when it doesn't exist. |
||||||
55 | * |
||||||
56 | * @param string $attribute |
||||||
57 | * @return mixed |
||||||
58 | */ |
||||||
59 | public function getAttribute(string $attribute) |
||||||
60 | { |
||||||
61 | return $this->attributes[$attribute] ?? null; |
||||||
62 | } |
||||||
63 | |||||||
64 | /** |
||||||
65 | * Check if widget has the attribute. |
||||||
66 | * |
||||||
67 | * @param string $attribute |
||||||
68 | * @return bool |
||||||
69 | */ |
||||||
70 | public function hasAttribute(string $attribute) |
||||||
71 | { |
||||||
72 | return array_key_exists($attribute, $this->attributes); |
||||||
73 | } |
||||||
74 | |||||||
75 | /** |
||||||
76 | * This method allows one to creat a widget without attaching it to any 'real' |
||||||
77 | * widget section, by moving it to a 'hidden' section. |
||||||
78 | * |
||||||
79 | * It exists for one reason: so that developers can add widgets to a custom array, without |
||||||
80 | * adding them to one of the widget sections. |
||||||
81 | * |
||||||
82 | * Ex: when developers need to pass multiple widgets as contents of the |
||||||
83 | * div widget. But they don't want them added to the before_content of after_content |
||||||
84 | * sections. So what they do is basically add them to a 'hidden' section, that nobody will ever see. |
||||||
85 | * |
||||||
86 | * @return Widget |
||||||
87 | */ |
||||||
88 | public static function make($attributes = null) |
||||||
89 | { |
||||||
90 | $widget = static::add($attributes); |
||||||
91 | $widget->section('hidden'); |
||||||
0 ignored issues
–
show
The method
section() does not exist on Backpack\CRUD\app\Library\Widget . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
92 | |||||||
93 | return $widget; |
||||||
94 | } |
||||||
95 | |||||||
96 | /** |
||||||
97 | * Remove an attribute from the current definition array. |
||||||
98 | * |
||||||
99 | * @param string $attribute Name of the attribute to forget (ex: class) |
||||||
100 | * @return Widget |
||||||
101 | */ |
||||||
102 | public function forget($attribute) |
||||||
103 | { |
||||||
104 | $this->offsetUnset($attribute); |
||||||
0 ignored issues
–
show
$attribute of type string is incompatible with the type Illuminate\Support\TKey expected by parameter $offset of Illuminate\Support\Fluent::offsetUnset() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
105 | |||||||
106 | return $this; |
||||||
107 | } |
||||||
108 | |||||||
109 | // TODO: add ability to push a widget right after another widget |
||||||
110 | public function after($destination) |
||||||
0 ignored issues
–
show
The parameter
$destination is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
111 | { |
||||||
112 | } |
||||||
113 | |||||||
114 | // TODO: add ability to push a widget right before another widget |
||||||
115 | public function before($destination) |
||||||
0 ignored issues
–
show
The parameter
$destination is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
116 | { |
||||||
117 | } |
||||||
118 | |||||||
119 | /** |
||||||
120 | * Make this widget the first one in its section. |
||||||
121 | * |
||||||
122 | * @return Widget |
||||||
123 | */ |
||||||
124 | public function makeFirst() |
||||||
125 | { |
||||||
126 | $this->collection()->pull($this->attributes['name']); |
||||||
127 | $this->collection()->prepend($this); |
||||||
128 | |||||||
129 | return $this; |
||||||
130 | } |
||||||
131 | |||||||
132 | /** |
||||||
133 | * Make this widget the last one in its section. |
||||||
134 | * |
||||||
135 | * @return Widget |
||||||
136 | */ |
||||||
137 | public function makeLast() |
||||||
138 | { |
||||||
139 | $this->collection()->pull($this->attributes['name']); |
||||||
140 | $this->collection()->push($this); |
||||||
141 | |||||||
142 | return $this; |
||||||
143 | } |
||||||
144 | |||||||
145 | /** |
||||||
146 | * Get an array of full paths to the widget view, consisting of: |
||||||
147 | * - the path given in the widget definition |
||||||
148 | * - fallback view paths as configured in backpack/config/base.php. |
||||||
149 | * |
||||||
150 | * @return array |
||||||
151 | */ |
||||||
152 | public function getFinalViewPath() |
||||||
153 | { |
||||||
154 | if (isset($this->attributes['viewNamespace'])) { |
||||||
155 | $path = $this->attributes['viewNamespace'].'.'.$this->attributes['type']; |
||||||
156 | |||||||
157 | if (view()->exists($path)) { |
||||||
158 | return $path; |
||||||
0 ignored issues
–
show
|
|||||||
159 | } |
||||||
160 | } |
||||||
161 | $type = $this->attributes['type']; |
||||||
162 | $paths = array_map(function ($item) use ($type) { |
||||||
163 | return $item.'.'.$type; |
||||||
164 | }, ViewNamespaces::getWithFallbackFor('widgets', 'backpack.ui.component_view_namespaces.widgets')); |
||||||
165 | |||||||
166 | foreach ($paths as $path) { |
||||||
167 | if (view()->exists($path)) { |
||||||
168 | return $path; |
||||||
169 | } |
||||||
170 | } |
||||||
171 | // if no view exists, in any of the directories above... no bueno |
||||||
172 | if (! backpack_pro()) { |
||||||
173 | throw new BackpackProRequiredException('Cannot find the widget view: '.$this->attributes['type'].'. Please check for typos.'.(backpack_pro() ? '' : ' If you are trying to use a PRO widget, please first purchase and install the backpack/pro addon from backpackforlaravel.com'), 1); |
||||||
174 | } |
||||||
175 | abort(500, 'Cannot find the view for «'.$this->attributes['type'].'» widget type. Please check for typos.', ['developer-error-exception']); |
||||||
176 | } |
||||||
177 | |||||||
178 | // ------- |
||||||
179 | // ALIASES |
||||||
180 | // ------- |
||||||
181 | // Aka convenience methods. |
||||||
182 | // These method just call other methods. |
||||||
183 | |||||||
184 | // Alias of add() |
||||||
185 | public static function name(...$args) |
||||||
186 | { |
||||||
187 | return static::add(...$args); |
||||||
0 ignored issues
–
show
$args is expanded, but the parameter $attributes of Backpack\CRUD\app\Library\Widget::add() does not expect variable arguments.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
188 | } |
||||||
189 | |||||||
190 | // Alias of section() |
||||||
191 | public function to(...$args) |
||||||
192 | { |
||||||
193 | return $this->section(...$args); |
||||||
194 | } |
||||||
195 | |||||||
196 | // Alias of section() |
||||||
197 | public function group(...$args) |
||||||
198 | { |
||||||
199 | return $this->section(...$args); |
||||||
200 | } |
||||||
201 | |||||||
202 | // Alias of viewNamespace() |
||||||
203 | public function from(...$args) |
||||||
204 | { |
||||||
205 | return $this->viewNamespace(...$args); |
||||||
0 ignored issues
–
show
The method
viewNamespace() does not exist on Backpack\CRUD\app\Library\Widget . Since you implemented __call , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
206 | } |
||||||
207 | |||||||
208 | // ------------------ |
||||||
209 | // COLLECTION METHODS |
||||||
210 | // ------------------ |
||||||
211 | // Manipulate the global widget collection. |
||||||
212 | |||||||
213 | public static function collection() |
||||||
214 | { |
||||||
215 | return app('widgets'); |
||||||
216 | } |
||||||
217 | |||||||
218 | /** |
||||||
219 | * Remove the widget from its section. |
||||||
220 | * |
||||||
221 | * @return Widget |
||||||
222 | */ |
||||||
223 | public function remove() |
||||||
224 | { |
||||||
225 | $this->collection()->pull($this->attributes['name']); |
||||||
226 | |||||||
227 | return $this; |
||||||
228 | } |
||||||
229 | |||||||
230 | /** |
||||||
231 | * This alias of remove() exists for one reason: so that developers can add |
||||||
232 | * widgets to a custom array, instead of adding them to one of the widget |
||||||
233 | * sections. Ex: when developers need to pass multiple widgets as contents of the |
||||||
234 | * div widget. But they don't want them added to the before_content of after_content |
||||||
235 | * sections. So what they do is basically add them to a section, then remove them. |
||||||
236 | * What's left is the widget itself, but without being attached to any section. |
||||||
237 | * |
||||||
238 | * @return Widget |
||||||
239 | */ |
||||||
240 | public function onlyHere(...$args) |
||||||
241 | { |
||||||
242 | return $this->remove(...$args); |
||||||
0 ignored issues
–
show
The call to
Backpack\CRUD\app\Library\Widget::remove() has too many arguments starting with $args .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
243 | } |
||||||
244 | |||||||
245 | // --------------- |
||||||
246 | // PRIVATE METHODS |
||||||
247 | // --------------- |
||||||
248 | |||||||
249 | /** |
||||||
250 | * Update the global CrudPanel object with the current widget attributes. |
||||||
251 | * |
||||||
252 | * @return Widget |
||||||
253 | */ |
||||||
254 | private function save() |
||||||
255 | { |
||||||
256 | $itemExists = $this->collection()->filter(function ($item) { |
||||||
257 | return $item->attributes['name'] === $this->attributes['name']; |
||||||
258 | })->isNotEmpty(); |
||||||
259 | if (! $itemExists) { |
||||||
260 | $this->collection()->put($this->attributes['name'], $this); |
||||||
261 | } else { |
||||||
262 | $this->collection()[$this->attributes['name']] = $this; |
||||||
263 | } |
||||||
264 | |||||||
265 | return $this; |
||||||
266 | } |
||||||
267 | |||||||
268 | // ----------------- |
||||||
269 | // DEBUGGING METHODS |
||||||
270 | // ----------------- |
||||||
271 | |||||||
272 | /** |
||||||
273 | * Dump the current object to the screen, |
||||||
274 | * so that the developer can see its contents. |
||||||
275 | * |
||||||
276 | * @return Widget |
||||||
277 | */ |
||||||
278 | public function dump() |
||||||
279 | { |
||||||
280 | dump($this); |
||||||
281 | |||||||
282 | return $this; |
||||||
283 | } |
||||||
284 | |||||||
285 | /** |
||||||
286 | * Dump and die. Dumps the current object to the screen, |
||||||
287 | * so that the developer can see its contents, then stops |
||||||
288 | * the execution. |
||||||
289 | * |
||||||
290 | * @return Widget |
||||||
291 | */ |
||||||
292 | public function dd() |
||||||
293 | { |
||||||
294 | dd($this); |
||||||
295 | |||||||
296 | return $this; |
||||||
297 | } |
||||||
298 | |||||||
299 | /** |
||||||
300 | * Overwritten methods to prevent BC in Laravel 11, since they introduced the `value()` method |
||||||
301 | * in their Fluent class. Although the Widget class is Fluent, it does not behave the same |
||||||
302 | * in regards to `value()`, since we use it as a key in widget definition. |
||||||
303 | */ |
||||||
304 | public function value($value, $default = null) |
||||||
0 ignored issues
–
show
The parameter
$default is not used and could be removed.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for parameters that have been defined for a function or method, but which are not used in the method body. ![]() |
|||||||
305 | { |
||||||
306 | $this->attributes['value'] = $value; |
||||||
307 | |||||||
308 | return $this->save(); |
||||||
309 | } |
||||||
310 | |||||||
311 | #[\ReturnTypeWillChange] |
||||||
312 | public function offsetGet($offset): mixed |
||||||
313 | { |
||||||
314 | return $this->get($offset); |
||||||
315 | } |
||||||
316 | |||||||
317 | public function __get($key) |
||||||
318 | { |
||||||
319 | return $this->get($key); |
||||||
320 | } |
||||||
321 | |||||||
322 | // ------------- |
||||||
323 | // MAGIC METHODS |
||||||
324 | // ------------- |
||||||
325 | |||||||
326 | /** |
||||||
327 | * Any call to a non-existing method on this class will be assumed to be |
||||||
328 | * an attribute that the developer wants to add to that particular widget. |
||||||
329 | * |
||||||
330 | * Eg: class('something') will set the "class" attribute to "something" |
||||||
331 | * |
||||||
332 | * @param string $method The method being called that doesn't exist. |
||||||
333 | * @param array $parameters The arguments when that method was called. |
||||||
334 | * @return Widget |
||||||
335 | */ |
||||||
336 | public function __call($method, $parameters) |
||||||
337 | { |
||||||
338 | $this->attributes[$method] = count($parameters) > 0 ? $parameters[0] : true; |
||||||
339 | |||||||
340 | return $this->save(); |
||||||
341 | } |
||||||
342 | } |
||||||
343 |