Skip to content

Instantly share code, notes, and snippets.

@drmmr763
Created November 12, 2015 16:39
Show Gist options
  • Save drmmr763/338c9ab380b533ab2623 to your computer and use it in GitHub Desktop.
Save drmmr763/338c9ab380b533ab2623 to your computer and use it in GitHub Desktop.
custom request criteria
<?php
namespace App\Criteria;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Prettus\Repository\Contracts\RepositoryInterface;
use Prettus\Repository\Contracts\CriteriaInterface;
/**
* Class RequestCriteria
* @package Prettus\Repository\Criteria
*/
class RequestCriteria implements CriteriaInterface
{
/**
* @var \Illuminate\Http\Request
*/
protected $request;
protected $model;
protected $builder;
protected $query;
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Apply criteria in query repository
*
* @param $model
* @param RepositoryInterface $repository
* @return mixed
*/
public function apply($model, RepositoryInterface $repository)
{
$fieldsSearchable = $repository->getFieldsSearchable();
// set the class properties so they can be accessed by other functions
/** @var \Illuminate\Database\Eloquent\Model $model */
$this->model = $model;
$this->builder = $model;
$this->query = $this->builder->getQuery();
// check that the model has searchable fields
if (! is_array($fieldsSearchable) && count($fieldsSearchable)) {
throw new \InvalidArgumentException('The model ' . get_class($model) . 'does not have any search parameters');
}
// verify the request params are fields we can search by
$this->verifySearchFieldsSearchable($fieldsSearchable);
$fields = $this->request->all();
// if we have fields use them to parse
if (is_array($fields) && count($fields)) {
// remove reserved keywords in ordering and filtering
$fields = $this->removeReservedFields($fields);
// build the where statements
$this->parseFilter($fields);
}
// if we have sort params use that to sort
if ($sortBy = $this->request->get('sort', null)) {
// add in order by statements
$this->parseSort($sortBy);
}
$model = $this->builder->setQuery($this->query);
return $model;
}
/**
* Remove fields from the request that are used in sorting or paging
*
* @param array $fields
*
* @return array
*/
public function removeReservedFields(array $fields = array())
{
$defaultParams = config('repository.criteria.params', null);
foreach ($fields as $key => $field)
{
if (in_array($key, $defaultParams)) {
unset($fields[$key]);
}
}
return $fields;
}
/**
* Check the request params and ensure all fields are valid for the repository
*
* @param array $fieldsSearchable array of fields we can search by. supplied by repository
*
* @return $this
*/
public function verifySearchFieldsSearchable($fieldsSearchable)
{
$params = $this->request->all();
$defaultParams = config('repository.criteria.params', null);
// loop params and check they are allowed by the repository
foreach ($params as $key => $param) {
$fieldName = $key;
// if the field name has a dash we split it and use the prefix field name
if (strpos($fieldName, '-')) {
$paramParts = explode('-', $fieldName);
$fieldName = $paramParts[0];
}
// now check and ensure the field name is a valid field to search by
if (! in_array($fieldName, $defaultParams) && ! array_key_exists($fieldName, $fieldsSearchable)) {
throw new \InvalidArgumentException('Field name ' . $fieldName . ' is not an approved search field');
}
}
return $this;
}
/**
* Parse the remaining filter params
*
* Borrowed from https://github.com/marcelgwerder/laravel-api-handler
*
* No license information as of 11/12/2015
*
* @param array $filterParams
* @return $this
*/
protected function parseFilter($filterParams) {
$supportedPostfixes = [
'st' => '<',
'gt' => '>',
'min' => '>=',
'max' => '<=',
'lk' => 'LIKE',
'not-lk' => 'NOT LIKE',
'in' => 'IN',
'not-in' => 'NOT IN',
'not' => '!=',
];
$supportedPrefixesStr = implode('|', $supportedPostfixes);
$supportedPostfixesStr = implode('|', array_keys($supportedPostfixes));
foreach ($filterParams as $filterParamKey => $filterParamValue) {
$keyMatches = [];
//Matches every parameter with an optional prefix and/or postfix
//e.g. not-title-lk, title-lk, not-title, title
$keyRegex = '/^(?:(' . $supportedPrefixesStr . ')-)?(.*?)(?:-(' . $supportedPostfixesStr . ')|$)/';
preg_match($keyRegex, $filterParamKey, $keyMatches);
if (!isset($keyMatches[3])) {
if (strtolower(trim($filterParamValue)) == 'null') {
$comparator = 'NULL';
} else {
$comparator = '=';
}
} else {
if (strtolower(trim($filterParamValue)) == 'null') {
$comparator = 'NOT NULL';
} else {
$comparator = $supportedPostfixes[$keyMatches[3]];
}
}
$column = $keyMatches[2];
if ($comparator == 'IN') {
$values = explode(',', $filterParamValue);
$this->query->whereIn($column, $values);
} else if ($comparator == 'NOT IN') {
$values = explode(',', $filterParamValue);
$this->query->whereNotIn($column, $values);
} else {
$values = explode('|', $filterParamValue);
if (count($values) > 1) {
$this->query->where(function ($query) use ($column, $comparator, $values) {
foreach ($values as $value) {
if ($comparator == 'LIKE' || $comparator == 'NOT LIKE') {
$value = preg_replace('/(^\*|\*$)/', '%', $value);
}
//Link the filters with AND of there is a "not" and with OR if there's none
if ($comparator == '!=' || $comparator == 'NOT LIKE') {
$this->model->where($column, $comparator, $value);
} else {
$this->model->orWhere($column, $comparator, $value);
}
}
});
} else {
$value = $values[0];
if ($comparator == 'LIKE' || $comparator == 'NOT LIKE') {
$value = preg_replace('/(^\*|\*$)/', '%', $value);
}
if ($comparator == 'NULL' || $comparator == 'NOT NULL') {
$this->query->whereNull($column, 'and', $comparator == 'NOT NULL');
} else {
$this->query->where($column, $comparator, $value);
}
}
}
}
return $this;
}
/**
* Parse the sort param and determine whether the sorting is ascending or descending.
* A descending sort has a leading "-". Apply it to the query.
*
* Borrowed from https://github.com/marcelgwerder/laravel-api-handler
*
* No license information as of 11/12/2015
*
* @param string $sortParam
* @return $this
*/
protected function parseSort($sortParam)
{
foreach (explode(',', $sortParam) as $sortElem) {
//Check if ascending or derscenting(-) sort
if (preg_match('/^-.+/', $sortElem)) {
$direction = 'desc';
} else {
$direction = 'asc';
}
$pair = [preg_replace('/^-/', '', $sortElem), $direction];
//Only add the sorts that are on the base resource
if (strpos($sortElem, '.') === false) {
call_user_func_array([$this->query, 'orderBy'], $pair);
} else {
// $this->additionalSorts[] = $pair;
}
}
return $this;
}
}
@andersao
Copy link

Hi @drmmr
I liked it, I'll check and see if implement the package!

@celorodovalho
Copy link

@andersao when?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment