TitasGailius / nova-search-relations

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

BelongsTo different database #33

Closed maximesahagian closed 3 years ago

maximesahagian commented 3 years ago

Hello,

This package sounds perfect, but impossible to filter with a different database connection.. (my belongs to model/resource is connected to another database than the resource).

When I search for a belongs to name, an error appears because the package tries to select the model in the same database, but the $connection is well set in the other model..

Is there a way to solve it?

Best.

TitasGailius commented 3 years ago

This extension basically applies this Laravel query:

Given a search term is foobar when searching for user posts by a title.

User::whereHas('posts', function ($query) {
    return $query->where('title', 'foobar');
});

so if your User and Post models are from different databases and this type of query does not work I can't help much.

yuoppp commented 2 years ago

Here is my approach to implement multi-connection searching using nova-search-relations.

<?php

declare(strict_types=1);

namespace App\Nova\Search;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\Relation;
use Titasgailius\SearchRelations\Contracts\Search;
use Titasgailius\SearchRelations\Searches\ColumnSearch;

class ExternalRelationSearch implements Search
{
    public function __construct(
        private array $columns,
    ) {
    }

    public function apply(Builder $query, string $relation, string $search): Builder
    {
        /** @var Relation $relationObj */
        $relationObj = $query->getModel()->{$relation}();
        $relatedModel = $relationObj->getRelated();

        list($externalModelKey, $localModelKey) = $this->getRelationKeys($relationObj);

        /**
         * Load all keys of external model, matching the search criteria
         */
        $keys = (new ColumnSearch($this->columns))
            ->apply(
                $relatedModel->select($externalModelKey),
                $relation,
                $search
            )
            ->get()
            ->pluck($externalModelKey);

        /**
         * Set loaded keys as whereIn statement in the search query
         */
        return empty($keys) ? $query : $query->orWhereIn($localModelKey, $keys);
    }

    protected function getRelationKeys(Relation $relation): array
    {
        // Complete with other relation types if needed
        if ($relation instanceof BelongsTo) {
            return [$relation->getOwnerKeyName(), $relation->getForeignKeyName()];
        }

        throw new \InvalidArgumentException(sprintf(
            'Relation type %s is not supported in %s:%s. Maybe you need to implement it yourself',
            get_class($relation),
            get_class($this),
            __METHOD__,
        ));
    }
}

In resource file:

public static function searchableRelations(): array
{
    return [
        'user' => new ExternalRelationSearch(['email']),
    ];
}

Be careful with this method, because you load all related models matching the search criteria, which can lead to a high memory usage.

Also make sure you set up the connection in the related model class.

yuoppp commented 2 years ago

@TitasGailius check this out. Maybe it deserves to be contributed :p

vesper8 commented 2 years ago

@yuoppp thank you! I'm in the exact same boat and your solutions works perfectly