default([]); $this->afterStateHydrated(static function (CheckboxList $component, $state) { if (is_array($state)) { return; } $component->state([]); }); $this->searchDebounce(0); $this->registerActions([ fn (CheckboxList $component): Action => $component->getSelectAllAction(), fn (CheckboxList $component): Action => $component->getDeselectAllAction(), ]); } public function getSelectAllAction(): Action { $action = Action::make($this->getSelectAllActionName()) ->label(__('filament-forms::components.checkbox_list.actions.select_all.label')) ->livewireClickHandlerEnabled(false) ->link() ->size(ActionSize::Small); if ($this->modifySelectAllActionUsing) { $action = $this->evaluate($this->modifySelectAllActionUsing, [ 'action' => $action, ]) ?? $action; } return $action; } public function selectAllAction(?Closure $callback): static { $this->modifySelectAllActionUsing = $callback; return $this; } public function getSelectAllActionName(): string { return 'selectAll'; } public function getDeselectAllAction(): Action { $action = Action::make($this->getDeselectAllActionName()) ->label(__('filament-forms::components.checkbox_list.actions.deselect_all.label')) ->livewireClickHandlerEnabled(false) ->link() ->size(ActionSize::Small); if ($this->modifyDeselectAllActionUsing) { $action = $this->evaluate($this->modifyDeselectAllActionUsing, [ 'action' => $action, ]) ?? $action; } return $action; } public function deselectAllAction(?Closure $callback): static { $this->modifyDeselectAllActionUsing = $callback; return $this; } public function getDeselectAllActionName(): string { return 'deselectAll'; } public function relationship(string | Closure | null $name = null, string | Closure | null $titleAttribute = null, ?Closure $modifyQueryUsing = null): static { $this->relationship = $name ?? $this->getName(); $this->relationshipTitleAttribute = $titleAttribute; $this->options(static function (CheckboxList $component) use ($modifyQueryUsing): array { $relationship = Relation::noConstraints(fn () => $component->getRelationship()); $relationshipQuery = app(RelationshipJoiner::class)->prepareQueryForNoConstraints($relationship); if ($modifyQueryUsing) { $relationshipQuery = $component->evaluate($modifyQueryUsing, [ 'query' => $relationshipQuery, ]) ?? $relationshipQuery; } if ($component->hasOptionLabelFromRecordUsingCallback()) { return $relationshipQuery ->get() ->mapWithKeys(static fn (Model $record) => [ $record->{Str::afterLast($relationship->getQualifiedRelatedKeyName(), '.')} => $component->getOptionLabelFromRecord($record), ]) ->toArray(); } $relationshipTitleAttribute = $component->getRelationshipTitleAttribute(); if (empty($relationshipQuery->getQuery()->orders)) { $relationshipQuery->orderBy($relationshipQuery->qualifyColumn($relationshipTitleAttribute)); } if (str_contains($relationshipTitleAttribute, '->')) { if (! str_contains($relationshipTitleAttribute, ' as ')) { $relationshipTitleAttribute .= " as {$relationshipTitleAttribute}"; } } else { $relationshipTitleAttribute = $relationshipQuery->qualifyColumn($relationshipTitleAttribute); } return $relationshipQuery ->pluck($relationshipTitleAttribute, $relationship->getQualifiedRelatedKeyName()) ->toArray(); }); $this->loadStateFromRelationshipsUsing(static function (CheckboxList $component, ?array $state) use ($modifyQueryUsing): void { $relationship = $component->getRelationship(); if ($modifyQueryUsing) { $component->evaluate($modifyQueryUsing, [ 'query' => $relationship->getQuery(), ]); } /** @var Collection $relatedRecords */ $relatedRecords = $relationship->getResults(); $component->state( // Cast the related keys to a string, otherwise Livewire does not // know how to handle deselection. // // https://github.com/filamentphp/filament/issues/1111 $relatedRecords ->pluck($relationship->getRelatedKeyName()) ->map(static fn ($key): string => strval($key)) ->all(), ); }); $this->saveRelationshipsUsing(static function (CheckboxList $component, ?array $state) use ($modifyQueryUsing) { $relationship = $component->getRelationship(); if ($modifyQueryUsing) { $component->evaluate($modifyQueryUsing, [ 'query' => $relationship->getQuery(), ]); } /** @var Collection $relatedRecords */ $relatedRecords = $relationship->getResults(); $recordsToDetach = array_diff( $relatedRecords ->pluck($relationship->getRelatedKeyName()) ->map(static fn ($key): string => strval($key)) ->all(), $state ?? [], ); if (count($recordsToDetach) > 0) { $relationship->detach($recordsToDetach); } $pivotData = $component->getPivotData(); if ($pivotData === []) { $relationship->sync($state ?? [], detaching: false); return; } $relationship->syncWithPivotValues($state ?? [], $pivotData, detaching: false); }); $this->dehydrated(false); return $this; } public function bulkToggleable(bool | Closure $condition = true): static { $this->isBulkToggleable = $condition; return $this; } public function getOptionLabelFromRecordUsing(?Closure $callback): static { $this->getOptionLabelFromRecordUsing = $callback; return $this; } public function hasOptionLabelFromRecordUsingCallback(): bool { return $this->getOptionLabelFromRecordUsing !== null; } public function getOptionLabelFromRecord(Model $record): string | Htmlable { return $this->evaluate( $this->getOptionLabelFromRecordUsing, namedInjections: [ 'record' => $record, ], typedInjections: [ Model::class => $record, $record::class => $record, ], ); } public function getRelationshipTitleAttribute(): ?string { return $this->evaluate($this->relationshipTitleAttribute); } public function getLabel(): string | Htmlable | null { if ($this->label === null && $this->getRelationship()) { $label = (string) str($this->getRelationshipName()) ->before('.') ->kebab() ->replace(['-', '_'], ' ') ->ucfirst(); return ($this->shouldTranslateLabel) ? __($label) : $label; } return parent::getLabel(); } public function getRelationship(): ?BelongsToMany { $name = $this->getRelationshipName(); if (blank($name)) { return null; } return $this->getModelInstance()->{$name}(); } public function getRelationshipName(): ?string { return $this->evaluate($this->relationship); } public function isBulkToggleable(): bool { return (bool) $this->evaluate($this->isBulkToggleable); } }