Have you ever done a task in which you were supposed to make a lot of queries with advanced options such as combining many conditions like a report page or search page?

Eloquent Filter

Implement Some tasks are like a Jenga game. If your code is not thought-out at the first place, you will face problem.

If you said yes presumably, You know how faced a lot of troubles when you want to implement these kinds of features, which is why Eloquent Filter in Laravel has been developed in recent years and aims to evolve more and more. If you would like to know basically about this package, You had better check it out here or you can see GitHub.

In short, this package is able to generate a bunch of complex queries based on input which can be a query string or an array on Laravel. For instance :

/users/list?email=mehdifathi.developer@gmail.com

SELECT ... WHERE ... email = 'mehdifathi.developer@gmail.com'

In the first place, you should install this command.

$ composer require mehdi-fathi/eloquent-filter

2- Add eloquentFilter\ServiceProvider::class to provider app.php

'providers' => [
/*
* Package Service Providers...
*/
eloquentFilter\ServiceProvider::class
]

3- Add Facade 'EloquentFilter' => eloquentFilter\Facade\EloquentFilter::class to aliases app.php

'alias' => [
/*
* Facade alias...
*/
'EloquentFilter' => eloquentFilter\Facade\EloquentFilter::class,
],

That’s it enjoy! I want to introduce some new features that have been done lately.

Custom Detection Conditions

Sometimes you want to make your custom condition to make a new query that Eloquent Filter doesn’t support by default. The good news is you would make a custom condition in the eloquent filter from now on.

You can make conditions to generate a new query after checking that. For example :

We must have two classes. The First detects conditions second class generates the query.

  • Step 1: Create a class to detect some conditions
use eloquentFilter\QueryFilter\Detection\Contract\DetectorConditionsContract;

/**
* Class WhereRelationLikeCondition.
*/
class WhereRelationLikeCondition implements DetectorConditionsContract
{
/**
* @param $field
* @param $params
* @param $is_override_method
*
* @return string|null
*/
public static function detect($field, $params, $is_override_method = false): ?string
{
if (!empty($params['value']) && !empty($params['limit']) && !empty($params['email'])) {
$method = WhereRelationLikeConditionQuery::class;
}
return $method ?? null;
}
}
  • Step 2:Right after, create a class to generate a query. In this example we make WhereRelationLikeConditionQuery class:
use eloquentFilter\QueryFilter\Queries\BaseClause;
use Illuminate\Database\Eloquent\Builder;

/**
* Class WhereRelationLikeConditionQuery.
*/
class WhereRelationLikeConditionQuery extends BaseClause
{
/**
* @param $query
*
* @return Builder
*/
public function apply($query): Builder
{
return $query
->whereHas('posts', function ($q) {
$q->where('comment', 'like', "%" . $this->values['like_relation_value'] . "%");
})
->where("$this->filter", '<>', $this->values['value'])
->where('email', 'like', "%" . $this->values['email'] . "%")
->limit($this->values['limit']);
}
}
  • Step 3: You make the method EloquentFilterCustomDetection for return array detections of the condition in the model.
use eloquentFilter\QueryFilter\ModelFilters\Filterable;

class User extends Model
{
use Filterable;

private static $whiteListFilter =[
'username',
'posts.count_post',
'posts.category',
'posts.orders.name',
];
/**
* @return \Illuminate\Database\Eloquent\Relations\belongsTo
*/
public function posts()
{
return $this->belongsTo('Models\Post');
}
public function EloquentFilterCustomDetection(): array
{
return [
WhereRelationLikeCondition::class
];
}
}
  • Each of the query params is used to detect in WhereRelationLikeCondition for the first time after that check by default detection eloquent filter.

Make the method EloquentFilterCustomDetection in the above example and return the array conditions class.

/users/list?username[value]=mehdi&username[limit]=10&username[email]=mehdifathi&username[like_relation_value]=mehdi&count_posts=10
select * from "users"
where exists (select * from "posts" where
"users"."post_id" = "posts"."id"
and "comment" like ?) and "username" <> ? and "email" like ? and "count_posts" = ? limit 10

You just run the code User::filter(); to see the result.

  • Model::setLoadInjectedDetection(false) : You can deactivate custom detection conditions on the fly.

-you can set custom detection on the fly by use of the method SetCustomDetection. For example :

$users = User::SetCustomDetection([WhereRelationLikeCondition::class])->filter();

-You can disable EloquentFilterCustomDetection on the fly by this code :

User::SetLoadDefaultDetection(false)->filter();

-You can set many detection conditions. e.g:

class User extends Model
{
use Filterable;
public function EloquentFilterCustomDetection(): array
{
return [
WhereRelationLikeCondition::class,
WhereRelationLikeVersion2Condition::class,
WhereRelationLikeVersion3Condition::class,
];
}
}
  • EloquentFilter::getInjectedDetections() gets all of your customs injected detection.
  • Every custom detection will run before any detections by default eloquent filter.

Configuring

You can generate a config file to configure Eloquent Filter.

Publish Config

php artisan vendor:publish --provider="eloquentFilter\ServiceProvider"

Config

  • You can disable/enable Eloquent Filter in the config file (eloquentFilter.php).
  • 'enabled' => env('EloquentFilter_ENABLED', true),
  • Eloquent Filter recognizes every param of the query string. Maybe you have a query string that you don’t want to recognize by Eloquent Filter. You can use ignoreRequest it for his purpose. But we have a clean solution to this problem. You can set the param request_filter_key in the config file. Therefore, every query string will recognize by the request_filter_key param.
  • 'request_filter_key' => '', // filter

For example, if you set 'request_filter_key' => 'filter', that Eloquent Filter will recognize filter query string.

/users/list?filter[email]=mehdifathi.developer@gmail.com

  • You can disable/enable all the custom detection of Eloquent Filter in the config file (eloquentFilter.php).
  • 'enabled_custom_detection' => env('EloquentFilter_Custom_Detection_ENABLED', true),
  • You should set an index array ignore_request to ignore all filters.
  • 'ignore_request' => [] //[ 'show_query','new_trend' ],
  • You had better keep max_limit. It's a limitation for preventing making awful queries mistakenly by the developer or intentionally by a villain user.
  • 'max_limit' => 20

Alias

Sometimes you may want to change some parameters in the URL while those mention a field of the model.
e.g. name of the input form is not similar to the model or you want to change them for other reasons so the alias as a new feature can be useful.

class Stat extends Model
{
use Filterable;
/**
* @var array
*/
private static $whiteListFilter = [
'type',
'national_code',
];
/**
* @var array
*/
private $aliasListFilter = [
'national_code' => 'code',
];
}

Then you should send the code param in the URL for making a query with the national code field of the model readily.

Magic Methods

Magic methods are a collection of methods that you can use as a wrapper in the Eloquent Filter. For example, serialize data before filtering or changing data in response and others. Now Eloquent Filter have serializeRequestFilter,ResponseFilter.

Request Filter

Eloquent Filter has a magic method for just change requests injected before handling by the eloquent filter. This method is SerializeRequestFilter. You just implement a method called SerializeRequestFilter in your Model. For example

class User extends Model
{
use Filterable;
public function serializeRequestFilter($request)
{
$request['username'] = trim($request['username']);
return $request;
}
}

As above code, you can modify every query params of the Model in the method serializeRequestFilter before running by Eloquent Filter. That is a practical method when you want to set user_id or convert date or remove space and others.

Response Filter

Response Filter is an overriding method for changing response right after handling by Eloquent Filter. The method is called getResponseFilter and You could implement the method getResponseFilter in your Model. e.g:

class User extends Model
{
use Filterable;
public function getResponseFilter($response)
{
$data['data'] = $response;
return $data;
}
}

Black List Detections

Obviously, you never want all users who are able to get data by manipulating requests. As a result, we’d better have an eloquent control feature. Although we have this ability on the request side, we need this feature on the Eloquent side as well.

We would set a blacklist detection to prevent making conditions by using it. Therefore, that list has been disabled in making conditions. for example:

namespace App\Http\Controllers;
/**
* Class UsersController.
*/
class UsersController
{
public function list()
{
$users = User::setBlackListDetection(
[
'WhereCondition',
]
)->filter()
->orderByDesc('id')
->paginate();
}
}
  • You are able to set it on the Model layer as well. black_list_detections array is used for this purpose.
<?php
namespace Tests\Models;
use eloquentFilter\QueryFilter\ModelFilters\Filterable;
use Illuminate\Database\Eloquent\Model;
class Car extends Model
{
use Filterable;

private static $whiteListFilter = '*';
protected $black_list_detections = [
'WhereCondition',
];
}

Macro Methods

-isUsedEloquentFilter is a macro method for the builder to check either query using eloquent-filter.

-getDetectionsInjected is a macro method to get a list array of injected objects.

e.g:

$users = User::SetCustomDetection([WhereRelationLikeCondition::class])->filter();
echo $users->isUsedEloquentFilter(); // will true
echo $users->getDetectionsInjected(); // will showing a list array of injected objects

--

--