openstackid-resources/app/Http/Utils/Filters/Filter.php

596 lines
22 KiB
PHP

<?php namespace utils;
/**
* Copyright 2015 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\QueryBuilder;
use Illuminate\Support\Facades\Validator;
use models\exceptions\ValidationException;
/**
* Class Filter
* @package utils
*/
final class Filter
{
/**
* @var array
*/
private $filters = [];
/**
* @var array
*/
private $bindings = [];
public function __construct(array $filters = [])
{
$this->filters = $filters;
}
/**
* @param FilterElement|array $filter
* @return $this
*/
public function addFilterCondition($filter)
{
$this->filters[] = $filter;
return $this;
}
/**
* will return an array of filter elements, OR filters are returned on a sub array
* @param string $field
* @return null|FilterElement[]
*/
public function getFilter($field)
{
$res = [];
foreach ($this->filters as $filter) {
if ($filter instanceof FilterElement && $filter->getField() === $field) {
$res[] = $filter;
}
else if (is_array($filter)) {
// OR
$or_res = [];
foreach ($filter as $e) {
if ($e instanceof FilterElement && $e->getField() === $field) {
$or_res[] = $e;
}
}
foreach ($or_res as $e){
$res[] = $e;
}
}
}
return $res;
}
/**
* @param string $field
* @return null|FilterElement
*/
public function getUniqueFilter($field){
$res = $this->getFilter($field);
return count($res) == 1 ? $res[0]:null;
}
/**
* @param string $field
* @return bool
*/
public function hasFilter($field){
return count($this->getFilter($field)) > 0;
}
/**
* @param string $field
* @return null|FilterElement[]
*/
public function getFlatFilter($field)
{
$res = [];
foreach ($this->filters as $filter) {
if ($filter instanceof FilterElement && $filter->getField() === $field) {
$res[] = $filter;
}
else if (is_array($filter)) {
// OR
foreach ($filter as $e) {
if ($e instanceof FilterElement && $e->getField() === $field) {
$res[] = $e;
}
}
}
}
return $res;
}
/**
* @return array
*/
public function getFiltersKeyValues(){
$res = [];
foreach ($this->filters as $filter) {
if ($filter instanceof FilterElement) {
$res[$filter->getField()] = $filter->getValue();
}
else if (is_array($filter)) {
// OR
foreach ($filter as $e) {
if ($e instanceof FilterElement) {
if(!isset($res[$e->getField()])) $res[$e->getField()] = [];
$res[$e->getField()][] = $e->getValue();
}
}
}
}
return $res;
}
/**
* @param array $rules
* @param array $messages
* @throws ValidationException
*/
public function validate(array $rules, array $messages = []){
$filter_key_values = $this->getFiltersKeyValues();
foreach($rules as $field => $rule) {
if(!isset($filter_key_values[$field])) continue;
$values = $filter_key_values[$field];
if(!is_array($values)) $values = [$values];
foreach ($values as $val) {
if(is_array($val)){
foreach($val as $sub_val){
self::_validate($field, $sub_val, $rule, $messages);
}
}
else {
self::_validate($field, $val, $rule, $messages);
}
}
}
}
private static function _validate($field, $val, $rule, $messages){
$validation = Validator::make
(
[$field => $val],
[$field => $rule],
$messages
);
if ($validation->fails()) {
$ex = new ValidationException();
throw $ex->setMessages($validation->messages()->toArray());
}
}
/**
* @param Criteria $criteria
* @param array $mappings
* @return Criteria
*/
public function apply2Criteria(Criteria $criteria, array $mappings)
{
foreach ($this->filters as $filter) {
if ($filter instanceof FilterElement) {
if (isset($mappings[$filter->getField()])) {
$mapping = $mappings[$filter->getField()];
if ($mapping instanceof FilterMapping) {
continue;
}
$mapping = explode(':', $mapping);
$value = $filter->getValue();
if (count($mapping) > 1) {
$value = $this->convertValue($value, $mapping[1]);
}
$criteria->andWhere(Criteria::expr()->eq($mapping[0], $value));
}
} else if (is_array($filter)) {
// OR
foreach ($filter as $e) {
if ($e instanceof FilterElement && isset($mappings[$e->getField()])) {
$mapping = $mappings[$e->getField()];
if ($mapping instanceof FilterMapping) {
continue;
}
$mapping = explode(':', $mapping);
$value = $filter->getValue();
if (count($mapping) > 1) {
$value = $this->convertValue($value, $mapping[1]);
}
$criteria->orWhere(Criteria::expr()->eq($mapping[0], $value));
}
}
}
}
return $criteria;
}
/**
* @param QueryBuilder $query
* @param array $mappings
* @return $this
*/
public function apply2Query(QueryBuilder $query, array $mappings)
{
$param_prefix = "param_%s";
$param_idx = 1;
$bindings = [];
foreach ($this->filters as $filter) {
if ($filter instanceof FilterElement && isset($mappings[$filter->getField()])) {
$mapping = $mappings[$filter->getField()];
if ($mapping instanceof DoctrineJoinFilterMapping) {
$query = $mapping->apply($query, $filter);
continue;
}
if ($mapping instanceof DoctrineSwitchFilterMapping) {
$query = $mapping->apply($query, $filter);
continue;
}
if ($mapping instanceof DoctrineFilterMapping) {
$query = $mapping->apply($query, $filter);
continue;
}
if ($mapping instanceof DoctrineInstanceOfFilterMapping) {
$query = $mapping->apply($query, $filter);
continue;
}
else if(is_array($mapping)){
$condition = '';
foreach ($mapping as $mapping_or){
$mapping_or = explode(':', $mapping_or);
$value = $filter->getValue();
if (count($mapping_or) > 1) {
$value = $this->convertValue($value, $mapping_or[1]);
}
if(!empty($condition)) $condition .= ' OR ';
/**********************/
if(is_array($value)){
$inner_condition = '( ';
foreach ($value as $val) {
$inner_condition .= sprintf(" %s %s :%s %s ", $mapping[0], $filter->getOperator(), sprintf($param_prefix, $param_idx), $filter->getSameFieldOp());
$bindings[sprintf($param_prefix, $param_idx)] = $val;
++$param_idx;
}
$inner_condition = substr($inner_condition, 0, (strlen($filter->getSameFieldOp())+1) * -1);
$inner_condition .= ' )';
$condition .= $inner_condition;
}
else {
$bindings[sprintf($param_prefix, $param_idx)] = $value;
$condition .= sprintf("%s %s :%s", $mapping_or[0], $filter->getOperator(), sprintf($param_prefix, $param_idx));
++$param_idx;
}
/**********************/
}
$query->andWhere($condition);
}
else {
$mapping = explode(':', $mapping);
$value = $filter->getValue();
$condition = '';
if (count($mapping) > 1) {
$value = $this->convertValue($value, $mapping[1]);
}
if(is_array($value)){
$inner_condition = '( ';
foreach ($value as $val) {
$inner_condition .= sprintf(" %s %s :%s %s ", $mapping[0], $filter->getOperator(), sprintf($param_prefix, $param_idx), $filter->getSameFieldOp());
$bindings[sprintf($param_prefix, $param_idx)] = $val;
++$param_idx;
}
$inner_condition = substr($inner_condition, 0, (strlen($filter->getSameFieldOp())+1) * -1);
$inner_condition .= ' )';
$condition .= $inner_condition;
}
else {
$bindings[sprintf($param_prefix, $param_idx)] = $value;
$condition .= sprintf("%s %s :%s", $mapping[0], $filter->getOperator(), sprintf($param_prefix, $param_idx));
++$param_idx;
}
$query->andWhere($condition);
}
}
else if (is_array($filter)) {
// OR
$sub_or_query = '';
foreach ($filter as $e) {
if ($e instanceof FilterElement && isset($mappings[$e->getField()])) {
$mapping = $mappings[$e->getField()];
if ($mapping instanceof DoctrineJoinFilterMapping) {
$condition = $mapping->applyOr($query, $e);
if(!empty($sub_or_query)) $sub_or_query .= ' OR ';
$sub_or_query .= $condition;
continue;
}
if ($mapping instanceof DoctrineSwitchFilterMapping) {
$condition = $mapping->applyOr($query, $e);
if(!empty($sub_or_query)) $sub_or_query .= ' OR ';
$sub_or_query .= $condition;
continue;
}
if ($mapping instanceof DoctrineFilterMapping) {
$condition = $mapping->applyOr($query, $e);
if(!empty($sub_or_query)) $sub_or_query .= ' OR ';
$sub_or_query .= $condition;
continue;
}
if ($mapping instanceof DoctrineInstanceOfFilterMapping) {
$condition = $mapping->applyOr($query, $e);
if(!empty($sub_or_query)) $sub_or_query .= ' OR ';
$sub_or_query .= $condition;
continue;
}
else if(is_array($mapping)){
$condition = '';
foreach ($mapping as $mapping_or){
$mapping_or = explode(':', $mapping_or);
$value = $e->getValue();
if (count($mapping_or) > 1) {
$value = $this->convertValue($value, $mapping_or[1]);
}
if(!empty($condition)) $condition .= ' OR ';
if(is_array($value)){
$inner_condition = '( ';
foreach ($value as $val) {
$inner_condition .= sprintf(" %s %s :%s %s ", $mapping_or[0], $e->getOperator(), sprintf($param_prefix, $param_idx), $e->getSameFieldOp());
$bindings[sprintf($param_prefix, $param_idx)] = $val;
++$param_idx;
}
$inner_condition = substr($inner_condition, 0, (strlen($e->getSameFieldOp())+1) * -1);
$inner_condition .= ' )';
$condition .= $inner_condition;
}
else {
$bindings[sprintf($param_prefix, $param_idx)] = $value;
$condition .= sprintf("%s %s :%s", $mapping_or[0], $e->getOperator(), sprintf($param_prefix, $param_idx));
++$param_idx;
}
}
if(!empty($sub_or_query)) $sub_or_query .= ' OR ';
$sub_or_query .= ' ( '.$condition.' ) ';
}
else {
$mapping = explode(':', $mapping);
$value = $e->getValue();
if (count($mapping) > 1) {
$value = $this->convertValue($value, $mapping[1]);
}
if(!empty($sub_or_query)) $sub_or_query .= ' OR ';
if(is_array($value)){
$inner_condition = '( ';
foreach ($value as $val) {
$inner_condition .= sprintf(" %s %s :%s %s ", $mapping[0], $e->getOperator(), sprintf($param_prefix, $param_idx), $e->getSameFieldOp());
$bindings[sprintf($param_prefix, $param_idx)] = $val;
++$param_idx;
}
$inner_condition = substr($inner_condition, 0, (strlen($e->getSameFieldOp())+1) * -1);
$inner_condition .= ' )';
$sub_or_query .= $inner_condition;
}
else {
$bindings[sprintf($param_prefix, $param_idx)] = $value;
$sub_or_query .= sprintf("%s %s :%s", $mapping[0], $e->getOperator(), sprintf($param_prefix, $param_idx));
++$param_idx;
}
}
}
}
$query->andWhere($sub_or_query);
}
}
foreach($bindings as $param => $value)
$query->setParameter($param, $value);
return $this;
}
/**
* @param string $value
* @param string $original_format
* @return mixed
*/
private function convertValue($value, $original_format)
{
switch ($original_format) {
case 'datetime_epoch':
if(is_array($value)){
$res = [];
foreach ($value as $val){
$datetime = new \DateTime("@$val");
$res[] = sprintf("%s", $datetime->format("Y-m-d H:i:s"));
}
return $res;
}
$datetime = new \DateTime("@$value");
return sprintf("%s", $datetime->format("Y-m-d H:i:s"));
break;
case 'json_int':
if(is_array($value)){
$res = [];
foreach ($value as $val){
$res[] = intval($val);
}
return $res;
}
return intval($value);
break;
case 'json_string':
if(is_array($value)){
$res = [];
foreach ($value as $val){
$res[] = sprintf("%s", $val);
}
return $res;
}
return sprintf("%s", $value);
break;
default:
return $value;
break;
}
}
/**
* @return array
*/
public function getSQLBindings()
{
return $this->bindings;
}
/**
* @param array $mappings
* @return string
*/
public function toRawSQL(array $mappings, int $param_idx = 1)
{
$sql = '';
$this->bindings = [];
$param_prefix = "param_%s";
foreach ($this->filters as $filter) {
if ($filter instanceof FilterElement) {
if (isset($mappings[$filter->getField()])) {
$mapping = $mappings[$filter->getField()];
$mapping = explode(':', $mapping);
$value = $filter->getValue();
$op = $filter->getOperator();
if (count($mapping) > 1) {
$filter->setValue($this->convertValue($value, $mapping[1]));
}
if(is_array($value)){
$cond = '( ';
foreach ($value as $val) {
$cond .= sprintf(" %s %s :%s %s ", $mapping[0], $op, sprintf($param_prefix, $param_idx), $filter->getSameFieldOp());
$this->bindings[sprintf($param_prefix, $param_idx)] = $val;
++$param_idx;
}
$cond = substr($cond, 0, (strlen($filter->getSameFieldOp())+1) * -1);
$cond .= ' )';
}
else {
$cond = sprintf(' %s %s :%s', $mapping[0], $op, sprintf($param_prefix, $param_idx));
$this->bindings[sprintf($param_prefix, $param_idx)] = $filter->getValue();
++$param_idx;
}
if (!empty($sql)) $sql .= " AND ";
$sql .= $cond;
}
} else if (is_array($filter)) {
// OR
$sql .= " ( ";
$sql_or = '';
foreach ($filter as $e) {
if ($e instanceof FilterElement && isset($mappings[$e->getField()])) {
$mapping = $mappings[$e->getField()];
$mapping = explode(':', $mapping);
$value = $e->getValue();
$op = $e->getOperator();
if (count($mapping) > 1) {
$e->setValue($this->convertValue($value, $mapping[1]));
}
if(is_array($value)){
$cond = '( ';
foreach ($value as $val) {
$cond .= sprintf(" %s %s :%s %s ", $mapping[0], $op, sprintf($param_prefix, $param_idx), $e->getSameFieldOp());
$this->bindings[sprintf($param_prefix, $param_idx)] = $val;
++$param_idx;
}
$cond = substr($cond, 0, (strlen($e->getSameFieldOp())+1) * -1);
$cond .= ' )';
}
else {
$cond = sprintf(" %s %s :%s", $mapping[0], $op, sprintf($param_prefix, $param_idx));
$this->bindings[sprintf($param_prefix, $param_idx)] = $e->getValue();
++$param_idx;
}
if (!empty($sql_or)) $sql_or .= " OR ";
$sql_or .= $cond;
}
}
$sql .= $sql_or . " ) ";
}
}
return $sql;
}
/**
* @param string $field
* @return array
*/
public function getFilterCollectionByField($field){
$list = [];
$filter = $this->getFilter($field);
if(is_array($filter)){
if(is_array($filter[0])){
foreach ($filter[0] as $filter_element)
$list[] = intval($filter_element->getValue());
}
else{
$list[] = intval($filter[0]->getValue());
}
}
return $list;
}
public function __toString():string
{
$res = "";
foreach ($this->filters as $filter) {
if ($filter instanceof FilterElement) {
$res .= '('.$filter.')';
}
else if (is_array($filter)) {
// OR
$or_res = [];
foreach ($filter as $e) {
if ($e instanceof FilterElement) {
$or_res[] = '('.$e.')';
}
}
if (count($or_res)) $res = '('.implode("|",$or_res).')';
}
}
return $res;
}
}