Skip to content

Commit

Permalink
Merge pull request #15139 from dododedodonl/fix/search-json-relation
Browse files Browse the repository at this point in the history
Resolve exception for search on json columns on a relationship
  • Loading branch information
danharrin authored Feb 3, 2025
2 parents 2603152 + 202cab4 commit d14d8b2
Show file tree
Hide file tree
Showing 19 changed files with 313 additions and 76 deletions.
2 changes: 1 addition & 1 deletion packages/actions/src/Exports/ExportColumn.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public function applyEagerLoading(EloquentBuilder $query): EloquentBuilder
return $query;
}

$relationshipName = $this->getRelationshipName();
$relationshipName = $this->getRelationshipName($query->getModel());

if (array_key_exists($relationshipName, $query->getEagerLoads())) {
return $query;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public function applyEagerLoading(Builder | Relation $query): Builder | Relation

if ($this->hasRelationship($query->getModel())) {
return $query->with([
"{$this->getRelationshipName()}.media" => $modifyMediaQuery,
"{$this->getRelationshipName($query->getModel())}.media" => $modifyMediaQuery,
]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public function applyEagerLoading(Builder | Relation $query): Builder | Relation
}

if ($this->hasRelationship($query->getModel())) {
return $query->with(["{$this->getRelationshipName()}.tags"]);
return $query->with(["{$this->getRelationshipName($query->getModel())}.tags"]);
}

return $query->with(['tags']);
Expand Down
119 changes: 93 additions & 26 deletions packages/support/src/Concerns/HasCellState.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Stringable;
use Znck\Eloquent\Relations\BelongsToThrough;
Expand Down Expand Up @@ -113,11 +114,12 @@ public function getStateFromRecord(): mixed
return null;
}

$relationshipAttribute = $this->getRelationshipAttribute();
$attributeName = $this->getAttributeName($record);
$fullAttributeName = $this->getFullAttributeName($record);

$state = collect($this->getRelationshipResults($record))
->filter(fn (Model $record): bool => array_key_exists($relationshipAttribute, $record->attributesToArray()))
->pluck($relationshipAttribute)
->filter(fn (Model $record): bool => array_key_exists($attributeName, $record->attributesToArray()))
->pluck($fullAttributeName)
->filter(fn ($state): bool => filled($state))
->when($this->isDistinctList(), fn (Collection $state) => $state->unique())
->values();
Expand All @@ -143,7 +145,13 @@ public function getSeparator(): ?string

public function hasRelationship(Model $record): bool
{
return $this->getRelationship($record) !== null;
$name = $this->getName();

if (! str($name)->contains('.')) {
return false;
}

return $record->isRelation((string) str($name)->before('.'));
}

/**
Expand All @@ -154,22 +162,29 @@ public function queriesRelationships(Model $record): bool
return $this->hasRelationship($record);
}

public function getRelationship(Model $record, ?string $name = null): ?Relation
public function getRelationship(Model $record, ?string $relationshipName = null): ?Relation
{
if (blank($name) && (! str($this->getName())->contains('.'))) {
return null;
if (isset($relationshipName)) {
$nameParts = explode('.', $relationshipName);
} else {
$name = $this->getName();

if (! str($name)->contains('.')) {
return null;
}

$nameParts = explode('.', $name);
array_pop($nameParts);
}

$relationship = null;

foreach (explode('.', $name ?? $this->getRelationshipName()) as $nestedRelationshipName) {
if (! $record->isRelation($nestedRelationshipName)) {
$relationship = null;

foreach ($nameParts as $namePart) {
if (! $record->isRelation($namePart)) {
break;
}

$relationship = $record->{$nestedRelationshipName}();
$relationship = $record->{$namePart}();
$record = $relationship->getRelated();
}

Expand All @@ -184,7 +199,7 @@ public function getRelationshipResults(Model $record, ?array $relationships = nu
{
$results = [];

$relationships ??= explode('.', $this->getRelationshipName());
$relationships ??= explode('.', $this->getRelationshipName($record));

while (count($relationships)) {
$currentRelationshipName = array_shift($relationships);
Expand Down Expand Up @@ -230,15 +245,46 @@ public function getRelationshipResults(Model $record, ?array $relationships = nu
return $results;
}

public function getRelationshipAttribute(?string $name = null): string
public function getAttributeName(Model $record): string
{
$name = $this->getName();

if (! str($name)->contains('.')) {
return $name;
}

$nameParts = explode('.', $name);

foreach ($nameParts as $namePart) {
if (! $record->isRelation($namePart)) {
break;
}

array_shift($nameParts);
}

return Arr::first($nameParts);
}

public function getFullAttributeName(Model $record): string
{
$name ??= $this->getName();
$name = $this->getName();

if (! str($name)->contains('.')) {
return $name;
}

return (string) str($name)->afterLast('.');
$nameParts = explode('.', $name);

foreach ($nameParts as $namePart) {
if (! $record->isRelation($namePart)) {
break;
}

array_shift($nameParts);
}

return implode('.', $nameParts);
}

public function getInverseRelationshipName(Model $record): string
Expand All @@ -247,10 +293,17 @@ public function getInverseRelationshipName(Model $record): string
return $this->inverseRelationshipName;
}

$inverseRelationships = [];
$nameParts = explode('.', $this->getName());
array_pop($nameParts);

$inverseRelationshipParts = [];

foreach (explode('.', $this->getRelationshipName()) as $nestedRelationshipName) {
$relationship = $record->{$nestedRelationshipName}();
foreach ($nameParts as $namePart) {
if (! $record->isRelation($namePart)) {
break;
}

$relationship = $record->{$namePart}();
$record = $relationship->getRelated();

$inverseNestedRelationshipName = (string) str(class_basename($relationship->getParent()::class))
Expand All @@ -266,29 +319,43 @@ public function getInverseRelationshipName(Model $record): string
// The conventional relationship doesn't exist, but we can
// attempt to use the original relationship name instead.

if (! $record->isRelation($nestedRelationshipName)) {
if (! $record->isRelation($namePart)) {
$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;
$inverseNestedRelationshipName = $namePart;
}

array_unshift($inverseRelationships, $inverseNestedRelationshipName);
array_unshift($inverseRelationshipParts, $inverseNestedRelationshipName);
}

return implode('.', $inverseRelationships);
return implode('.', $inverseRelationshipParts);
}

public function getRelationshipName(?string $name = null): ?string
public function getRelationshipName(Model $record): ?string
{
$name ??= $this->getName();
$name = $this->getName();

if (! str($name)->contains('.')) {
return null;
}

return (string) str($name)->beforeLast('.');
$nameParts = explode('.', $name);
array_pop($nameParts);

$relationshipParts = [];

foreach ($nameParts as $namePart) {
if (! $record->isRelation($namePart)) {
break;
}

$relationshipParts[] = $namePart;
$record = $record->{$namePart}()->getRelated();
}

return implode('.', $relationshipParts);
}
}
9 changes: 5 additions & 4 deletions packages/tables/src/Columns/Concerns/CanBeSearchable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Filament\Tables\Columns\Concerns;

use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;

trait CanBeSearchable
Expand Down Expand Up @@ -56,9 +57,9 @@ public function forceSearchCaseInsensitive(bool | Closure | null $condition = tr
/**
* @return array<string>
*/
public function getSearchColumns(): array
public function getSearchColumns(Model $record): array
{
return $this->searchColumns ?? $this->getDefaultSearchColumns();
return $this->searchColumns ?? $this->getDefaultSearchColumns($record);
}

public function isSearchable(): bool
Expand All @@ -84,8 +85,8 @@ public function isSearchForcedCaseInsensitive(): ?bool
/**
* @return array{0: string}
*/
public function getDefaultSearchColumns(): array
public function getDefaultSearchColumns(Model $record): array
{
return [(string) str($this->getName())->afterLast('.')];
return [$this->getFullAttributeName($record)];
}
}
9 changes: 5 additions & 4 deletions packages/tables/src/Columns/Concerns/CanBeSortable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Filament\Tables\Columns\Concerns;

use Closure;
use Illuminate\Database\Eloquent\Model;

trait CanBeSortable
{
Expand Down Expand Up @@ -36,9 +37,9 @@ public function sortable(bool | array | Closure $condition = true, ?Closure $que
/**
* @return array<string>
*/
public function getSortColumns(): array
public function getSortColumns(Model $record): array
{
return $this->sortColumns ?? $this->getDefaultSortColumns();
return $this->sortColumns ?? $this->getDefaultSortColumns($record);
}

public function isSortable(): bool
Expand All @@ -49,8 +50,8 @@ public function isSortable(): bool
/**
* @return array{0: string}
*/
public function getDefaultSortColumns(): array
public function getDefaultSortColumns(Model $record): array
{
return [str($this->getName())->afterLast('.')];
return [$this->getFullAttributeName($record)];
}
}
12 changes: 5 additions & 7 deletions packages/tables/src/Columns/Concerns/CanUpdateState.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,28 +58,26 @@ public function updateState(mixed $state): mixed

$columnName = $this->getName();

if ($this->getRelationship($record)) {
$columnName = $this->getRelationshipAttribute();
$columnRelationshipName = $this->getRelationshipName();
if ($this->hasRelationship($record)) {
$columnName = $this->getFullAttributeName($record);
$columnRelationshipName = $this->getRelationshipName($record);

$record = Arr::get(
$record->load($columnRelationshipName),
$columnRelationshipName,
);
} elseif (
(($tableRelationship = $this->getTable()->getRelationship()) instanceof BelongsToMany) &&
in_array($columnName, $tableRelationship->getPivotColumns())
in_array($this->getAttributeName($record), $tableRelationship->getPivotColumns())
) {
$record = $record->{$tableRelationship->getPivotAccessor()};
} else {
$columnName = (string) str($columnName)->replace('.', '->');
}

if (! ($record instanceof Model)) {
return null;
}

$record->setAttribute($columnName, $state);
$record->setAttribute((string) str($columnName)->replace('.', '->'), $state);
$record->save();

$this->callAfterStateUpdated($state);
Expand Down
Loading

0 comments on commit d14d8b2

Please sign in to comment.