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

conedevelopment / root / 18109928020

29 Sep 2025 08:33PM UTC coverage: 77.523% (-0.09%) from 77.609%
18109928020

push

github

iamgergo
wip

5 of 5 new or added lines in 1 file covered. (100.0%)

14 existing lines in 2 files now uncovered.

3342 of 4311 relevant lines covered (77.52%)

35.2 hits per line

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

97.52
/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\Root;
26
use Cone\Root\Support\Alert;
27
use Cone\Root\Traits\AsForm;
28
use Cone\Root\Traits\Authorizable;
29
use Cone\Root\Traits\HasRootEvents;
30
use Cone\Root\Traits\RegistersRoutes;
31
use Cone\Root\Traits\ResolvesActions;
32
use Cone\Root\Traits\ResolvesFilters;
33
use Cone\Root\Traits\ResolvesWidgets;
34
use Cone\Root\Traits\Translatable;
35
use Cone\Root\Widgets\Metric;
36
use Cone\Root\Widgets\Widget;
37
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
38
use Illuminate\Contracts\Support\Arrayable;
39
use Illuminate\Contracts\Support\MessageBag;
40
use Illuminate\Database\Eloquent\Builder;
41
use Illuminate\Database\Eloquent\Model;
42
use Illuminate\Database\Eloquent\SoftDeletes;
43
use Illuminate\Http\Request;
44
use Illuminate\Routing\Events\RouteMatched;
45
use Illuminate\Routing\Router;
46
use Illuminate\Support\Collection;
47
use Illuminate\Support\Facades\DB;
48
use Illuminate\Support\Facades\Gate;
49
use Illuminate\Support\Facades\Redirect;
50
use Illuminate\Support\Facades\URL;
51
use Illuminate\Support\Str;
52
use Symfony\Component\HttpFoundation\Response;
53
use Throwable;
54

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

181
        return $this;
1✔
182
    }
183

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

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

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

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

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

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

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

244
        return $this;
1✔
245
    }
246

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

254
        return $this;
×
255
    }
256

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

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

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

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

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

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

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

318
    /**
319
     * Get the title for the model.
320
     */
321
    public function modelTitle(Model $model): string
7✔
322
    {
323
        return (string) $model->getKey();
7✔
324
    }
325

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

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

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

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

368
    /**
369
     * Resolve the fields collection.
370
     */
371
    public function resolveFields(Request $request): Fields
197✔
372
    {
373
        if (is_null($this->fields)) {
197✔
374
            $this->withFields(fn (): array => array_values(array_filter([
197✔
375
                $this->resolveTranslationsField($request),
197✔
376
                $this->resolveEventsField($request),
197✔
377
            ])));
197✔
378
        }
379

380
        return $this->__resolveFields($request);
197✔
381
    }
382

383
    /**
384
     * Define the filters for the object.
385
     */
386
    public function filters(Request $request): array
4✔
387
    {
388
        $fields = $this->resolveFields($request)->authorized($request);
4✔
389

390
        $searchables = $fields->searchable();
4✔
391

392
        $sortables = $fields->sortable();
4✔
393

394
        $filterables = $fields->filterable();
4✔
395

396
        return array_values(array_filter([
4✔
397
            $searchables->isNotEmpty() ? new Search($searchables) : null,
4✔
398
            $sortables->isNotEmpty() ? new Sort($sortables) : null,
4✔
399
            $this->isSoftDeletable() ? new TrashStatus : null,
4✔
400
            ...$filterables->map->toFilter()->all(),
4✔
401
        ]));
4✔
402
    }
403

404
    /**
405
     * Handle the callback for the field resolution.
406
     */
407
    protected function resolveField(Request $request, Field $field): void
197✔
408
    {
409
        $field->setAttribute('form', $this->getKey());
197✔
410
        $field->resolveErrorsUsing(fn (Request $request): MessageBag => $this->errors($request));
197✔
411

412
        if ($field instanceof Relation) {
197✔
413
            $field->resolveRouteKeyNameUsing(fn (): string => Str::of($field->getRelationName())->singular()->ucfirst()->prepend($this->getKey())->value());
197✔
414
        }
415
    }
416

417
    /**
418
     * Handle the callback for the filter resolution.
419
     */
420
    protected function resolveFilter(Request $request, Filter $filter): void
4✔
421
    {
422
        $filter->setKey(sprintf('%s_%s', $this->getKey(), $filter->getKey()));
4✔
423
    }
424

425
    /**
426
     * Handle the callback for the action resolution.
427
     */
428
    protected function resolveAction(Request $request, Action $action): void
197✔
429
    {
430
        $action->withQuery(fn (): Builder => $this->resolveFilteredQuery($request));
197✔
431
    }
432

433
    /**
434
     * Handle the callback for the widget resolution.
435
     */
436
    protected function resolveWidget(Request $request, Widget $widget): void
197✔
437
    {
438
        if ($widget instanceof Metric) {
197✔
439
            $widget->withQuery(fn (): Builder => $this->resolveQuery($request));
197✔
440
        }
441
    }
442

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

456
    /**
457
     * Get the per page key.
458
     */
459
    public function getPerPageKey(): string
1✔
460
    {
461
        return sprintf('%s_per_page', $this->getKey());
1✔
462
    }
463

464
    /**
465
     * Get the sort key.
466
     */
467
    public function getSortKey(): string
1✔
468
    {
469
        return sprintf('%s_sort', $this->getKey());
1✔
470
    }
471

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

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

516
    /**
517
     * Handle the request.
518
     */
519
    public function handleFormRequest(Request $request, Model $model): Response
3✔
520
    {
521
        $this->validateFormRequest($request, $model);
3✔
522

523
        try {
524
            return DB::transaction(function () use ($request, $model): Response {
3✔
525
                $this->resolveFields($request)
3✔
526
                    ->authorized($request, $model)
3✔
527
                    ->visible($request->isMethod('POST') ? 'create' : 'update')
3✔
528
                    ->subResource(false)
3✔
529
                    ->persist($request, $model);
3✔
530

531
                $this->saving($request, $model);
3✔
532

533
                $model->save();
3✔
534

535
                if (in_array(HasRootEvents::class, class_uses_recursive($model))) {
3✔
536
                    $model->recordRootEvent(
3✔
537
                        $model->wasRecentlyCreated ? 'Created' : 'Updated',
3✔
538
                        $request->user()
3✔
539
                    );
3✔
540
                }
541

542
                $this->saved($request, $model);
3✔
543

544
                return $this->formResponse($request, $model);
3✔
545
            });
3✔
546
        } catch (Throwable $exception) {
×
547
            report($exception);
×
548

549
            throw new SaveFormDataException($exception->getMessage());
×
550
        }
551
    }
552

553
    /**
554
     * Make a form response.
555
     */
556
    public function formResponse(Request $request, Model $model): Response
3✔
557
    {
558
        return Redirect::to($this->modelUrl($model))
3✔
559
            ->with('alerts.resource-saved', Alert::success(__('The resource has been saved!')));
3✔
560
    }
561

562
    /**
563
     * Handle the saving form event.
564
     */
565
    public function saving(Request $request, Model $model): void
3✔
566
    {
567
        //
568
    }
3✔
569

570
    /**
571
     * Handle the saved form event.
572
     */
573
    public function saved(Request $request, Model $model): void
3✔
574
    {
575
        //
576
    }
3✔
577

578
    /**
579
     * Register the routes.
580
     */
581
    public function registerRoutes(Request $request, Router $router): void
197✔
582
    {
583
        $this->__registerRoutes($request, $router);
197✔
584

585
        $router->group([
197✔
586
            'prefix' => $this->getRoutePrefix(),
197✔
587
            'middleware' => $this->getRouteMiddleware(),
197✔
588
        ], function (Router $router) use ($request): void {
197✔
589
            $this->resolveActions($request)->registerRoutes($request, $router);
197✔
590
            $this->resolveWidgets($request)->registerRoutes($request, $router);
197✔
591

592
            $router->prefix('{resourceModel}')->group(function (Router $router) use ($request): void {
197✔
593
                $this->resolveFields($request)->registerRoutes($request, $router);
197✔
594
            });
197✔
595
        });
197✔
596
    }
597

598
    /**
599
     * Get the route middleware for the registered routes.
600
     */
601
    public function getRouteMiddleware(): array
197✔
602
    {
603
        return [
197✔
604
            Authorize::class.':_resource',
197✔
605
        ];
197✔
606
    }
607

608
    /**
609
     * Handle the route matched event.
610
     */
611
    public function routeMatched(RouteMatched $event): void
25✔
612
    {
613
        $event->route->defaults('resource', $this->getKey());
25✔
614

615
        if ($controller = $event->route->getController()) {
25✔
616
            $controller->middleware($this->getRouteMiddleware());
25✔
617

618
            if (! is_null($this->getPolicy())) {
25✔
UNCOV
619
                $controller->authorizeResource($this->getModel(), 'resourceModel');
×
620
            }
621
        }
622

623
        $this->__routeMatched($event);
25✔
624
    }
625

626
    /**
627
     * Get the instance as an array.
628
     */
629
    public function toArray(): array
4✔
630
    {
631
        return [
4✔
632
            'icon' => $this->getIcon(),
4✔
633
            'key' => $this->getKey(),
4✔
634
            'model' => $this->getModel(),
4✔
635
            'modelName' => $this->getModelName(),
4✔
636
            'name' => $this->getName(),
4✔
637
            'uriKey' => $this->getUriKey(),
4✔
638
            'url' => $this->getUri(),
4✔
639
            'baseUrl' => $this->getUri(),
4✔
640
        ];
4✔
641
    }
642

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

679
    /**
680
     * Get the create representation of the resource.
681
     */
682
    public function toCreate(Request $request): array
1✔
683
    {
684
        return array_merge($this->toArray(), [
1✔
685
            'template' => 'root::resources.form',
1✔
686
            'title' => __('Create :resource', ['resource' => $this->getModelName()]),
1✔
687
            'model' => $model = $this->getModelInstance(),
1✔
688
            'action' => $this->getUri(),
1✔
689
            'method' => 'POST',
1✔
690
            'uploads' => $this->hasFileField($request),
1✔
691
            'fields' => $this->resolveFields($request)
1✔
692
                ->subResource(false)
1✔
693
                ->authorized($request, $model)
1✔
694
                ->visible('create')
1✔
695
                ->mapToInputs($request, $model),
1✔
696
            'abilities' => $this->mapResourceAbilities($request),
1✔
697
        ]);
1✔
698
    }
699

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

737
    /**
738
     * Get the edit representation of the resource.
739
     */
740
    public function toEdit(Request $request, Model $model): array
1✔
741
    {
742
        return array_merge($this->toArray(), [
1✔
743
            'template' => 'root::resources.form',
1✔
744
            'title' => __('Edit :resource: :model', ['resource' => $this->getModelName(), 'model' => $this->modelTitle($model)]),
1✔
745
            'model' => $model,
1✔
746
            'action' => $this->modelUrl($model),
1✔
747
            'method' => 'PATCH',
1✔
748
            'uploads' => $this->hasFileField($request),
1✔
749
            'fields' => $this->resolveFields($request)
1✔
750
                ->subResource(false)
1✔
751
                ->authorized($request, $model)
1✔
752
                ->visible('update')
1✔
753
                ->mapToInputs($request, $model),
1✔
754
            'abilities' => array_merge(
1✔
755
                $this->mapResourceAbilities($request),
1✔
756
                $this->mapModelAbilities($request, $model)
1✔
757
            ),
1✔
758
        ]);
1✔
759
    }
760
}
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