idimensionz/linode-api-v4

View on GitHub
src/LinodeApiV4/Api/Filters/FilterAbstract.php

Summary

Maintainability
B
4 hrs
Test Coverage
<?php
/*
 * iDimensionz/{linode-api-v4}
 * LinodeApiFilterAbstract.php
 *  
 * The MIT License (MIT)
 * 
 * Copyright (c) 2017 Dimensionz
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
*/

namespace iDimensionz\LinodeApiV4\Api\Filters;

use iDimensionz\LinodeApiV4\Api\Filters\FilterConditionFilter;
use iDimensionz\LinodeApiV4\Api\Filters\FilterFieldCondition\FilterFieldConditionAbstract;

abstract class FilterAbstract implements \JsonSerializable
{
    const CONDITION_OPERATOR_AND = '+and';
    const CONDITION_OPERATOR_OR = '+or';

    const FILTER_FIELD_TYPE_INTEGER = 'Integer';
    const FILTER_FIELD_TYPE_STRING = 'String';
    const FILTER_FIELD_TYPE_ENUM = 'Enum';
    const FILTER_FIELD_TYPE_ARRAY_STRING = 'Array[string]';
    const FILTER_FIELD_TYPE_BOOLEAN = 'Boolean';

    /**
     * @var array   Keys and types specific to the actual filter.
     */
    private $filterFields;
    /**
     * @var array   Array of valid values for Enum fields.
     */
    private $filterEnums;
    /**
     * @var array   Filter conditions
     */
    private $conditions;
    /**
     * @var array
     */
    private $conditionOperator;

    public function __construct($conditionOperator=self::CONDITION_OPERATOR_AND)
    {
        $this->setConditionOperator($conditionOperator);
    }

    /**
     * @return array Array containing the X-Filter values.
     */
    public function getHeader()
    {
        $header = ['X-Filter' => json_encode($this)];

        return $header;
    }

    /**
     * @return array
     */
    public function getConditions()
    {
        return $this->conditions;
    }

    /**
     * @param FilterConditionAbstract $condition
     */
    public function addCondition($condition)
    {
        if ($this->isValidCondition($condition)) {
            $this->conditions[] = $condition;
        };
    }

    /**
     * @return array
     */
    protected function getFilterFields()
    {
        return $this->filterFields;
    }

    /**
     * @param array $filterFields
     */
    protected function setFilterFields($filterFields)
    {
        $this->filterFields = $filterFields;
    }

    /**
     * @return array
     */
    protected function getFilterEnums()
    {
        return $this->filterEnums;
    }

    protected function getFilterEnum($field)
    {
        return $this->filterEnums[$field];
    }

    /**
     * @param array $filterEnums
     */
    public function setFilterEnums(array $filterEnums)
    {
        $this->filterEnums = $filterEnums;
    }

    /**
     * @param string    $field
     * @param mixed     $value
     */
    public function addFilterEnum($field, $value)
    {
        $this->filterEnums[$field] = $value;
    }

    /**
     * @return array
     */
    public function getConditionOperator()
    {
        return $this->conditionOperator;
    }

    /**
     * @param string $conditionOperator
     * @throws \InvalidArgumentException
     */
    public function setConditionOperator($conditionOperator)
    {
        if (!$this->isValidConditionOperator($conditionOperator)) {
            throw new \InvalidArgumentException(
                __METHOD__ . '/conditionOperator must be one of ' .
                implode(',', $this->getValidConditionOperators())
            );
        }
        $this->conditionOperator = $conditionOperator;
    }

    /**
     * @return array
     */
    public function getValidConditionOperators()
    {
        return [self::CONDITION_OPERATOR_AND, self::CONDITION_OPERATOR_OR];
    }

    /**
     * @param string $conditionOperator
     * @return bool
     */
    public function isValidConditionOperator($conditionOperator)
    {
        return in_array($conditionOperator, $this->getValidConditionOperators());
    }

    /**
     * @param FilterConditionAbstract $condition
     * @return bool
     */
    protected function isValidCondition($condition)
    {
        $isValidCondition = false;
        if ($condition instanceof FilterConditionAbstract) {
            if ($condition instanceof FilterConditionFilter) {
                $isValidCondition = true;
            } elseif ($condition instanceof FilterFieldConditionAbstract) {
                $filterFields = $this->getFilterFields();
                $filterField = $condition->getField();
                $value = $condition->getValue();
                switch ($filterFields[$filterField]) {
                    case self::FILTER_FIELD_TYPE_INTEGER:
                        $isValidCondition = is_int($value);
                        break;
                    case self::FILTER_FIELD_TYPE_STRING:
                        $value = (string)$value;
                        $isValidCondition = true;
                        break;
                    case self::FILTER_FIELD_TYPE_ARRAY_STRING:
                        $isValidCondition = is_array($value);
                        if ($isValidCondition) {
                            // Force each array item to string type.
                            foreach ($value as $key => $item) {
                                $value[$key] = (string)$item;
                            }
                        }
                        break;
                    case self::FILTER_FIELD_TYPE_ENUM:
                        $isValidCondition = in_array($value, $this->getFilterEnum($filterField));
                        break;
                    default:
                        $isValidCondition = false;
                        break;
                }
            }
        }

        return $isValidCondition;
    }

    /**
     * @return array
     */
    public function jsonSerialize()
    {
        $returnData = [];
        $conditions = $this->getConditions();
        $data = [];
        if (is_array($conditions) && !empty($conditions)) {
            if (self::CONDITION_OPERATOR_OR == $this->getConditionOperator()) {
                foreach ($conditions as $condition) {
                    $data[] = json_encode($condition);
                }
                $returnData = ['+or', $data];
            } elseif (self::CONDITION_OPERATOR_AND == $this->getConditionOperator()) {
                $returnData = $conditions;
                // @todo Add logic to only add '+and' for complex filters. (i.e. single filters don't need '+and').
                // $returnData = ['+and', $data];
            }
        }

        return $returnData;
    }
}