Created
November 12, 2015 16:39
-
-
Save drmmr763/338c9ab380b533ab2623 to your computer and use it in GitHub Desktop.
custom request criteria
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @drmmr
I liked it, I'll check and see if implement the package!