inverseRelationshipName = $name; return $this; } public function distinctList(bool | Closure $condition = true): static { $this->isDistinctList = $condition; return $this; } public function getStateUsing(mixed $callback): static { $this->getStateUsing = $callback; return $this; } public function state(mixed $state): static { $this->getStateUsing($state); return $this; } public function default(mixed $state): static { $this->defaultState = $state; return $this; } public function isDistinctList(): bool { return (bool) $this->evaluate($this->isDistinctList); } public function getDefaultState(): mixed { return $this->evaluate($this->defaultState); } public function getState(): mixed { $state = ($this->getStateUsing !== null) ? $this->evaluate($this->getStateUsing) : $this->getStateFromRecord(); if (is_string($state) && ($separator = $this->getSeparator())) { $state = explode($separator, $state); $state = (count($state) === 1 && blank($state[0])) ? [] : $state; } if (blank($state)) { $state = $this->getDefaultState(); } return $state; } public function getStateFromRecord(): mixed { $record = $this->getRecord(); $state = data_get($record, $this->getName()); if ($state !== null) { return $state; } if (! $this->hasRelationship($record)) { return null; } $relationship = $this->getRelationship($record); if (! $relationship) { return null; } $relationshipAttribute = $this->getRelationshipAttribute(); $state = collect($this->getRelationshipResults($record)) ->filter(fn (Model $record): bool => array_key_exists($relationshipAttribute, $record->attributesToArray())) ->pluck($relationshipAttribute) ->filter(fn ($state): bool => filled($state)) ->when($this->isDistinctList(), fn (Collection $state) => $state->unique()) ->values(); if (! $state->count()) { return null; } return $state->all(); } public function separator(string | Closure | null $separator = ','): static { $this->separator = $separator; return $this; } public function getSeparator(): ?string { return $this->evaluate($this->separator); } public function hasRelationship(Model $record): bool { return $this->getRelationship($record) !== null; } /** * @deprecated Use `hasRelationship()` instead. */ public function queriesRelationships(Model $record): bool { return $this->hasRelationship($record); } public function getRelationship(Model $record, ?string $name = null): ?Relation { if (blank($name) && (! str($this->getName())->contains('.'))) { return null; } $relationship = null; foreach (explode('.', $name ?? $this->getRelationshipName()) as $nestedRelationshipName) { if (! $record->isRelation($nestedRelationshipName)) { $relationship = null; break; } $relationship = $record->{$nestedRelationshipName}(); $record = $relationship->getRelated(); } return $relationship; } /** * @param array | null $relationships * @return array */ public function getRelationshipResults(Model $record, ?array $relationships = null): array { $results = []; $relationships ??= explode('.', $this->getRelationshipName()); while (count($relationships)) { $currentRelationshipName = array_shift($relationships); $currentRelationshipValue = $record->getRelationValue($currentRelationshipName); if ($currentRelationshipValue instanceof Collection) { if (! count($relationships)) { $results = [ ...$results, ...$currentRelationshipValue->all(), ]; continue; } foreach ($currentRelationshipValue as $valueRecord) { $results = [ ...$results, ...$this->getRelationshipResults( $valueRecord, $relationships, ), ]; } break; } if (! $currentRelationshipValue instanceof Model) { break; } if (! count($relationships)) { $results[] = $currentRelationshipValue; break; } $record = $currentRelationshipValue; } return $results; } public function getRelationshipAttribute(?string $name = null): string { $name ??= $this->getName(); if (! str($name)->contains('.')) { return $name; } return (string) str($name)->afterLast('.'); } public function getInverseRelationshipName(Model $record): string { if (filled($this->inverseRelationshipName)) { return $this->inverseRelationshipName; } $inverseRelationships = []; foreach (explode('.', $this->getRelationshipName()) as $nestedRelationshipName) { $relationship = $record->{$nestedRelationshipName}(); $record = $relationship->getRelated(); $inverseNestedRelationshipName = (string) str(class_basename($relationship->getParent()::class)) ->when( ($relationship instanceof BelongsTo || $relationship instanceof BelongsToMany || $relationship instanceof BelongsToThrough), fn (Stringable $name) => $name->plural(), ) ->camel(); if (! $record->isRelation($inverseNestedRelationshipName)) { // The conventional relationship doesn't exist, but we can // attempt to use the original relationship name instead. if (! $record->isRelation($nestedRelationshipName)) { $recordClass = $record::class; throw new Exception("When trying to guess the inverse relationship for column [{$this->getName()}], relationship [{$inverseNestedRelationshipName}] was not found on model [{$recordClass}]. Please define a custom [inverseRelationship()] for this column."); } $inverseNestedRelationshipName = $nestedRelationshipName; } array_unshift($inverseRelationships, $inverseNestedRelationshipName); } return implode('.', $inverseRelationships); } public function getRelationshipName(?string $name = null): ?string { $name ??= $this->getName(); if (! str($name)->contains('.')) { return null; } return (string) str($name)->beforeLast('.'); } }