TitasGailius / nova-search-relations

This package allow you to include relationship columns into Laravel Nova search query.
Other
352 stars 33 forks source link

Added Morphto Relationship #26

Closed thisisablock closed 4 years ago

TitasGailius commented 4 years ago

Good work! It looks really promising, I'll check it later this week.

TitasGailius commented 4 years ago

I plan to release https://github.com/TitasGailius/nova-search-relations/pull/27 soon.

Then a "morph" relation search could simply be something like a custom MorphSearch class.

/**
 * Get the searchable columns for the resource.
 *
 * @return array
 */
public static function searchableRelations(): array
{
    return [
        'commentable' => new MorphSearch(['title', 'name']),
    ];
}
TitasGailius commented 4 years ago

Another thing I don't necessarily agree with is that you pull all types from the database.

It would fail if we had a commentable relationship that could either be a Post or a Video and one of those model tables didn't have some of the specified columns:

/**
 * The relationship columns that should be searched.
 *
 * @var array
 */
public static $searchRelations = [
    'commentable' => ['post_title', 'video_description'],
];

That's why an approach similar to this might be better:

/**
 * Get the searchable columns for the resource.
 *
 * @return array
 */
public static function searchableRelations(): array
{
    return [
        'commentable' => new MorphSearch([
            'App\\Models\\Post' => ['psot_title'],
            'App\\Models\\Video' => ['video_description'],
        ]),
    ];
}
TitasGailius commented 4 years ago

I merged #27. Now you have full control on how the search works and you may even extend the search capabilities with your own logic.

Regarding morphable search, I'll check it this/next week.

thisisablock commented 4 years ago

@TitasGailius thanks :)

970Design commented 3 years ago

@TitasGailius thanks for adding this feature!

One issue I'm running into while using the master branch after this feature has been added is that ensureRelationshipExists in RelationSearch always fails on nested relationships when applying the search.

I'm unfortunately not sure why though.. the same queries work just fine on the tagged version 1.0.6 (nested relations also work fine after removing the ensureRelationshipExists check)

asivaneswaran commented 3 years ago

@TitasGailius What if I have a nester relation? Let's say I have something like:

commentable(Video or Photo).someModel

And I want to search fields in from someModel?

anditsung commented 1 year ago

@TitasGailius What if I have a nester relation? Let's say I have something like:

commentable(Video or Photo).someModel

And I want to search fields in from someModel?

example

Action Resource

public static function searchableRelations(): array
    {
        return [
            'target' => new MorphSearch([
                'App\Models\Role' => ['name'],
                'App\Models\User' => [
                    'name',
                    'username',
                    'email',
                    'roles' => ['name']
                ],
                'App\Models\Permission' => ['name'],
                'App\Models\Invite' => [
                    'email',
                    'role' => ['name']
                ],
            ])
        ];
    }

MorphSearch class

class MorphSearch implements Search
{
    /**
     * Searchable columns.
     *
     * @var array
     */
    protected $morphRelation;

    protected $fallbackClass = '*';

    protected $fallbackColumns = [
        'id'
    ];

    /**
     * Instantiate a new search query instance.
     *
     * @param array $columns
     */
    public function __construct(array $morphRelation)
    {
        $this->morphRelation = $morphRelation;
    }

    public function apply(Builder $query, string $relation, string $search): Builder
    {
        foreach ($this->morphRelation as $morphClass => $columns) {
            if (class_exists($morphClass) && is_subclass_of($morphClass, Model::class)) {
                $query->orWhereHasMorph($relation, [$morphClass], function ($query) use ($columns, $relation, $search) {
                    [$columnSearch, $relationSearch] = $this->parseColumns($columns);

                    (new ColumnSearch($columnSearch->toArray()))->apply($query, $relation, $search);

                    foreach ($relationSearch as $relationName => $relationsColumns) {
                        (new RelationSearch($relationsColumns))->apply($query, $relationName, $search);
                    }
                });
            }
        }

        // search all morph class with default columns
        $query->orWhereHasMorph($relation, $this->fallbackClass, function ($query) use ($relation, $search) {
            return (new ColumnSearch($this->fallbackColumns))->apply($query, $relation, $search);
        });

        return $query;
    }

    private function parseColumns($columns): array
    {
        $columns = collect($columns);

        $columnSearch = $columns->filter(function ($value, $key) {
            return is_numeric($key);
        });

        $relationSearch = $columns->filter(function ($value, $key) {
            return is_string($key);
        });

        return [$columnSearch, $relationSearch];
    }
}