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

conedevelopment / root / 20618156642

31 Dec 2025 11:33AM UTC coverage: 75.542% (-0.5%) from 76.05%
20618156642

push

github

iamgergo
fixes

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

93 existing lines in 4 files now uncovered.

3450 of 4567 relevant lines covered (75.54%)

33.14 hits per line

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

59.52
/src/Fields/MorphTo.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Cone\Root\Fields;
6

7
use Closure;
8
use Cone\Root\Fields\Select as SelectField;
9
use Cone\Root\Filters\Select;
10
use Illuminate\Database\Eloquent\Builder;
11
use Illuminate\Database\Eloquent\Model;
12
use Illuminate\Database\Eloquent\Relations\MorphTo as EloquentRelation;
13
use Illuminate\Http\Request;
14
use Illuminate\Support\Str;
15

16
/**
17
 * @extends \Cone\Root\Fields\BelongsTo<\Illuminate\Database\Eloquent\Relations\MorphTo>
18
 */
19
class MorphTo extends BelongsTo
20
{
21
    /**
22
     * The Blade template.
23
     */
24
    protected string $template = 'root::fields.relation';
25

26
    /**
27
     * Indicates whether the field is async.
28
     */
29
    protected bool $async = true;
30

31
    /**
32
     * The morph types.
33
     */
34
    protected array $types = [];
35

36
    /**
37
     * {@inheritdoc}
38
     */
39
    public function getRelation(Model $model): EloquentRelation
5✔
40
    {
41
        return parent::getRelation($model);
5✔
42
    }
43

44
    /**
45
     * {@inheritdoc}
46
     */
UNCOV
47
    public function async(bool $value = true): static
×
48
    {
UNCOV
49
        return $this;
×
50
    }
51

52
    /**
53
     * Get the morph types.
54
     */
55
    public function getTypes(): array
2✔
56
    {
57
        return $this->types;
2✔
58
    }
59

60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function filters(Request $request): array
2✔
64
    {
65
        $typeFilter = new class($this) extends Select
2✔
66
        {
2✔
67
            public function __construct(protected MorphTo $field)
68
            {
69
                parent::__construct('type');
2✔
70
            }
71

72
            public function getName(): string
73
            {
74
                return __('Type');
2✔
75
            }
76

77
            public function apply(Request $request, Builder $query, mixed $value): Builder
78
            {
UNCOV
79
                return $query;
×
80
            }
81

82
            public function options(Request $request): array
83
            {
84
                return array_map(
2✔
85
                    static function (string $type): string {
2✔
86
                        return __(Str::of($type)->classBasename()->headline()->value());
2✔
87
                    },
2✔
88
                    array_combine($this->field->getTypes(), $this->field->getTypes())
2✔
89
                );
2✔
90
            }
91

92
            public function toField(): SelectField
93
            {
94
                return parent::toField()
2✔
95
                    ->value(function (Request $request, Model $model): ?string {
2✔
96
                        return $model->getAttribute($this->field->getRelation($model)->getMorphType());
2✔
97
                    });
2✔
98
            }
99
        };
2✔
100

101
        return array_merge([$typeFilter], parent::filters($request));
2✔
102
    }
103

104
    /**
105
     * {@inheritdoc}
106
     */
UNCOV
107
    public function resolveRelatableQuery(Request $request, Model $model): Builder
×
108
    {
UNCOV
109
        $relation = $this->getRelation($model);
×
110

UNCOV
111
        $type = Str::before((string) $this->getOldValue($request), ':') ?: $request->query($relation->getMorphType());
×
112

UNCOV
113
        $model->setAttribute(
×
UNCOV
114
            $relation->getMorphType(),
×
UNCOV
115
            $type ?: $model->getAttribute($relation->getMorphType()) ?: ($this->types[0] ?? null)
×
UNCOV
116
        );
×
117

UNCOV
118
        return parent::resolveRelatableQuery($request, $model);
×
119
    }
120

121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function getSearchableColumns(): array
2✔
125
    {
126
        return match (true) {
127
            array_is_list($this->searchableColumns) => array_fill_keys($this->types, $this->searchableColumns),
2✔
128
            default => $this->searchableColumns,
2✔
129
        };
130
    }
131

132
    /**
133
     * Map the async searchable fields.
134
     */
135
    protected function mapAsyncSearchableFields(Request $request): Fields
2✔
136
    {
137
        $fields = new Fields;
2✔
138

139
        foreach ($this->getSearchableColumns() as $type => $columns) {
2✔
140
            foreach ($columns as $column) {
2✔
141
                $field = Hidden::make($this->getRelationName(), sprintf('%s:%s', $type, $column))
2✔
142
                    ->searchable(callback: function (Request $request, Builder $query, mixed $value, string $attribute): Builder {
2✔
UNCOV
143
                        [$type, $column] = explode(':', $attribute);
×
144

UNCOV
145
                        return match ($query->getModel()::class) {
×
UNCOV
146
                            $type => $query->where($query->qualifyColumn($column), 'like', "%{$value}%", 'or'),
×
UNCOV
147
                            default => $query,
×
UNCOV
148
                        };
×
149
                    });
2✔
150

151
                $fields->push($field);
2✔
152
            }
153
        }
154

155
        return $fields;
2✔
156
    }
157

158
    /**
159
     * {@inheritdoc}
160
     */
UNCOV
161
    public function searchable(bool|Closure $value = true, ?Closure $callback = null, array $columns = ['id']): static
×
162
    {
UNCOV
163
        $columns = match (true) {
×
UNCOV
164
            array_is_list($columns) => array_fill_keys($this->types, $columns),
×
UNCOV
165
            default => $columns,
×
UNCOV
166
        };
×
167

UNCOV
168
        return parent::searchable($value, $callback, $columns);
×
169
    }
170

171
    /**
172
     * Resolve the filter query.
173
     */
UNCOV
174
    public function resolveSearchQuery(Request $request, Builder $query, mixed $value): Builder
×
175
    {
UNCOV
176
        if (! $this->isSearchable()) {
×
UNCOV
177
            return parent::resolveSearchQuery($request, $query, $value);
×
178
        }
179

UNCOV
180
        return call_user_func_array($this->searchQueryResolver, [
×
UNCOV
181
            $request, $query, $value, $this->getSearchableColumns(),
×
UNCOV
182
        ]);
×
183
    }
184

185
    /**
186
     * {@inheritdoc}
187
     */
188
    public function resolveHydrate(Request $request, Model $model, mixed $value): void
3✔
189
    {
190
        $value = is_null($value) ? $value : explode(':', $value);
3✔
191

192
        $related = match (true) {
3✔
193
            ! is_null($value) => tap(new $value[0], static function (Model $related) use ($value): void {
3✔
UNCOV
194
                $related->forceFill([$related->getKeyName() => $value[1]]);
×
195
            }),
3✔
196
            default => $value,
3✔
197
        };
3✔
198

199
        parent::resolveHydrate($request, $model, $related);
3✔
200
    }
201

202
    /**
203
     * {@inheritdoc}
204
     */
UNCOV
205
    public function newOption(Model $related, string $label): Option
×
206
    {
UNCOV
207
        return new Option(
×
UNCOV
208
            sprintf('%s:%s', $related::class, $related->getKey()),
×
UNCOV
209
            sprintf('%s (%s)', $label, __(Str::of($related::class)->classBasename()->headline()->value()))
×
UNCOV
210
        );
×
211
    }
212

213
    /**
214
     * Set the morph types.
215
     */
216
    public function types(array $types): static
196✔
217
    {
218
        $this->types = $types;
196✔
219

220
        return $this;
196✔
221
    }
222

223
    /**
224
     * {@inheritdoc}
225
     */
226
    public function toInput(Request $request, Model $model): array
2✔
227
    {
228
        return array_merge(parent::toInput($request, $model), [
2✔
229
            'url' => sprintf('%s/search', $this->replaceRoutePlaceholders($request->route())),
2✔
230
            'morphTypeName' => $name = $this->getRelation($model)->getMorphType(),
2✔
231
            'morphType' => $model->getAttribute($name),
2✔
232
        ]);
2✔
233
    }
234
}
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