| Closure */ protected array | Closure $tableArguments = []; public function tableConfiguration(string | Closure $tableConfiguration): static { $this->tableConfiguration = $tableConfiguration; return $this; } /** * @param array | Closure $arguments */ public function tableArguments(array | Closure $arguments): static { $this->tableArguments = $arguments; return $this; } public function getTableConfiguration(): string { return $this->evaluate($this->tableConfiguration) ?? throw new LogicException('The [tableConfiguration()] method must be set when using a [TableSelect] component.'); } /** * @return array */ public function getTableArguments(): array { return $this->evaluate($this->tableArguments) ?? []; } public function relationship(string | Closure | null $name): static { $this->relationshipName($name); $this->loadStateFromRelationshipsUsing(static function (TableSelect $component, $state): void { if (filled($state)) { return; } $relationship = $component->getRelationship(); if ( ($relationship instanceof BelongsToMany) || ($relationship instanceof HasOneOrManyThrough) ) { /** @var Collection $relatedRecords */ $relatedRecords = $relationship->getResults(); $component->state( // Cast the related keys to a string, otherwise JavaScript does not // know how to handle deselection. // // https://github.com/filamentphp/filament/issues/1111 $relatedRecords ->pluck(($relationship instanceof BelongsToMany) ? $relationship->getRelatedKeyName() : $relationship->getRelated()->getKeyName()) ->map(static fn ($key): string => strval($key)) ->all(), ); return; } if ($relationship instanceof BelongsToThrough) { /** @var ?Model $relatedModel */ $relatedModel = $relationship->getResults(); $component->state( $relatedModel?->getAttribute( $relationship->getRelated()->getKeyName(), ), ); return; } if ($relationship instanceof HasMany) { /** @var Collection $relatedRecords */ $relatedRecords = $relationship->getResults(); $component->state( $relatedRecords ->pluck($relationship->getLocalKeyName()) ->all(), ); return; } if ($relationship instanceof HasOne) { $relatedModel = $relationship->getResults(); $component->state( $relatedModel?->getAttribute( $relationship->getLocalKeyName(), ), ); return; } /** @var BelongsTo $relationship */ $relatedModel = $relationship->getResults(); $component->state( $relatedModel?->getAttribute( $relationship->getOwnerKeyName(), ), ); }); $this->saveRelationshipsUsing(static function (TableSelect $component, Model $record, $state): void { $relationship = $component->getRelationship(); if (($relationship instanceof HasOne) || ($relationship instanceof HasMany)) { $query = $relationship->getQuery(); $query->update([ $relationship->getForeignKeyName() => null, ]); if (! empty($state)) { $relationship::noConstraints(function () use ($component, $record, $state): void { $relationship = $component->getRelationship(); $query = $relationship->getQuery()->whereIn($relationship->getLocalKeyName(), Arr::wrap($state)); $query->update([ $relationship->getForeignKeyName() => $record->getAttribute($relationship->getLocalKeyName()), ]); }); } return; } if ( ($relationship instanceof HasOneOrMany) || ($relationship instanceof HasOneOrManyThrough) || ($relationship instanceof BelongsToThrough) ) { return; } if (! $relationship instanceof BelongsToMany) { // If the model is new and the foreign key is already filled, we don't need to fill it again. // This could be a security issue if the foreign key was mutated in some way before it // was saved, and we don't want to overwrite that value. if ( $record->wasRecentlyCreated && filled($record->getAttributeValue($relationship->getForeignKeyName())) ) { return; } $relationship->associate($state); $record->wasRecentlyCreated && $record->save(); return; } /** @var Collection $relatedRecords */ $relatedRecords = $relationship->getResults(); $state = Arr::wrap($state ?? []); $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(fn (TableSelect $component): bool => ! $component->isMultiple()); return $this; } public function relationshipName(string | Closure | null $name): static { $this->relationship = $name; return $this; } public function getRelationship(): BelongsTo | BelongsToMany | HasOneOrMany | HasOneOrManyThrough | BelongsToThrough | null { if (! $this->hasRelationship()) { return null; } $record = $this->getModelInstance(); $relationship = null; $relationshipName = $this->getRelationshipName(); foreach (explode('.', $relationshipName) as $nestedRelationshipName) { if (! $record->isRelation($nestedRelationshipName)) { $relationship = null; break; } $relationship = $record->{$nestedRelationshipName}(); $record = $relationship->getRelated(); } if (! $relationship) { throw new LogicException("The relationship [{$relationshipName}] does not exist on the model [{$this->getModel()}]."); } return $relationship; } public function getRelationshipName(): ?string { return $this->evaluate($this->relationship); } public function hasRelationship(): bool { return filled($this->getRelationshipName()); } public function getLabel(): string | Htmlable | null { if ($this->label === null && $this->hasRelationship()) { $label = (string) str($this->getRelationshipName()) ->before('.') ->kebab() ->replace(['-', '_'], ' ') ->ucfirst(); return ($this->shouldTranslateLabel) ? __($label) : $label; } return parent::getLabel(); } public function multiple(bool | Closure $condition = true): static { $this->isMultiple = $condition; return $this; } public function isMultiple(): bool { return (bool) $this->evaluate($this->isMultiple); } }