new: [metaFields] Support of meta-fields types

pull/93/head
Sami Mokaddem 2022-02-25 15:22:57 +01:00
parent a9570426db
commit 6cb9887f03
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
3 changed files with 250 additions and 4 deletions

View File

@ -0,0 +1,178 @@
<?php
namespace MetaFieldsTypes;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\TableRegistry;
use Cake\ORM\Query;
use MetaFieldsTypes\TextType;
use TypeError;
class IPv4Type extends TextType
{
public const OPERATORS = ['contains', 'excludes'];
public const TYPE = 'ipv4';
public function __construct()
{
parent::__construct();
}
/**
* Validate the provided value against the expected type
*
* @param string $value
* @return boolean
*/
public function validate(string $value): bool
{
return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ||
filter_var(explode('/', $value)[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
}
public function setQueryExpression(QueryExpression $exp, string $searchValue, \App\Model\Entity\MetaTemplateField $metaTemplateField): QueryExpression
{
if (strpos($searchValue, '%') !== false) {
$textHandler = new TextType(); // we are wildcard filtering, use text filter instead
return $textHandler->setQueryExpression($exp, $searchValue, $metaTemplateField);
}
$allMetaValues = $this->fetchAllValuesForThisType([], $metaTemplateField);
$isNegation = false;
if (substr($searchValue, 0, 1) == '!') {
$searchValue = substr($searchValue, 1);
$isNegation = true;
}
foreach ($allMetaValues as $fieldID => $ip) {
if (!$this->IPInCidrBlock($searchValue, $ip)) {
if (!$isNegation) {
unset($allMetaValues[$fieldID]);
}
} else if ($isNegation) {
unset($allMetaValues[$fieldID]);
}
}
$matchingIDs = array_keys($allMetaValues);
if (!empty($matchingIDs)) {
$exp->in('MetaFields.id', $matchingIDs);
} else {
$exp->eq('MetaFields.id', -1); // No matching meta-fields, generate an impossible condition to return nothing
}
return $exp;
}
private function fetchAllMetatemplateFieldsIdForThisType(\App\Model\Entity\MetaTemplateField $metaTemplateField = null): Query
{
$conditions =[];
if (!is_null($metaTemplateField)) {
$conditions['id'] = $metaTemplateField->id;
} else {
$conditions['type'] = IPv4Type::TYPE;
}
$query = $this->MetaTemplateFields->find()->select(['id'])
->distinct()
->where($conditions);
return $query;
}
private function fetchAllValuesForThisType(array $conditions=[], \App\Model\Entity\MetaTemplateField $metaTemplateField=null): array
{
$metaTemplateFieldsIDs = $this->fetchAllMetatemplateFieldsIdForThisType($metaTemplateField);
if (empty($metaTemplateFieldsIDs)) {
return [];
}
$conditions = array_merge($conditions, ['meta_template_field_id IN' => $metaTemplateFieldsIDs]);
$allMetaValues = $this->MetaFields->find('list', [
'keyField' => 'id',
'valueField' => 'value'
])->where($conditions)->toArray();
return $allMetaValues;
}
/**
* Convert a CIDR block to an array containing the minimum and maximum IP address for this block
*
* @param string $cidr an CIDR block with the form x.x.x.x/yy
* @return array
*/
protected function cidrToRange($cidr): array
{
$range = array();
$cidr = explode('/', $cidr);
$range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1]))));
$range[1] = long2ip((ip2long($range[0])) + pow(2, (32 - (int)$cidr[1])) - 1);
return $range;
}
/**
* Check if the provided IP in contained by the CIDR block
*
* @param string $ip
* @param string $cidr an CIDR block with the form x.x.x.x/yy
* @return boolean
*/
private function _IPInCidrBlock(string $ip, string $cidr): bool
{
$range = $this->cidrToRange($cidr);
return ip2long($range[0]) <= ip2long($ip) && ip2long($ip) <= ip2long($range[1]);
}
/**
* Check if the provided cidr block in contained by the CIDR block
*
* @param string $cidr1 an CIDR block with the form x.x.x.x/yy
* @param string $cidr2 an CIDR block with the form x.x.x.x/yy
* @return boolean
*/
private function _cidrInCidrBlock(string $cidr1, string $cidr2): bool
{
$range = $this->cidrToRange($cidr1);
return $this->_IPInCidrBlock($range[0], $cidr2) && $this->_IPInCidrBlock($range[1], $cidr2);
}
private function _isValidIP(string $value): bool
{
return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
}
private function _isValidCidrBlock(string $value): bool
{
$explodedValue = explode('/', $value);
return $this->_isValidIP($explodedValue[0]);
}
private function _isValidIPOrCidrBlock(string $value): bool
{
$explodedValue = explode('/', $value);
if (count($explodedValue) == 1) {
return $this->_isValidIP($value);
} else if (count($explodedValue) == 2) {
return $this->_isValidCidrBlock($value);
}
return false;
}
protected function IPInCidrBlock(string $ip, string $cidr, bool $throw=false): bool
{
if (!$this->_isValidCidrBlock($cidr)) {
if ($throw) {
throw new TypeError("Invalid CDIR block.");
}
return false;
}
if (!$this->_isValidIPOrCidrBlock($ip)) {
if ($throw) {
throw new TypeError("Invalid IP.");
}
return false;
}
$explodedIp = explode('/', $ip);
if (count($explodedIp) == 1) {
return $this->_IPInCidrBlock($ip, $cidr);
} else {
return $this->_cidrInCidrBlock($ip, $cidr);
}
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace MetaFieldsTypes;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\TableRegistry;
class TextType
{
public const OPERATORS = ['=', '!='];
public const TYPE = 'text';
public function __construct()
{
$this->MetaFields = TableRegistry::getTableLocator()->get('MetaFields');
$this->MetaTemplateFields = TableRegistry::getTableLocator()->get('MetaTemplateFields');
}
public function validate(string $value): bool
{
return is_string($value);
}
public function setQueryExpression(QueryExpression $exp, string $searchValue, \App\Model\Entity\MetaTemplateField $metaTemplateField): QueryExpression
{
$field = 'MetaFields.value';
if (substr($searchValue, 0, 1) == '!') {
$searchValue = substr($searchValue, 1);
$exp->notEq($field, $searchValue);
} else if (strpos($searchValue, '%') !== false) {
$exp->like($field, $searchValue);
} else {
$exp->eq($field, $searchValue);
}
return $exp;
}
}

View File

@ -6,10 +6,14 @@ use Cake\ORM\Behavior;
use Cake\ORM\Entity;
use Cake\ORM\Query;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\Utility\Inflector;
use Cake\Database\Expression\QueryExpression;
use function PHPSTORM_META\type;
use MetaFieldsTypes\TextType;
use MetaFieldsTypes\IPv4Type;
require_once(APP . 'Lib' . DS . 'default' . DS . 'meta_fields_types' . DS . 'TextType.php');
require_once(APP . 'Lib' . DS . 'default' . DS . 'meta_fields_types' . DS . 'IPv4Type.php');
class MetaFieldsBehavior extends Behavior
{
@ -44,12 +48,25 @@ class MetaFieldsBehavior extends Behavior
];
private $aliasScope = null;
private $typeHandlers = [];
public function initialize(array $config): void
{
$this->bindAssociations();
$this->_metaTemplateFieldTable = $this->_table;
$this->_metaTemplateTable = $this->_table;
$this->loadTypeHandlers();
}
private function loadTypeHandlers()
{
$typeHandlers = [
new TextType(),
new IPv4Type(),
];
foreach ($typeHandlers as $handler) {
$this->typeHandlers[$handler::TYPE] = $handler;
}
}
public function getScope()
@ -198,7 +215,7 @@ class MetaFieldsBehavior extends Behavior
}
protected function getQueryExpressionForField(QueryExpression $exp, string $field, string $value): QueryExpression
protected function setQueryExpressionForTextField(QueryExpression $exp, string $field, string $value): QueryExpression
{
if (substr($value, 0, 1) == '!') {
$value = substr($value, 1);
@ -213,10 +230,16 @@ class MetaFieldsBehavior extends Behavior
protected function buildQuerySnippet(array $filter): Query
{
$whereClosure = function (QueryExpression $exp) use ($filter) {
$this->MetaTemplateFields = TableRegistry::getTableLocator()->get('MetaTemplateFields');
$metaTemplateField = $this->MetaTemplateFields->get($filter['meta_template_field_id']);
$whereClosure = function (QueryExpression $exp) use ($filter, $metaTemplateField) {
foreach ($filter as $column => $value) {
$keyedColumn = 'MetaFields.' . $column;
$this->getQueryExpressionForField($exp, $keyedColumn, $value);
if ($column == 'value') {
$this->setQueryExpressionForField($exp, $keyedColumn, $value, $metaTemplateField);
} else {
$this->setQueryExpressionForTextField($exp, $keyedColumn, $value);
}
}
return $exp;
};
@ -228,4 +251,13 @@ class MetaFieldsBehavior extends Behavior
return $query;
}
protected function setQueryExpressionForField(QueryExpression $exp, string $field, string $value, \App\Model\Entity\MetaTemplateField $metaTemplateField): QueryExpression
{
if (isset($this->typeHandlers[$metaTemplateField->type])) {
$exp = $this->typeHandlers[$metaTemplateField->type]->setQueryExpression($exp, $value, $metaTemplateField);
} else {
$exp = $this->setQueryExpressionForTextField($exp, $field, $value);
}
return $exp;
}
}