• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

conedevelopment / root / 15084089635

17 May 2025 10:00AM UTC coverage: 77.93% (+0.04%) from 77.891%
15084089635

push

github

web-flow
Modernize back-end.yml (#240)

3291 of 4223 relevant lines covered (77.93%)

36.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.18
/src/Resources/Resource.php
1
<?php
2

3
namespace Cone\Root\Resources;
4

5
use Cone\Root\Actions\Action;
6
use Cone\Root\Exceptions\SaveFormDataException;
7
use Cone\Root\Fields\BelongsToMany;
8
use Cone\Root\Fields\Events;
9
use Cone\Root\Fields\Field;
10
use Cone\Root\Fields\Fields;
11
use Cone\Root\Fields\HasMany;
12
use Cone\Root\Fields\Meta;
13
use Cone\Root\Fields\MorphMany;
14
use Cone\Root\Fields\Relation;
15
use Cone\Root\Fields\Translations;
16
use Cone\Root\Filters\Filter;
17
use Cone\Root\Filters\RenderableFilter;
18
use Cone\Root\Filters\Search;
19
use Cone\Root\Filters\Sort;
20
use Cone\Root\Filters\TrashStatus;
21
use Cone\Root\Http\Middleware\Authorize;
22
use Cone\Root\Interfaces\Form;
23
use Cone\Root\Root;
24
use Cone\Root\Traits\AsForm;
25
use Cone\Root\Traits\Authorizable;
26
use Cone\Root\Traits\HasRootEvents;
27
use Cone\Root\Traits\RegistersRoutes;
28
use Cone\Root\Traits\ResolvesActions;
29
use Cone\Root\Traits\ResolvesFilters;
30
use Cone\Root\Traits\ResolvesWidgets;
31
use Cone\Root\Traits\Translatable;
32
use Cone\Root\Widgets\Metric;
33
use Cone\Root\Widgets\Widget;
34
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
35
use Illuminate\Contracts\Support\Arrayable;
36
use Illuminate\Contracts\Support\MessageBag;
37
use Illuminate\Database\Eloquent\Builder;
38
use Illuminate\Database\Eloquent\Model;
39
use Illuminate\Database\Eloquent\SoftDeletes;
40
use Illuminate\Http\Request;
41
use Illuminate\Routing\Events\RouteMatched;
42
use Illuminate\Routing\Router;
43
use Illuminate\Support\Collection;
44
use Illuminate\Support\Facades\DB;
45
use Illuminate\Support\Facades\Gate;
46
use Illuminate\Support\Facades\URL;
47
use Illuminate\Support\Str;
48
use Throwable;
49

50
abstract class Resource implements Arrayable, Form
51
{
52
    use AsForm {
53
        AsForm::resolveFields as __resolveFields;
54
    }
55
    use Authorizable;
56
    use RegistersRoutes {
57
        RegistersRoutes::registerRoutes as __registerRoutes;
58
        RegistersRoutes::routeMatched as __routeMatched;
59
    }
60
    use ResolvesActions;
61
    use ResolvesFilters;
62
    use ResolvesWidgets;
63

64
    /**
65
     * The model class.
66
     */
67
    protected string $model;
68

69
    /**
70
     * The relations to eager load on every query.
71
     */
72
    protected array $with = [];
73

74
    /**
75
     * The relations to eager load on every query.
76
     */
77
    protected array $withCount = [];
78

79
    /**
80
     * The icon for the resource.
81
     */
82
    protected string $icon = 'archive';
83

84
    /**
85
     * The group for the resource.
86
     */
87
    protected string $group = 'General';
88

89
    /**
90
     * Boot the resource.
91
     */
92
    public function boot(Root $root): void
198✔
93
    {
94
        $root->routes(function (Router $router) use ($root): void {
198✔
95
            $this->registerRoutes($root->app['request'], $router);
198✔
96
        });
198✔
97

98
        $root->navigation->location('sidebar')->new(
198✔
99
            $this->getUri(),
198✔
100
            $this->getName(),
198✔
101
            ['icon' => $this->getIcon(), 'group' => __($this->group)],
198✔
102
        );
198✔
103
    }
104

105
    /**
106
     * Get the model for the resource.
107
     */
108
    public function getModel(): string
198✔
109
    {
110
        return $this->model;
198✔
111
    }
112

113
    /**
114
     * Get the key.
115
     */
116
    public function getKey(): string
198✔
117
    {
118
        return Str::of($this->getModel())->classBasename()->plural()->kebab()->value();
198✔
119
    }
120

121
    /**
122
     * Get the URI key.
123
     */
124
    public function getUriKey(): string
198✔
125
    {
126
        return $this->getKey();
198✔
127
    }
128

129
    /**
130
     * Get the route prefix.
131
     */
132
    public function getRoutePrefix(): string
198✔
133
    {
134
        return sprintf('resources/%s', $this->getUriKey());
198✔
135
    }
136

137
    /**
138
     * Get the route parameter name.
139
     */
140
    public function getRouteParameterName(): string
26✔
141
    {
142
        return '_resource';
26✔
143
    }
144

145
    /**
146
     * Get the name.
147
     */
148
    public function getName(): string
198✔
149
    {
150
        return __(Str::of($this->getModel())->classBasename()->headline()->plural()->value());
198✔
151
    }
152

153
    /**
154
     * Get the model name.
155
     */
156
    public function getModelName(): string
5✔
157
    {
158
        return __(Str::of($this->getModel())->classBasename()->value());
5✔
159
    }
160

161
    /**
162
     * Get the model instance.
163
     */
164
    public function getModelInstance(): Model
27✔
165
    {
166
        return new ($this->getModel());
27✔
167
    }
168

169
    /**
170
     * Set the resource icon.
171
     */
172
    public function icon(string $icon): static
1✔
173
    {
174
        $this->icon = $icon;
1✔
175

176
        return $this;
1✔
177
    }
178

179
    /**
180
     * Get the resource icon.
181
     */
182
    public function getIcon(): string
198✔
183
    {
184
        return $this->icon;
198✔
185
    }
186

187
    /**
188
     * Get the policy for the model.
189
     */
190
    public function getPolicy(): mixed
25✔
191
    {
192
        return Gate::getPolicyFor($this->getModel());
25✔
193
    }
194

195
    /**
196
     * Resolve the ability.
197
     */
198
    public function resolveAbility(string $ability, Request $request, Model $model, ...$arguments): bool
5✔
199
    {
200
        $policy = $this->getPolicy();
5✔
201

202
        return is_null($policy)
5✔
203
            || ! method_exists($policy, $ability)
5✔
204
            || Gate::allows($ability, [$model, ...$arguments]);
5✔
205
    }
206

207
    /**
208
     * Map the resource abilities.
209
     */
210
    public function mapResourceAbilities(Request $request): array
4✔
211
    {
212
        return [
4✔
213
            'viewAny' => $this->resolveAbility('viewAny', $request, $this->getModelInstance()),
4✔
214
            'create' => $this->resolveAbility('create', $request, $this->getModelInstance()),
4✔
215
        ];
4✔
216
    }
217

218
    /**
219
     * Map the model abilities.
220
     */
221
    public function mapModelAbilities(Request $request, Model $model): array
3✔
222
    {
223
        return [
3✔
224
            'view' => $this->resolveAbility('view', $request, $model),
3✔
225
            'update' => $this->resolveAbility('update', $request, $model),
3✔
226
            'restore' => $this->resolveAbility('restore', $request, $model),
3✔
227
            'delete' => $this->resolveAbility('delete', $request, $model),
3✔
228
            'forceDelete' => $this->resolveAbility('forceDelete', $request, $model),
3✔
229
        ];
3✔
230
    }
231

232
    /**
233
     * Set the relations to eagerload.
234
     */
235
    public function with(array $relations): static
1✔
236
    {
237
        $this->with = $relations;
1✔
238

239
        return $this;
1✔
240
    }
241

242
    /**
243
     * Set the relation counts to eagerload.
244
     */
245
    public function withCount(array $relations): static
×
246
    {
247
        $this->withCount = $relations;
×
248

249
        return $this;
×
250
    }
251

252
    /**
253
     * Make a new Eloquent query instance.
254
     */
255
    public function query(): Builder
24✔
256
    {
257
        return $this->getModelInstance()->newQuery()->with($this->with)->withCount($this->withCount);
24✔
258
    }
259

260
    /**
261
     * Resolve the query for the given request.
262
     */
263
    public function resolveQuery(Request $request): Builder
24✔
264
    {
265
        return $this->query();
24✔
266
    }
267

268
    /**
269
     * Resolve the filtered query for the given request.
270
     */
271
    public function resolveFilteredQuery(Request $request): Builder
3✔
272
    {
273
        return $this->resolveFilters($request)->apply($request, $this->resolveQuery($request));
3✔
274
    }
275

276
    /**
277
     * Resolve the route binding query.
278
     */
279
    public function resolveRouteBindingQuery(Request $request): Builder
20✔
280
    {
281
        return $this->resolveQuery($request)
20✔
282
            ->withoutEagerLoads()
20✔
283
            ->when(
20✔
284
                $this->isSoftDeletable(),
20✔
285
                static fn (Builder $query): Builder => $query->withTrashed()
20✔
286
            );
20✔
287
    }
288

289
    /**
290
     * Resolve the resource model for a bound value.
291
     */
292
    public function resolveRouteBinding(Request $request, string $id): Model
20✔
293
    {
294
        return $this->resolveRouteBindingQuery($request)->findOrFail($id);
20✔
295
    }
296

297
    /**
298
     * Determine if the model soft deletable.
299
     */
300
    public function isSoftDeletable(): bool
24✔
301
    {
302
        return in_array(SoftDeletes::class, class_uses_recursive($this->getModel()));
24✔
303
    }
304

305
    /**
306
     * Get the URL for the given model.
307
     */
308
    public function modelUrl(Model $model): string
6✔
309
    {
310
        return sprintf('%s/%s', $this->getUri(), $model->exists ? $model->getKey() : '');
6✔
311
    }
312

313
    /**
314
     * Get the title for the model.
315
     */
316
    public function modelTitle(Model $model): string
7✔
317
    {
318
        return $model->getKey();
7✔
319
    }
320

321
    /**
322
     * Determine whether the resource model has root events.
323
     */
324
    public function hasRootEvents(): bool
198✔
325
    {
326
        return in_array(HasRootEvents::class, class_uses_recursive($this->getModel()));
198✔
327
    }
328

329
    /**
330
     * Resolve the events field.
331
     */
332
    public function resolveEventsField(Request $request): ?Events
198✔
333
    {
334
        return $this->hasRootEvents() ? new Events : null;
198✔
335
    }
336

337
    /**
338
     * Determine whether the resource model has root events.
339
     */
340
    public function translatable(): bool
198✔
341
    {
342
        return in_array(Translatable::class, class_uses_recursive($this->getModel()));
198✔
343
    }
344

345
    /**
346
     * Resolve the translations field.
347
     */
348
    public function resolveTranslationsField(Request $request): ?Translations
198✔
349
    {
350
        return $this->translatable()
198✔
351
            ? Translations::make()
198✔
352
                ->withFields(fn (): array => $this->resolveFields($request)
198✔
353
                    ->translatable()
198✔
354
                    ->map(static fn (Field $field): Field => (clone $field)
198✔
355
                        ->translatable(false)
198✔
356
                        ->setModelAttribute($key = 'values->'.$field->getModelAttribute())
198✔
357
                        ->name($key)
198✔
358
                        ->id($key))
198✔
359
                    ->all())
198✔
360
            : null;
198✔
361
    }
362

363
    /**
364
     * Resolve the fields collection.
365
     */
366
    public function resolveFields(Request $request): Fields
198✔
367
    {
368
        if (is_null($this->fields)) {
198✔
369
            $this->withFields(fn (): array => array_values(array_filter([
198✔
370
                $this->resolveTranslationsField($request),
198✔
371
                $this->resolveEventsField($request),
198✔
372
            ])));
198✔
373
        }
374

375
        return $this->__resolveFields($request);
198✔
376
    }
377

378
    /**
379
     * Define the filters for the object.
380
     */
381
    public function filters(Request $request): array
4✔
382
    {
383
        $fields = $this->resolveFields($request)->authorized($request);
4✔
384

385
        $searchables = $fields->searchable();
4✔
386

387
        $sortables = $fields->sortable();
4✔
388

389
        $filterables = $fields->filterable();
4✔
390

391
        return array_values(array_filter([
4✔
392
            $searchables->isNotEmpty() ? new Search($searchables) : null,
4✔
393
            $sortables->isNotEmpty() ? new Sort($sortables) : null,
4✔
394
            $this->isSoftDeletable() ? new TrashStatus : null,
4✔
395
            ...$filterables->map->toFilter()->all(),
4✔
396
        ]));
4✔
397
    }
398

399
    /**
400
     * Handle the callback for the field resolution.
401
     */
402
    protected function resolveField(Request $request, Field $field): void
198✔
403
    {
404
        $field->setAttribute('form', $this->getKey());
198✔
405
        $field->resolveErrorsUsing(fn (Request $request): MessageBag => $this->errors($request));
198✔
406

407
        if ($field instanceof Relation) {
198✔
408
            $field->resolveRouteKeyNameUsing(fn (): string => Str::of($field->getRelationName())->singular()->ucfirst()->prepend($this->getKey())->value());
198✔
409
        }
410
    }
411

412
    /**
413
     * Handle the callback for the filter resolution.
414
     */
415
    protected function resolveFilter(Request $request, Filter $filter): void
4✔
416
    {
417
        $filter->setKey(sprintf('%s_%s', $this->getKey(), $filter->getKey()));
4✔
418
    }
419

420
    /**
421
     * Handle the callback for the action resolution.
422
     */
423
    protected function resolveAction(Request $request, Action $action): void
198✔
424
    {
425
        $action->withQuery(fn (): Builder => $this->resolveFilteredQuery($request));
198✔
426
    }
427

428
    /**
429
     * Handle the callback for the widget resolution.
430
     */
431
    protected function resolveWidget(Request $request, Widget $widget): void
198✔
432
    {
433
        if ($widget instanceof Metric) {
198✔
434
            $widget->withQuery(fn (): Builder => $this->resolveQuery($request));
198✔
435
        }
436
    }
437

438
    /**
439
     * Get the per page options.
440
     */
441
    public function getPerPageOptions(): array
1✔
442
    {
443
        return Collection::make([$this->getModelInstance()->getPerPage()])
1✔
444
            ->merge([15, 25, 50, 100])
1✔
445
            ->filter()
1✔
446
            ->unique()
1✔
447
            ->values()
1✔
448
            ->toArray();
1✔
449
    }
450

451
    /**
452
     * Get the per page key.
453
     */
454
    public function getPerPageKey(): string
1✔
455
    {
456
        return sprintf('%s_per_page', $this->getKey());
1✔
457
    }
458

459
    /**
460
     * Get the sort key.
461
     */
462
    public function getSortKey(): string
1✔
463
    {
464
        return sprintf('%s_sort', $this->getKey());
1✔
465
    }
466

467
    /**
468
     * Perform the query and the pagination.
469
     */
470
    public function paginate(Request $request): LengthAwarePaginator
1✔
471
    {
472
        return $this->resolveFilteredQuery($request)
1✔
473
            ->tap(function (Builder $query) use ($request): void {
1✔
474
                $this->resolveFields($request)
1✔
475
                    ->authorized($request, $query->getModel())
1✔
476
                    ->visible('index')
1✔
477
                    ->filter(fn (Field $field): bool => $field instanceof Relation)
1✔
478
                    ->each(static function (Relation $relation) use ($query, $request): void {
1✔
479
                        if ($relation instanceof BelongsToMany || $relation instanceof HasMany || $relation instanceof MorphMany) {
1✔
480
                            $relation->resolveAggregate($request, $query);
1✔
481
                        } elseif ($relation instanceof Meta) {
1✔
482
                            $query->with('metaData');
×
483
                        } else {
484
                            $query->with($relation->getRelationName());
1✔
485
                        }
486
                    });
1✔
487
            })
1✔
488
            ->latest()
1✔
489
            ->paginate($request->input($this->getPerPageKey()))
1✔
490
            ->withQueryString()
1✔
491
            ->through(fn (Model $model): array => $this->mapModel($request, $model));
1✔
492
    }
493

494
    /**
495
     * Map the model.
496
     */
497
    public function mapModel(Request $request, Model $model): array
1✔
498
    {
499
        return [
1✔
500
            'id' => $model->getKey(),
1✔
501
            'url' => $this->modelUrl($model),
1✔
502
            'model' => $model,
1✔
503
            'abilities' => $this->mapModelAbilities($request, $model),
1✔
504
            'fields' => $this->resolveFields($request)
1✔
505
                ->authorized($request, $model)
1✔
506
                ->visible('index')
1✔
507
                ->mapToDisplay($request, $model),
1✔
508
        ];
1✔
509
    }
510

511
    /**
512
     * Handle the request.
513
     */
514
    public function handleFormRequest(Request $request, Model $model): void
3✔
515
    {
516
        $this->validateFormRequest($request, $model);
3✔
517

518
        try {
519
            DB::beginTransaction();
3✔
520

521
            $this->resolveFields($request)
3✔
522
                ->authorized($request, $model)
3✔
523
                ->visible($request->isMethod('POST') ? 'create' : 'update')
3✔
524
                ->subResource(false)
3✔
525
                ->persist($request, $model);
3✔
526

527
            $this->saving($request, $model);
3✔
528

529
            $model->save();
3✔
530

531
            if (in_array(HasRootEvents::class, class_uses_recursive($model))) {
3✔
532
                $model->recordRootEvent(
3✔
533
                    $model->wasRecentlyCreated ? 'Created' : 'Updated',
3✔
534
                    $request->user()
3✔
535
                );
3✔
536
            }
537

538
            $this->saved($request, $model);
3✔
539

540
            DB::commit();
3✔
541
        } catch (Throwable $exception) {
×
542
            report($exception);
×
543

544
            DB::rollBack();
×
545

546
            throw new SaveFormDataException($exception->getMessage());
×
547
        }
548
    }
549

550
    /**
551
     * Handle the saving form event.
552
     */
553
    public function saving(Request $request, Model $model): void
3✔
554
    {
555
        //
556
    }
3✔
557

558
    /**
559
     * Handle the saved form event.
560
     */
561
    public function saved(Request $request, Model $model): void
3✔
562
    {
563
        //
564
    }
3✔
565

566
    /**
567
     * Register the routes.
568
     */
569
    public function registerRoutes(Request $request, Router $router): void
198✔
570
    {
571
        $this->__registerRoutes($request, $router);
198✔
572

573
        $router->group([
198✔
574
            'prefix' => $this->getRoutePrefix(),
198✔
575
            'middleware' => $this->getRouteMiddleware(),
198✔
576
        ], function (Router $router) use ($request): void {
198✔
577
            $this->resolveActions($request)->registerRoutes($request, $router);
198✔
578
            $this->resolveWidgets($request)->registerRoutes($request, $router);
198✔
579

580
            $router->prefix('{resourceModel}')->group(function (Router $router) use ($request): void {
198✔
581
                $this->resolveFields($request)->registerRoutes($request, $router);
198✔
582
            });
198✔
583
        });
198✔
584
    }
585

586
    /**
587
     * Get the route middleware for the registered routes.
588
     */
589
    public function getRouteMiddleware(): array
198✔
590
    {
591
        return [
198✔
592
            Authorize::class.':_resource',
198✔
593
        ];
198✔
594
    }
595

596
    /**
597
     * Handle the route matched event.
598
     */
599
    public function routeMatched(RouteMatched $event): void
25✔
600
    {
601
        $event->route->defaults('resource', $this->getKey());
25✔
602

603
        $controller = $event->route->getController();
25✔
604

605
        $controller->middleware($this->getRouteMiddleware());
25✔
606

607
        if (! is_null($this->getPolicy())) {
25✔
608
            $controller->authorizeResource($this->getModel(), 'resourceModel');
×
609
        }
610

611
        $this->__routeMatched($event);
25✔
612
    }
613

614
    /**
615
     * Get the instance as an array.
616
     */
617
    public function toArray(): array
4✔
618
    {
619
        return [
4✔
620
            'icon' => $this->getIcon(),
4✔
621
            'key' => $this->getKey(),
4✔
622
            'model' => $this->getModel(),
4✔
623
            'modelName' => $this->getModelName(),
4✔
624
            'name' => $this->getName(),
4✔
625
            'uriKey' => $this->getUriKey(),
4✔
626
            'url' => $this->getUri(),
4✔
627
            'baseUrl' => $this->getUri(),
4✔
628
        ];
4✔
629
    }
630

631
    /**
632
     * Get the index representation of the resource.
633
     */
634
    public function toIndex(Request $request): array
1✔
635
    {
636
        return array_merge($this->toArray(), [
1✔
637
            'template' => 'root::resources.index',
1✔
638
            'title' => $this->getName(),
1✔
639
            'standaloneActions' => $this->resolveActions($request)
1✔
640
                ->authorized($request, $model = $this->getModelInstance())
1✔
641
                ->standalone()
1✔
642
                ->mapToForms($request, $model),
1✔
643
            'actions' => $this->resolveActions($request)
1✔
644
                ->authorized($request, $model = $this->getModelInstance())
1✔
645
                ->visible('index')
1✔
646
                ->standalone(false)
1✔
647
                ->mapToForms($request, $model),
1✔
648
            'data' => $this->paginate($request),
1✔
649
            'widgets' => $this->resolveWidgets($request)
1✔
650
                ->authorized($request)
1✔
651
                ->visible('index')
1✔
652
                ->mapToDisplay($request),
1✔
653
            'perPageOptions' => $this->getPerPageOptions(),
1✔
654
            'perPageKey' => $this->getPerPageKey(),
1✔
655
            'sortKey' => $this->getSortKey(),
1✔
656
            'filters' => $this->resolveFilters($request)
1✔
657
                ->authorized($request)
1✔
658
                ->renderable()
1✔
659
                ->map(static fn (RenderableFilter $filter): array => $filter->toField()->toInput($request, $model))
1✔
660
                ->all(),
1✔
661
            'activeFilters' => $this->resolveFilters($request)->active($request)->count(),
1✔
662
            'url' => $this->getUri(),
1✔
663
            'abilities' => $this->mapResourceAbilities($request),
1✔
664
        ]);
1✔
665
    }
666

667
    /**
668
     * Get the create representation of the resource.
669
     */
670
    public function toCreate(Request $request): array
1✔
671
    {
672
        return array_merge($this->toArray(), [
1✔
673
            'template' => 'root::resources.form',
1✔
674
            'title' => __('Create :resource', ['resource' => $this->getModelName()]),
1✔
675
            'model' => $model = $this->getModelInstance(),
1✔
676
            'action' => $this->getUri(),
1✔
677
            'method' => 'POST',
1✔
678
            'uploads' => $this->hasFileField($request),
1✔
679
            'fields' => $this->resolveFields($request)
1✔
680
                ->subResource(false)
1✔
681
                ->authorized($request, $model)
1✔
682
                ->visible('create')
1✔
683
                ->mapToInputs($request, $model),
1✔
684
            'abilities' => $this->mapResourceAbilities($request),
1✔
685
        ]);
1✔
686
    }
687

688
    /**
689
     * Get the edit representation of the resource.
690
     */
691
    public function toShow(Request $request, Model $model): array
1✔
692
    {
693
        return array_merge($this->toArray(), [
1✔
694
            'template' => 'root::resources.show',
1✔
695
            'title' => sprintf('%s: %s', $this->getModelName(), $this->modelTitle($model)),
1✔
696
            'model' => $model,
1✔
697
            'action' => $this->modelUrl($model),
1✔
698
            'fields' => $this->resolveFields($request)
1✔
699
                ->subResource(false)
1✔
700
                ->authorized($request, $model)
1✔
701
                ->visible('show')
1✔
702
                ->mapToDisplay($request, $model),
1✔
703
            'actions' => $this->resolveActions($request)
1✔
704
                ->authorized($request, $model)
1✔
705
                ->visible('show')
1✔
706
                ->standalone(false)
1✔
707
                ->mapToForms($request, $model),
1✔
708
            'widgets' => $this->resolveWidgets($request)
1✔
709
                ->authorized($request, $model)
1✔
710
                ->visible('show')
1✔
711
                ->mapToDisplay($request),
1✔
712
            'relations' => $this->resolveFields($request)
1✔
713
                ->subResource()
1✔
714
                ->authorized($request, $model)
1✔
715
                ->map(static fn (Relation $relation): array => array_merge($relation->toSubResource($request, $model), [
1✔
716
                    'url' => URL::query($relation->modelUrl($model), $relation->parseQueryString($request->fullUrl())),
1✔
717
                ])),
1✔
718
            'abilities' => array_merge(
1✔
719
                $this->mapResourceAbilities($request),
1✔
720
                $this->mapModelAbilities($request, $model)
1✔
721
            ),
1✔
722
        ]);
1✔
723
    }
724

725
    /**
726
     * Get the edit representation of the resource.
727
     */
728
    public function toEdit(Request $request, Model $model): array
1✔
729
    {
730
        return array_merge($this->toArray(), [
1✔
731
            'template' => 'root::resources.form',
1✔
732
            'title' => __('Edit :resource: :model', ['resource' => $this->getModelName(), 'model' => $this->modelTitle($model)]),
1✔
733
            'model' => $model,
1✔
734
            'action' => $this->modelUrl($model),
1✔
735
            'method' => 'PATCH',
1✔
736
            'uploads' => $this->hasFileField($request),
1✔
737
            'fields' => $this->resolveFields($request)
1✔
738
                ->subResource(false)
1✔
739
                ->authorized($request, $model)
1✔
740
                ->visible('update')
1✔
741
                ->mapToInputs($request, $model),
1✔
742
            'abilities' => array_merge(
1✔
743
                $this->mapResourceAbilities($request),
1✔
744
                $this->mapModelAbilities($request, $model)
1✔
745
            ),
1✔
746
        ]);
1✔
747
    }
748
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc