|null */ public $resourceClass; /** * The resource name for the repeater. * * @var string|null */ public $resourceName; /** * The field's component. * * @var string */ public $component = 'repeater-field'; /** * Indicates if the field label and form element should sit on top of each other. * * @var bool */ public $stacked = false; /** * Indicates whether the field should use all available white-space. * * @var bool */ public $fullWidth = false; /** * The repeatable types used for the Repeater. * * @var \Laravel\Nova\Fields\Repeater\RepeatableCollection */ public $repeatables; /** * @var bool */ public $sortable = true; /** * @var string|null */ public $uniqueField; /** * The preset used for the field. * * @var \Laravel\Nova\Fields\Repeater\Presets\Preset|null */ public $preset; /** * Create a new field. * * @param string $name * @param string|null $attribute * @param (callable(mixed, mixed, ?string):mixed)|null $resolveCallback */ public function __construct($name, $attribute = null, callable $resolveCallback = null) { parent::__construct($name, $attribute, $resolveCallback); $this->onlyOnForms(); $this->repeatables = RepeatableCollection::make(); } /** * Specify the callback to be executed to retrieve the pivot fields. * * @param array $repeatables * @return $this */ public function repeatables(array $repeatables) { foreach ($repeatables as $repeatable) { $this->repeatables->push($repeatable); } return $this; } /** * Set the preset used for the field. * * @return $this */ public function preset(Preset $preset) { $this->preset = $preset; return $this; } /** * Use the JSON preset for the field. * * @return $this */ public function asJson() { return $this->preset(new JSON); } /** * Use the HasMany preset for the field. * * @param class-string<\Laravel\Nova\Resource>|null $resourceClass * @return $this * * @throws \Laravel\Nova\Exceptions\NovaException */ public function asHasMany($resourceClass = null) { /** @var class-string<\Laravel\Nova\Resource>|null $resource */ $resource = $resourceClass ?? ResourceRelationshipGuesser::guessResource($this->name); if ($resource) { $this->resourceClass = $resource; $this->resourceName = $resource::uriKey(); return $this->preset(new HasMany); } throw NovaException::missingResourceForRepeater($this->name); } /** * Return the preset instance for the field. * * @return \Laravel\Nova\Fields\Repeater\Presets\Preset */ public function getPreset() { return $this->preset ?? new JSON; } /** * Resolve the given attribute from the given resource. * * @param mixed $resource * @param string $attribute * @return mixed */ protected function resolveAttribute($resource, $attribute) { $request = app(NovaRequest::class); return $this->getPreset()->get($request, $resource, $attribute, $this->repeatables); } /** * Determine if the field collection contains an ID field. */ protected function fieldsContainsIDField(FieldCollection $fields): bool { return $fields->contains(function (Field $field) { return $field instanceof ID && $field->attribute === $this->uniqueField || $field instanceof Hidden && $field->attribute === $this->uniqueField; }); } /** * Hydrate the given attribute on the model based on the incoming request. * * @param string $requestAttribute * @param \Illuminate\Database\Eloquent\Model|\Laravel\Nova\Support\Fluent $model * @param string $attribute * @return \Closure */ protected function fillAttributeFromRequest(NovaRequest $request, $requestAttribute, $model, $attribute) { return $this->getPreset()->set($request, $requestAttribute, $model, $attribute, $this->repeatables, $this->uniqueField); } /** * Get the creation rules for this field. * * @return array * * @phpstan-return array */ public function getCreationRules(NovaRequest $request) { return array_merge_recursive(parent::getCreationRules($request), $this->formatRules()); } /** * Get the update rules for this field. * * @return array * * @phpstan-return array */ public function getUpdateRules(NovaRequest $request) { return array_merge_recursive(parent::getUpdateRules($request), $this->formatRules()); } /** * Format available rules. * * @return array * * @phpstan-return array */ protected function formatRules() { $request = app(NovaRequest::class); if ($request->method() === 'GET') { return []; } return collect($request->{$this->validationKey()}) ->map(function ($item) { return $this->repeatables->findByKey($item['type']); }) ->flatMap(function (Repeatable $repeatable, $index) use ($request) { return FieldCollection::make($repeatable->fields($request)) ->mapWithKeys(function (Field $field) use ($index) { return ["{$this->validationKey()}.{$index}.fields.{$field->attribute}" => $field->rules]; }); }) ->all(); } /** * Set the unique database column to use when attempting upserts. * * @param string|null $key * @return $this */ public function uniqueField($key) { $this->uniqueField = $key; return $this; } /** * Prepare the field for JSON serialization. * * @return array */ public function jsonSerialize(): array { return array_merge([ 'repeatables' => $this->repeatables, 'sortable' => $this->sortable, ], parent::jsonSerialize()); } }