Tucker-Eric / EloquentFilter

An Eloquent Way To Filter Laravel Models And Their Relationships
http://tucker-eric.github.io/EloquentFilter
MIT License
1.72k stars 120 forks source link

Relations with one related method #190

Closed DevilSAM closed 11 months ago

DevilSAM commented 11 months ago

Hi, Eric! Your package is awesome! Thank you!

I just want to ask about filtering by relations where related model should use the same method twice. For example:

class ConstructionFilter extends BaseModelFilter
{
    public $relations = [
        'owner' => [
            'owner.surname' => 'full_name',
            'owner.name'    => 'full_name',
        ]
    ];
}

class OwnerFilter extends BaseModelFilter
{
    public function fullName($value)
    {
        $this->where('full_name', 'ilike', "%$value%");
    }
}

So if I try to filter construction by owner's name and surname it will use only one of these fields and filter it with owner.fullName.

I guess it happens because of under the hood $relations combine all the related methods and use one whereHas for it. Of cause I can write two different methods in my OwnerFilter with the same logic but I just want to know is there any way to make work the code which uses different keys and same values for one relation?

Thank you.

Tucker-Eric commented 11 months ago

Hmm, that's interesting. We could get around this by using the related setup method and updating that fullName filter to expect (or cast) an array it can loop through to chain the queries.

class ConstructionFilter extends BaseModelFilter
{
    public $relations = [
        'owner' => [
            'owner.surname' => 'full_name',
            'owner.name'    => 'full_name',
        ]
    ];

    public ownerSetup()
    {
        // Get all aliases of `full_name`
        $fullNameKeys = array_keys($this->relations["owner"], "full_name");

        // Get all values of the input
        $fullNameValues = []
        foreach($fullNameKeys as $key) {
            $val = $this->input($key);
            if($this->includeFilterInput($val)) {
                $fullNameValues[] = $val;
            }
            // Set value to null so it won't get passed to `OwnerFilter` 
            $this->push($key, null)
        }

        // Push the values to one key so that is the value that gets passed to the other filter
        $this->push($fullNameKeys[0], $fullNameValues)
    }
}

class OwnerFilter extends BaseModelFilter
{
    public function fullName($values)
    {
        foreach((array) $values as $value) {
            $this->where('full_name', 'ilike', "%$value%");    
        }
    }
}
DevilSAM commented 11 months ago

Thanks! It works. A bit more complicated than adding another method to OwnerFilter, but this is exactly what I wanted. Thanks again :)

P.S. I suppose It must be input($key)

>  $val = $this->input($val);
Tucker-Eric commented 11 months ago

@DevilSAM Ahh, yep! Updated reply