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

conedevelopment / root / 13086943877

01 Feb 2025 08:35AM UTC coverage: 79.285% (+1.2%) from 78.037%
13086943877

push

github

web-flow
Merge pull request #234 from xHeaven/patch-1

Codebase health checkup

195 of 239 new or added lines in 45 files covered. (81.59%)

2 existing lines in 2 files now uncovered.

2572 of 3244 relevant lines covered (79.28%)

35.8 hits per line

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

87.64
/src/Fields/Repeater.php
1
<?php
2

3
namespace Cone\Root\Fields;
4

5
use Closure;
6
use Cone\Root\Http\Controllers\RepeaterController;
7
use Cone\Root\Traits\RegistersRoutes;
8
use Cone\Root\Traits\ResolvesFields;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Http\Request;
11
use Illuminate\Routing\Router;
12
use Illuminate\Support\Facades\View;
13
use Illuminate\Support\Str;
14

15
class Repeater extends Field
16
{
17
    use RegistersRoutes {
18
        RegistersRoutes::registerRoutes as __registerRoutes;
19
    }
20
    use ResolvesFields {
21
        ResolvesFields::withFields as __withFields;
22
    }
23

24
    /**
25
     * The Blade template.
26
     */
27
    protected string $template = 'root::fields.repeater';
28

29
    /**
30
     * The option fields resolver.
31
     */
32
    protected ?Closure $optionFieldsResolver = null;
33

34
    /**
35
     * The maximum number of options.
36
     */
37
    protected ?int $max = null;
38

39
    /**
40
     * Create a new repeater field instance.
41
     */
42
    public function __construct(string $label, Closure|string|null $modelAttribute = null)
43
    {
44
        parent::__construct($label, $modelAttribute);
198✔
45

46
        $this->hiddenOn(['index']);
198✔
47
    }
48

49
    /**
50
     * Get the URI key.
51
     */
52
    public function getUriKey(): string
53
    {
54
        return str_replace('.', '-', $this->getRequestKey());
198✔
55
    }
56

57
    /**
58
     * Get the route parameter name.
59
     */
60
    public function getRouteParameterName(): string
61
    {
62
        return 'field';
1✔
63
    }
64

65
    /**
66
     * Set the maximum number of options.
67
     */
68
    public function max(int $value): static
69
    {
70
        $this->max = $value;
×
71

72
        return $this;
×
73
    }
74

75
    /**
76
     * Get the option name.
77
     */
78
    public function getOptionName(): string
79
    {
80
        return __(Str::singular($this->label));
3✔
81
    }
82

83
    /**
84
     * Get the add new option label.
85
     */
86
    public function getAddNewOptionLabel(): string
87
    {
88
        return __('Add :name', ['name' => $this->getOptionName()]);
2✔
89
    }
90

91
    /**
92
     * {@inheritdoc}
93
     */
94
    public function getValueForHydrate(Request $request): array
95
    {
96
        return array_values((array) parent::getValueForHydrate($request));
3✔
97
    }
98

99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function getOldValue(Request $request): array
103
    {
104
        return array_values((array) parent::getOldValue($request));
×
105
    }
106

107
    /**
108
     * Handle the callback for the field resolution.
109
     */
110
    protected function resolveField(Request $request, Field $field): void
111
    {
112
        $field->setModelAttribute(
198✔
113
            sprintf('%s.*.%s', $this->getModelAttribute(), $field->getModelAttribute())
198✔
114
        );
198✔
115

116
        if ($field instanceof Relation) {
198✔
NEW
117
            $field->resolveRouteKeyNameUsing(
×
NEW
118
                fn (): string => Str::of($field->getRelationName())->singular()->ucfirst()->prepend($this->getModelAttribute())->value()
×
NEW
119
            );
×
120
        }
121
    }
122

123
    /**
124
     * {@inheritdoc}
125
     */
126
    public function withFields(Closure $callback): static
127
    {
128
        $this->optionFieldsResolver = function (Request $request, Model $model, Model $tmpModel) use ($callback): Fields {
198✔
129
            $fields = new Fields([
1✔
130
                Hidden::make(__('Key'), '_key'),
1✔
131
            ]);
1✔
132

133
            $fields->register(call_user_func_array($callback, [$request, $model, $tmpModel]));
1✔
134

135
            $fields->each(function (Field $field) use ($tmpModel): void {
1✔
136
                $attribute = sprintf(
1✔
137
                    '%s.%s.%s',
1✔
138
                    $this->getModelAttribute(),
1✔
139
                    $tmpModel->getAttribute('_key'),
1✔
140
                    $key = $field->getModelAttribute()
1✔
141
                );
1✔
142

143
                $field->setModelAttribute($attribute)
1✔
144
                    ->name($attribute)
1✔
145
                    ->id($attribute)
1✔
146
                    ->value(fn (): mixed => $tmpModel->getAttribute($key));
1✔
147
            });
1✔
148

149
            return $fields;
1✔
150
        };
198✔
151

152
        return $this->__withFields($callback);
198✔
153
    }
154

155
    /**
156
     * Resolve the option fields.
157
     */
158
    public function resolveOptionFields(Request $request, Model $model, Model $tmpModel): Fields
159
    {
160
        return is_null($this->optionFieldsResolver)
1✔
161
            ? new Fields
×
162
            : call_user_func_array($this->optionFieldsResolver, [$request, $model, $tmpModel]);
1✔
163
    }
164

165
    /**
166
     * Make a new temporary model for the option.
167
     */
168
    public function newTemporaryModel(array $attributes = []): Model
169
    {
170
        $model = new class extends Model
1✔
171
        {
1✔
172
            //
173
        };
1✔
174

175
        return $model->forceFill(array_replace(
1✔
176
            ['_key' => Str::uuid()],
1✔
177
            $attributes
1✔
178
        ));
1✔
179
    }
180

181
    /**
182
     * Resolve the repeater options.
183
     */
184
    public function resolveOptions(Request $request, Model $model): array
185
    {
186
        $value = (array) $this->resolveValue($request, $model);
2✔
187

188
        return array_map(fn (array $option): array => $this->toOption($request, $model, $this->newTemporaryModel($option)), $value);
2✔
189
    }
190

191
    /**
192
     * Build a new option.
193
     */
194
    public function buildOption(Request $request, Model $model): array
195
    {
196
        $option = $this->toOption($request, $model, $this->newTemporaryModel([]));
1✔
197

198
        $option['fields'] = $option['fields']->mapToInputs($request, $model);
1✔
199

200
        $option['html'] = View::make('root::fields.repeater-option', $option)->render();
1✔
201

202
        return $option;
1✔
203
    }
204

205
    /**
206
     * Get the model URL.
207
     */
208
    public function modelUrl(Model $model): string
209
    {
210
        return str_replace('{resourceModel}', $model->exists ? $model->getKey() : 'create', $this->getUri());
2✔
211
    }
212

213
    /**
214
     * {@inheritdoc}
215
     */
216
    public function resolveFormat(Request $request, Model $model): ?string
217
    {
218
        if (is_null($this->formatResolver)) {
3✔
219
            $this->formatResolver = function (Request $request, Model $model, ?array $value = null): string {
3✔
220
                $values = array_map(fn (array $value): array => $this->resolveOptionFields($request, $model, $this->newTemporaryModel($value))
3✔
221
                    ->authorized($request, $model)
3✔
222
                    ->visible('show')
3✔
223
                    ->mapToDisplay($request, $model), (array) $value);
3✔
224

225
                return View::make('root::fields.repeater-table', ['values' => $values])->render();
3✔
226
            };
3✔
227
        }
228

229
        return parent::resolveFormat($request, $model);
3✔
230
    }
231

232
    /**
233
     * Register the routes using the given router.
234
     */
235
    public function registerRoutes(Request $request, Router $router): void
236
    {
237
        $this->__registerRoutes($request, $router);
198✔
238

239
        $router->prefix($this->getUriKey())->group(function (Router $router) use ($request): void {
198✔
240
            $this->resolveFields($request)->registerRoutes($request, $router);
198✔
241
        });
198✔
242
    }
243

244
    /**
245
     * The routes that should be registered.
246
     */
247
    public function routes(Router $router): void
248
    {
249
        $router->post('/', RepeaterController::class);
198✔
250
    }
251

252
    /**
253
     * Get the option representation of the model and the temporary model.
254
     */
255
    public function toOption(Request $request, Model $model, Model $tmpModel): array
256
    {
257
        return [
1✔
258
            'open' => true,
1✔
259
            'value' => $tmpModel->getAttribute('_key'),
1✔
260
            'label' => $this->getOptionName(),
1✔
261
            'fields' => $this->resolveOptionFields($request, $model, $tmpModel),
1✔
262
        ];
1✔
263
    }
264

265
    /**
266
     * {@inheritdoc}
267
     */
268
    public function toInput(Request $request, Model $model): array
269
    {
270
        return array_merge(parent::toInput($request, $model), [
2✔
271
            'addNewLabel' => $this->getAddNewOptionLabel(),
2✔
272
            'max' => $this->max,
2✔
273
            'options' => array_map(static function (array $option) use ($request, $model): array {
2✔
274
                $option['fields'] = $option['fields']->mapToInputs($request, $model);
×
275

276
                return array_merge($option, [
×
277
                    'html' => View::make('root::fields.repeater-option', $option)->render(),
×
278
                ]);
×
279
            }, $this->resolveOptions($request, $model)),
2✔
280
            'url' => $this->modelUrl($model),
2✔
281
        ]);
2✔
282
    }
283

284
    /**
285
     * {@inheritdoc}
286
     */
287
    public function toValidate(Request $request, Model $model): array
288
    {
289
        return array_merge(
3✔
290
            parent::toValidate($request, $model),
3✔
291
            $this->resolveFields($request)->mapToValidate($request, $model)
3✔
292
        );
3✔
293
    }
294
}
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