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

Only take first filter parameter #104

Closed mateo2181 closed 5 years ago

mateo2181 commented 5 years ago

I want to filter related models with one to many relations but always is taken only the first parameter. For example: http://localhost:8000/p/profile/search?sport=8&residence_country=12 return only users with sport id 8 but not with residence country id 12. http://localhost:8000/p/profile/search?residence_country=12&sport=8 return only users with residence country id 12 but not with sport id 8 .

Someone had this problem?

USER MODEL:

class User extends Authenticatable implements HasMedia, MustVerifyEmail
{
    use HasRoleAndPermission;
    use Notifiable;
    use SoftDeletes;
    use HasMediaTrait;
    use Filterable;
    use Messagable;
    use Billable;

    protected $table = 'users';

    public function athlete()
    {
        return $this->hasOne('App\Models\Athlete');
    }

   public function scout()
    {
        return $this->hasOne('App\Models\Scout');
    }
}

ATHLETE MODEL:

class Athlete extends Model
{
    use DatesFormat;
    use Filterable;

    protected $table = 'athletes';

    protected $fillable = [
        'nationality_id',
        'second_nationality_id',
        'residence_country_id',
        'residence_city',
        'date_of_birth',
        'phone',
        'sport_id',
        'secondary_sport_id',
        'sporting_goal',
        'professional_start_date',
        'professional_end_date',
        'height',
        'gender',
        'status',
        'emailTutor',
        'firstnameTutor',
        'lastnameTutor',
        'phoneTutor',
        'user_id'
    ];

    public function residenceCountry()
    {
        return $this->belongsTo('App\Models\Country','residence_country_id');
    }

    public function sport()
    {
        return $this->belongsTo('App\Models\Sport')->withTrashed();
    }

SCOUT MODEL:

class Scout extends Model
{
    protected $table = 'scouts';

    protected $fillable = ['nationality_id', 'residence_country_id','user_id','sport_id','phone'];

    protected $with = [
        'currentEntity',
        'nationality',
        'residenceCountry'
    ];

    public function user()
    {
        return $this->belongsTo('App\Models\User','user_id');
    }

    public function sport()
    {
        return $this->belongsTo('App\Models\Sport')->withTrashed();
    }

    public function residenceCountry()
    {
        return $this->belongsTo('App\Models\Country','residence_country_id');
    }
}

USER FILTER:

class UserFilter extends ModelFilter
{
    public $relations = [];

    public function fullname($name)
    {
        return $this->where(function($q) use ($name)
        {
            $names = explode(" ",$name);
            return $q->where('firstname', 'LIKE', "%$name%")
                     ->orWhere('lastname', 'LIKE', "%$name%")
                     ->orWhereIn('lastname',$names)
                     ->whereIn('firstname',$names)
                     ->orWhere('email','LIKE',"%$name%");
        });
    }

    public function sport($sport)
    {
        return $this->whereHas('athlete', function($q) use ($sport) {
            return $q->where('sport_id', $sport)->orWhere('secondary_sport_id', $sport);
        })->orWhereHas('scout', function($q) use ($sport) {
            return $q->where('sport_id', $sport);
        });
    }

    public function gender($value)
    {
        return $this->whereHas('athlete', function($q) use ($value) {
            return $q->where('gender', $value);
        });
    }

    public function age($value)
    {

        $arrayNumbers = explode('-',$value);

        $ageFrom = (int)$arrayNumbers[0];
        $ageTo = (int)$arrayNumbers[1];

        $from = Carbon::now()->subYears((int)($ageTo+1));
        $from = $from->format('Y-m-d');
        $to = Carbon::now()->subYears($ageFrom)->format('Y-m-d');
        \Log::info($from);
        \Log::info($to);
        if($this->input('typeUser') == 'Scout' || $this->input('typeUser') == '') return true;
        return $this->related('athlete', function($q) use ($from,$to) {
            return $q->where('date_of_birth','>', $from)->where('date_of_birth','<=', $to);
        });
    }

    public function status($value)
    {
        return $this->whereHas('athlete', function($q) use ($value) {
            return $q->where('status', $value);
        });
    }

    public function sportingGoal($value)
    {
        return $this->whereHas('athlete', function($q) use ($value) {
            return $q->where('sporting_goal', $value);
        });
    }

    public function residenceCountry($value)
    {
        return $this->whereHas('athlete', function($q) use ($value) {
            return $q->where('residence_country_id', $value);
        })->orWhereHas('scout', function($q) use ($value) {
            return $q->where('residence_country_id', $value);
        });
    }

    public function nationality($value)
    {
        return $this->whereHas('athlete', function($q) use ($value) {
            return $q->where('nationality_id', $value);
        })->orWhereHas('scout', function($q) use ($value) {
            return $q->where('nationality_id', $value);
        });
    }
}
Tucker-Eric commented 5 years ago

This is going to be because you're chaining with OR outside of subqueries. A model filter runs methods in the order it encounters the input. With your current model filter you're creating:

$sport = 8;
$residence = 12;
User::whereHas('athlete', function($q) use ($sport) {
        return $q->where('sport_id', $sport)->orWhere('secondary_sport_id', $sport);
    })
    ->orWhereHas('scout', function($q) use ($sport) {
        return $q->where('sport_id', $sport);
    })
    ->whereHas('athlete', function($q) use ($value) {
        return $q->where('residence_country_id', $residence);
    })
    ->orWhereHas('scout', function($q) use ($value) {
        return $q->where('residence_country_id', $residence);
    });

Which is why only one of your constraints is being matched.

Wrapping any method using an OR statement in a subquery would fix this:

<?
class UserFilter extends ModelFilter
{
    public $relations = [];

    public function fullname($name)
    {
        return $this->where(function($q) use ($name)
        {
            $names = explode(" ",$name);
            return $q->where('firstname', 'LIKE', "%$name%")
                     ->orWhere('lastname', 'LIKE', "%$name%")
                     ->orWhereIn('lastname',$names)
                     ->whereIn('firstname',$names)
                     ->orWhere('email','LIKE',"%$name%");
        });
    }

    public function sport($sport)
    {
        return $this->where(function($query) use ($sport)
        {
            $this->whereHas('athlete', function($q) use ($sport) {
                return $q->where('sport_id', $sport)->orWhere('secondary_sport_id', $sport);
            })->orWhereHas('scout', function($q) use ($sport) {
                return $q->where('sport_id', $sport);
            });
        });
    }

    public function gender($value)
    {
        return $this->whereHas('athlete', function($q) use ($value) {
            return $q->where('gender', $value);
        });
    }

    public function age($value)
    {

        $arrayNumbers = explode('-',$value);

        $ageFrom = (int)$arrayNumbers[0];
        $ageTo = (int)$arrayNumbers[1];

        $from = Carbon::now()->subYears((int)($ageTo+1));
        $from = $from->format('Y-m-d');
        $to = Carbon::now()->subYears($ageFrom)->format('Y-m-d');
        \Log::info($from);
        \Log::info($to);
        if($this->input('typeUser') == 'Scout' || $this->input('typeUser') == '') return true;
        return $this->related('athlete', function($q) use ($from,$to) {
            return $q->where('date_of_birth','>', $from)->where('date_of_birth','<=', $to);
        });
    }

    public function status($value)
    {
        return $this->whereHas('athlete', function($q) use ($value) {
            return $q->where('status', $value);
        });
    }

    public function sportingGoal($value)
    {
        return $this->whereHas('athlete', function($q) use ($value) {
            return $q->where('sporting_goal', $value);
        });
    }

    public function residenceCountry($value)
    {
        return $this->where(function($query) use ($value)
        {
            return $query->whereHas('athlete', function($q) use ($value) {
                return $q->where('residence_country_id', $value);
            })->orWhereHas('scout', function($q) use ($value) {
                return $q->where('residence_country_id', $value);
            });
        });
    }

    public function nationality($value)
    {
        return $this->where(function($query) use ($value)
        {
            return $this->whereHas('athlete', function($q) use ($value) {
                return $q->where('nationality_id', $value);
            })->orWhereHas('scout', function($q) use ($value) {
                return $q->where('nationality_id', $value);
            });
        });
    }
}
mateo2181 commented 5 years ago

Thanks you!! Now works perfectly!