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

conedevelopment / root / 20656991146

02 Jan 2026 11:33AM UTC coverage: 74.719% (-0.7%) from 75.421%
20656991146

push

github

iamgergo
fix

3458 of 4628 relevant lines covered (74.72%)

32.75 hits per line

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

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

3
declare(strict_types=1);
4

5
namespace Cone\Root\Resources;
6

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

56
abstract class Resource implements Arrayable, Form
57
{
58
    use AsForm {
59
        AsForm::resolveFields as __resolveFields;
60
    }
61
    use Authorizable;
62
    use RegistersRoutes {
63
        RegistersRoutes::registerRoutes as __registerRoutes;
64
        RegistersRoutes::routeMatched as __routeMatched;
65
    }
66
    use ResolvesActions;
67
    use ResolvesFilters;
68
    use ResolvesWidgets;
69

70
    /**
71
     * The model class.
72
     */
73
    protected string $model;
74

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

80
    /**
81
     * The relations to eager load on every query.
82
     */
83
    protected array $withCount = [];
84

85
    /**
86
     * The icon for the resource.
87
     */
88
    protected string $icon = 'archive';
89

90
    /**
91
     * The group for the resource.
92
     */
93
    protected string $group = 'General';
94

95
    /**
96
     * Boot the resource.
97
     */
98
    public function boot(Root $root): void
196✔
99
    {
100
        $root->routes(function (Router $router) use ($root): void {
196✔
101
            $this->registerRoutes($root->app['request'], $router);
196✔
102
        });
196✔
103

104
        $root->navigation->location('sidebar')->new(
196✔
105
            $this->getUri(),
196✔
106
            $this->getName(),
196✔
107
            ['icon' => $this->getIcon(), 'group' => __($this->group)],
196✔
108
        );
196✔
109
    }
110

111
    /**
112
     * Get the model for the resource.
113
     */
114
    public function getModel(): string
196✔
115
    {
116
        return $this->model;
196✔
117
    }
118

119
    /**
120
     * Get the key.
121
     */
122
    public function getKey(): string
196✔
123
    {
124
        return Str::of($this->getModel())->classBasename()->plural()->kebab()->value();
196✔
125
    }
126

127
    /**
128
     * Get the URI key.
129
     */
130
    public function getUriKey(): string
196✔
131
    {
132
        return $this->getKey();
196✔
133
    }
134

135
    /**
136
     * Get the route prefix.
137
     */
138
    public function getRoutePrefix(): string
196✔
139
    {
140
        return sprintf('resources/%s', $this->getUriKey());
196✔
141
    }
142

143
    /**
144
     * Get the route parameter name.
145
     */
146
    public function getRouteParameterName(): string
25✔
147
    {
148
        return '_resource';
25✔
149
    }
150

151
    /**
152
     * Get the name.
153
     */
154
    public function getName(): string
196✔
155
    {
156
        return __(Str::of($this->getModel())->classBasename()->headline()->plural()->value());
196✔
157
    }
158

159
    /**
160
     * Get the model name.
161
     */
162
    public function getModelName(): string
5✔
163
    {
164
        return __(Str::of($this->getModel())->classBasename()->headline()->value());
5✔
165
    }
166

167
    /**
168
     * Get the model instance.
169
     */
170
    public function getModelInstance(): Model
26✔
171
    {
172
        return new ($this->getModel());
26✔
173
    }
174

175
    /**
176
     * Set the resource icon.
177
     */
178
    public function icon(string $icon): static
1✔
179
    {
180
        $this->icon = $icon;
1✔
181

182
        return $this;
1✔
183
    }
184

185
    /**
186
     * Get the resource icon.
187
     */
188
    public function getIcon(): string
196✔
189
    {
190
        return $this->icon;
196✔
191
    }
192

193
    /**
194
     * Get the policy for the model.
195
     */
196
    public function getPolicy(): mixed
24✔
197
    {
198
        return Gate::getPolicyFor($this->getModel());
24✔
199
    }
200

201
    /**
202
     * Resolve the ability.
203
     */
204
    public function resolveAbility(string $ability, Request $request, Model $model, ...$arguments): bool
4✔
205
    {
206
        $policy = $this->getPolicy();
4✔
207

208
        return is_null($policy)
4✔
209
            || ! method_exists($policy, $ability)
4✔
210
            || Gate::allows($ability, [$model, ...$arguments]);
4✔
211
    }
212

213
    /**
214
     * Map the resource abilities.
215
     */
216
    public function mapResourceAbilities(Request $request): array
4✔
217
    {
218
        return [
4✔
219
            'viewAny' => $this->resolveAbility('viewAny', $request, $this->getModelInstance()),
4✔
220
            'create' => $this->resolveAbility('create', $request, $this->getModelInstance()),
4✔
221
        ];
4✔
222
    }
223

224
    /**
225
     * Map the model abilities.
226
     */
227
    public function mapModelAbilities(Request $request, Model $model): array
3✔
228
    {
229
        return [
3✔
230
            'view' => $this->resolveAbility('view', $request, $model),
3✔
231
            'update' => $this->resolveAbility('update', $request, $model),
3✔
232
            'restore' => $this->resolveAbility('restore', $request, $model),
3✔
233
            'delete' => $this->resolveAbility('delete', $request, $model),
3✔
234
            'forceDelete' => $this->resolveAbility('forceDelete', $request, $model),
3✔
235
        ];
3✔
236
    }
237

238
    /**
239
     * Set the relations to eagerload.
240
     */
241
    public function with(array $relations): static
1✔
242
    {
243
        $this->with = $relations;
1✔
244

245
        return $this;
1✔
246
    }
247

248
    /**
249
     * Set the relation counts to eagerload.
250
     */
251
    public function withCount(array $relations): static
×
252
    {
253
        $this->withCount = $relations;
×
254

255
        return $this;
×
256
    }
257

258
    /**
259
     * Make a new Eloquent query instance.
260
     */
261
    public function query(): Builder
23✔
262
    {
263
        return $this->getModelInstance()->newQuery()->with($this->with)->withCount($this->withCount);
23✔
264
    }
265

266
    /**
267
     * Resolve the query for the given request.
268
     */
269
    public function resolveQuery(Request $request): Builder
23✔
270
    {
271
        return $this->query();
23✔
272
    }
273

274
    /**
275
     * Resolve the filtered query for the given request.
276
     */
277
    public function resolveFilteredQuery(Request $request): Builder
3✔
278
    {
279
        return $this->resolveFilters($request)->apply($request, $this->resolveQuery($request));
3✔
280
    }
281

282
    /**
283
     * Resolve the route binding query.
284
     */
285
    public function resolveRouteBindingQuery(Request $request): Builder
19✔
286
    {
287
        return $this->resolveQuery($request)->when(
19✔
288
            $this->isSoftDeletable(),
19✔
289
            static fn (Builder $query): Builder => $query->withTrashed()
19✔
290
        );
19✔
291
    }
292

293
    /**
294
     * Resolve the resource model for a bound value.
295
     */
296
    public function resolveRouteBinding(Request $request, string $id): Model
19✔
297
    {
298
        return $this->resolveRouteBindingQuery($request)->findOrFail($id);
19✔
299
    }
300

301
    /**
302
     * Determine if the model soft deletable.
303
     */
304
    public function isSoftDeletable(): bool
23✔
305
    {
306
        return in_array(SoftDeletes::class, class_uses_recursive($this->getModel()));
23✔
307
    }
308

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

317
    /**
318
     * Get the title for the model.
319
     */
320
    public function modelTitle(Model $model): string
7✔
321
    {
322
        return match (true) {
323
            ! $model->exists => __('New :model', ['model' => $this->getModelName()]),
7✔
324
            default => (string) $model->getKey(),
7✔
325
        };
326
    }
327

328
    /**
329
     * Determine whether the resource model has root events.
330
     */
331
    public function hasRootEvents(): bool
196✔
332
    {
333
        return in_array(HasRootEvents::class, class_uses_recursive($this->getModel()));
196✔
334
    }
335

336
    /**
337
     * Resolve the events field.
338
     */
339
    public function resolveEventsField(Request $request): ?Events
196✔
340
    {
341
        return $this->hasRootEvents() ? new Events : null;
196✔
342
    }
343

344
    /**
345
     * Determine whether the resource model has root events.
346
     */
347
    public function translatable(): bool
196✔
348
    {
349
        return in_array(Translatable::class, class_uses_recursive($this->getModel()));
196✔
350
    }
351

352
    /**
353
     * Resolve the translations field.
354
     */
355
    public function resolveTranslationsField(Request $request): ?Translations
196✔
356
    {
357
        if (! $this->translatable()) {
196✔
358
            return null;
×
359
        }
360

361
        return Translations::make()
196✔
362
            ->withFields(function () use ($request): array {
196✔
363
                return $this->resolveFields($request)
196✔
364
                    ->translatable()
196✔
365
                    ->map(static function (Field $field): Field {
196✔
366
                        return (clone $field)
×
367
                            ->translatable(false)
×
368
                            ->value(static function (Request $request, Translation $model) use ($field): mixed {
×
369
                                return $model->values[$field->getModelAttribute()] ?? null;
×
370
                            })
×
371
                            ->hydrate(static function (Request $request, Translation $model, mixed $value) use ($field): void {
×
372
                                $model->values[$field->getModelAttribute()] = $value;
×
373
                            });
×
374
                    })->all();
196✔
375
            });
196✔
376
    }
377

378
    /**
379
     * Resolve the fields collection.
380
     */
381
    public function resolveFields(Request $request): Fields
196✔
382
    {
383
        if (is_null($this->fields)) {
196✔
384
            $this->withFields(fn (): array => array_values(array_filter([
196✔
385
                $this->resolveTranslationsField($request),
196✔
386
                $this->resolveEventsField($request),
196✔
387
            ])));
196✔
388
        }
389

390
        return $this->__resolveFields($request);
196✔
391
    }
392

393
    /**
394
     * Define the filters for the object.
395
     */
396
    public function filters(Request $request): array
4✔
397
    {
398
        $fields = $this->resolveFields($request)->authorized($request);
4✔
399

400
        $searchables = $fields->searchable();
4✔
401

402
        $sortables = $fields->sortable();
4✔
403

404
        $filterables = $fields->filterable();
4✔
405

406
        return array_values(array_filter([
4✔
407
            $searchables->isNotEmpty() ? new Search($searchables) : null,
4✔
408
            $sortables->isNotEmpty() ? new Sort($sortables) : null,
4✔
409
            $this->isSoftDeletable() ? new TrashStatus : null,
4✔
410
            ...$filterables->map->toFilter()->all(),
4✔
411
        ]));
4✔
412
    }
413

414
    /**
415
     * Handle the callback for the field resolution.
416
     */
417
    protected function resolveField(Request $request, Field $field): void
196✔
418
    {
419
        $field->setAttribute('form', $this->getKey());
196✔
420
        $field->resolveErrorsUsing(fn (Request $request): MessageBag => $this->errors($request));
196✔
421

422
        if ($field instanceof Relation) {
196✔
423
            $field->resolveRouteKeyNameUsing(fn (): string => Str::of($field->getRelationName())->singular()->ucfirst()->prepend($this->getKey())->value());
196✔
424
        }
425
    }
426

427
    /**
428
     * Handle the callback for the filter resolution.
429
     */
430
    protected function resolveFilter(Request $request, Filter $filter): void
4✔
431
    {
432
        $filter->setKey(sprintf('%s_%s', $this->getKey(), $filter->getKey()));
4✔
433
    }
434

435
    /**
436
     * Handle the callback for the action resolution.
437
     */
438
    protected function resolveAction(Request $request, Action $action): void
196✔
439
    {
440
        $action->withQuery(fn (): Builder => $this->resolveFilteredQuery($request));
196✔
441
    }
442

443
    /**
444
     * Handle the callback for the widget resolution.
445
     */
446
    protected function resolveWidget(Request $request, Widget $widget): void
196✔
447
    {
448
        if ($widget instanceof Metric) {
196✔
449
            $widget->withQuery(fn (): Builder => $this->resolveQuery($request));
196✔
450
        }
451
    }
452

453
    /**
454
     * Get the per page options.
455
     */
456
    public function getPerPageOptions(): array
1✔
457
    {
458
        return Collection::make([$this->getModelInstance()->getPerPage()])
1✔
459
            ->merge([15, 25, 50, 100])
1✔
460
            ->filter()
1✔
461
            ->unique()
1✔
462
            ->values()
1✔
463
            ->toArray();
1✔
464
    }
465

466
    /**
467
     * Get the per page key.
468
     */
469
    public function getPerPageKey(): string
1✔
470
    {
471
        return sprintf('%s_per_page', $this->getKey());
1✔
472
    }
473

474
    /**
475
     * Get the sort key.
476
     */
477
    public function getSortKey(): string
1✔
478
    {
479
        return sprintf('%s_sort', $this->getKey());
1✔
480
    }
481

482
    /**
483
     * Perform the query and the pagination.
484
     */
485
    public function paginate(Request $request): LengthAwarePaginator
1✔
486
    {
487
        return $this->resolveFilteredQuery($request)
1✔
488
            ->tap(function (Builder $query) use ($request): void {
1✔
489
                $this->resolveFields($request)
1✔
490
                    ->authorized($request, $query->getModel())
1✔
491
                    ->visible('index')
1✔
492
                    ->filter(fn (Field $field): bool => $field instanceof Relation)
1✔
493
                    ->each(static function (Relation $relation) use ($query, $request): void {
1✔
494
                        if ($relation instanceof BelongsToMany || $relation instanceof HasMany || $relation instanceof MorphMany) {
1✔
495
                            $relation->resolveAggregate($request, $query);
1✔
496
                        } elseif ($relation instanceof Meta) {
1✔
497
                            $query->with('metaData');
×
498
                        } else {
499
                            $query->with($relation->getRelationName());
1✔
500
                        }
501
                    });
1✔
502
            })
1✔
503
            ->latest()
1✔
504
            ->paginate($request->input($this->getPerPageKey()))
1✔
505
            ->withQueryString()
1✔
506
            ->through(fn (Model $model): array => $this->mapModel($request, $model));
1✔
507
    }
508

509
    /**
510
     * Map the model.
511
     */
512
    public function mapModel(Request $request, Model $model): array
1✔
513
    {
514
        return [
1✔
515
            'id' => $model->getKey(),
1✔
516
            'url' => $this->modelUrl($model),
1✔
517
            'model' => $model,
1✔
518
            'abilities' => $this->mapModelAbilities($request, $model),
1✔
519
            'fields' => $this->resolveFields($request)
1✔
520
                ->authorized($request, $model)
1✔
521
                ->visible('index')
1✔
522
                ->mapToDisplay($request, $model),
1✔
523
        ];
1✔
524
    }
525

526
    /**
527
     * Handle the request.
528
     */
529
    public function handleFormRequest(Request $request, Model $model): Response
3✔
530
    {
531
        $this->validateFormRequest($request, $model);
3✔
532

533
        try {
534
            return DB::transaction(function () use ($request, $model): Response {
3✔
535
                $this->resolveFields($request)
3✔
536
                    ->authorized($request, $model)
3✔
537
                    ->visible($request->isMethod('POST') ? 'create' : 'update')
3✔
538
                    ->subResource(false)
3✔
539
                    ->persist($request, $model);
3✔
540

541
                $this->saving($request, $model);
3✔
542

543
                $model->save();
3✔
544

545
                if (in_array(HasRootEvents::class, class_uses_recursive($model))) {
3✔
546
                    $model->recordRootEvent(
3✔
547
                        $model->wasRecentlyCreated ? 'Created' : 'Updated',
3✔
548
                        $request->user()
3✔
549
                    );
3✔
550
                }
551

552
                $this->saved($request, $model);
3✔
553

554
                return $this->formResponse($request, $model);
3✔
555
            });
3✔
556
        } catch (Throwable $exception) {
×
557
            report($exception);
×
558

559
            throw new SaveFormDataException($exception->getMessage());
×
560
        }
561
    }
562

563
    /**
564
     * Hydrate the model with the request data.
565
     */
566
    public function handleHydrateRequest(Request $request, Model $model): void
×
567
    {
568
        DB::transaction(function () use ($request, $model): void {
×
569
            $this->resolveFields($request)
×
570
                ->authorized($request, $model)
×
571
                ->visible($request->isMethod('POST') ? 'create' : 'update')
×
572
                ->subResource(false)
×
573
                ->each(static function (Field $field) use ($request, $model): void {
×
574
                    $field->resolveHydrate($request, $model, $field->getValueForHydrate($request));
×
575
                });
×
576
        });
×
577
    }
578

579
    /**
580
     * Make a form response.
581
     */
582
    public function formResponse(Request $request, Model $model): Response
3✔
583
    {
584
        return Redirect::to($this->modelUrl($model))
3✔
585
            ->with('alerts.resource-saved', Alert::success(__('The resource has been saved!')));
3✔
586
    }
587

588
    /**
589
     * Handle the saving form event.
590
     */
591
    public function saving(Request $request, Model $model): void
3✔
592
    {
593
        //
594
    }
3✔
595

596
    /**
597
     * Handle the saved form event.
598
     */
599
    public function saved(Request $request, Model $model): void
3✔
600
    {
601
        //
602
    }
3✔
603

604
    /**
605
     * Register the routes.
606
     */
607
    public function registerRoutes(Request $request, Router $router): void
196✔
608
    {
609
        $this->__registerRoutes($request, $router);
196✔
610

611
        $router->group([
196✔
612
            'prefix' => $this->getRoutePrefix(),
196✔
613
            'middleware' => $this->getRouteMiddleware(),
196✔
614
        ], function (Router $router) use ($request): void {
196✔
615
            $this->resolveActions($request)->registerRoutes($request, $router);
196✔
616
            $this->resolveWidgets($request)->registerRoutes($request, $router);
196✔
617

618
            $router->prefix('{resourceModel}')->group(function (Router $router) use ($request): void {
196✔
619
                $this->resolveFields($request)->registerRoutes($request, $router);
196✔
620
            });
196✔
621
        });
196✔
622
    }
623

624
    /**
625
     * Get the route middleware for the registered routes.
626
     */
627
    public function getRouteMiddleware(): array
196✔
628
    {
629
        return [
196✔
630
            Authorize::class.':_resource',
196✔
631
        ];
196✔
632
    }
633

634
    /**
635
     * Handle the route matched event.
636
     */
637
    public function routeMatched(RouteMatched $event): void
24✔
638
    {
639
        $event->route->defaults('resource', $this->getKey());
24✔
640

641
        if ($controller = $event->route->getController()) {
24✔
642
            $controller->middleware($this->getRouteMiddleware());
24✔
643

644
            if (! is_null($this->getPolicy())) {
24✔
645
                $controller->authorizeResource($this->getModel(), 'resourceModel');
×
646
            }
647
        }
648

649
        $this->__routeMatched($event);
24✔
650
    }
651

652
    /**
653
     * Get the instance as an array.
654
     */
655
    public function toArray(): array
4✔
656
    {
657
        return [
4✔
658
            'icon' => $this->getIcon(),
4✔
659
            'key' => $this->getKey(),
4✔
660
            'model' => $this->getModel(),
4✔
661
            'modelName' => $this->getModelName(),
4✔
662
            'name' => $this->getName(),
4✔
663
            'uriKey' => $this->getUriKey(),
4✔
664
            'url' => $this->getUri(),
4✔
665
            'baseUrl' => $this->getUri(),
4✔
666
        ];
4✔
667
    }
668

669
    /**
670
     * Get the index representation of the resource.
671
     */
672
    public function toIndex(Request $request): array
1✔
673
    {
674
        return array_merge($this->toArray(), [
1✔
675
            'template' => 'root::resources.index',
1✔
676
            'title' => $this->getName(),
1✔
677
            'standaloneActions' => $this->resolveActions($request)
1✔
678
                ->authorized($request, $model = $this->getModelInstance())
1✔
679
                ->standalone()
1✔
680
                ->mapToForms($request, $model),
1✔
681
            'actions' => $this->resolveActions($request)
1✔
682
                ->authorized($request, $model = $this->getModelInstance())
1✔
683
                ->visible('index')
1✔
684
                ->standalone(false)
1✔
685
                ->mapToForms($request, $model),
1✔
686
            'data' => $this->paginate($request),
1✔
687
            'widgets' => $this->resolveWidgets($request)
1✔
688
                ->authorized($request)
1✔
689
                ->visible('index')
1✔
690
                ->mapToDisplay($request),
1✔
691
            'perPageOptions' => $this->getPerPageOptions(),
1✔
692
            'perPageKey' => $this->getPerPageKey(),
1✔
693
            'sortKey' => $this->getSortKey(),
1✔
694
            'filters' => $this->resolveFilters($request)
1✔
695
                ->authorized($request)
1✔
696
                ->renderable()
1✔
697
                ->map(static fn (RenderableFilter $filter): array => $filter->toField()->toInput($request, $model))
1✔
698
                ->all(),
1✔
699
            'activeFilters' => $this->resolveFilters($request)->active($request)->count(),
1✔
700
            'url' => $this->getUri(),
1✔
701
            'abilities' => $this->mapResourceAbilities($request),
1✔
702
        ]);
1✔
703
    }
704

705
    /**
706
     * Get the create representation of the resource.
707
     */
708
    public function toCreate(Request $request): array
1✔
709
    {
710
        return array_merge($this->toArray(), [
1✔
711
            'template' => 'root::resources.form',
1✔
712
            'title' => __('Create :resource', ['resource' => $this->getModelName()]),
1✔
713
            'model' => $model = $this->getModelInstance(),
1✔
714
            'action' => $this->getUri(),
1✔
715
            'hydrateUrl' => sprintf('%s/create/hydrate', $this->getUri()),
1✔
716
            'method' => 'POST',
1✔
717
            'uploads' => $this->hasFileField($request),
1✔
718
            'fields' => $this->resolveFields($request)
1✔
719
                ->subResource(false)
1✔
720
                ->authorized($request, $model)
1✔
721
                ->visible('create')
1✔
722
                ->mapToInputs($request, $model),
1✔
723
            'abilities' => $this->mapResourceAbilities($request),
1✔
724
        ]);
1✔
725
    }
726

727
    /**
728
     * Get the edit representation of the resource.
729
     */
730
    public function toShow(Request $request, Model $model): array
1✔
731
    {
732
        return array_merge($this->toArray(), [
1✔
733
            'template' => 'root::resources.show',
1✔
734
            'title' => sprintf('%s: %s', $this->getModelName(), $this->modelTitle($model)),
1✔
735
            'model' => $model,
1✔
736
            'action' => $this->modelUrl($model),
1✔
737
            'fields' => $this->resolveFields($request)
1✔
738
                ->subResource(false)
1✔
739
                ->authorized($request, $model)
1✔
740
                ->visible('show')
1✔
741
                ->mapToDisplay($request, $model),
1✔
742
            'actions' => $this->resolveActions($request)
1✔
743
                ->authorized($request, $model)
1✔
744
                ->visible('show')
1✔
745
                ->standalone(false)
1✔
746
                ->mapToForms($request, $model),
1✔
747
            'widgets' => $this->resolveWidgets($request)
1✔
748
                ->authorized($request, $model)
1✔
749
                ->visible('show')
1✔
750
                ->mapToDisplay($request),
1✔
751
            'relations' => $this->resolveFields($request)
1✔
752
                ->subResource()
1✔
753
                ->authorized($request, $model)
1✔
754
                ->map(static fn (Relation $relation): array => array_merge($relation->toSubResource($request, $model), [
1✔
755
                    'url' => URL::query($relation->modelUrl($model), $relation->parseQueryString($request->fullUrl())),
1✔
756
                ])),
1✔
757
            'abilities' => array_merge(
1✔
758
                $this->mapResourceAbilities($request),
1✔
759
                $this->mapModelAbilities($request, $model)
1✔
760
            ),
1✔
761
        ]);
1✔
762
    }
763

764
    /**
765
     * Get the edit representation of the resource.
766
     */
767
    public function toEdit(Request $request, Model $model): array
1✔
768
    {
769
        return array_merge($this->toArray(), [
1✔
770
            'template' => 'root::resources.form',
1✔
771
            'title' => __('Edit :resource: :model', ['resource' => $this->getModelName(), 'model' => $this->modelTitle($model)]),
1✔
772
            'model' => $model,
1✔
773
            'action' => $this->modelUrl($model),
1✔
774
            'hydrateUrl' => sprintf('%s/hydrate', $this->modelUrl($model)),
1✔
775
            'method' => 'PATCH',
1✔
776
            'uploads' => $this->hasFileField($request),
1✔
777
            'fields' => $this->resolveFields($request)
1✔
778
                ->subResource(false)
1✔
779
                ->authorized($request, $model)
1✔
780
                ->visible('update')
1✔
781
                ->mapToInputs($request, $model),
1✔
782
            'abilities' => array_merge(
1✔
783
                $this->mapResourceAbilities($request),
1✔
784
                $this->mapModelAbilities($request, $model)
1✔
785
            ),
1✔
786
        ]);
1✔
787
    }
788
}
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

© 2026 Coveralls, Inc