EntityCondition.php

Same filename in other branches
  1. 9 core/modules/jsonapi/src/Query/EntityCondition.php
  2. 8.9.x core/modules/jsonapi/src/Query/EntityCondition.php
  3. 11.x core/modules/jsonapi/src/Query/EntityCondition.php

Namespace

Drupal\jsonapi\Query

File

core/modules/jsonapi/src/Query/EntityCondition.php

View source
<?php

namespace Drupal\jsonapi\Query;

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Http\Exception\CacheableBadRequestHttpException;

/**
 * A condition object for the EntityQuery.
 *
 * @internal JSON:API maintains no PHP API since its API is the HTTP API. This
 *   class may change at any time and this will break any dependencies on it.
 *
 * @see https://www.drupal.org/project/drupal/issues/3032787
 * @see jsonapi.api.php
 */
class EntityCondition {
    
    /**
     * The field key in the filter condition: filter[lorem][condition][<field>].
     *
     * @var string
     */
    const PATH_KEY = 'path';
    
    /**
     * The value key in the filter condition: filter[lorem][condition][<value>].
     *
     * @var string
     */
    const VALUE_KEY = 'value';
    
    /**
     * The operator key in the condition: filter[lorem][condition][<operator>].
     *
     * @var string
     */
    const OPERATOR_KEY = 'operator';
    
    /**
     * The allowed condition operators.
     *
     * @var string[]
     */
    public static $allowedOperators = [
        '=',
        '<>',
        '>',
        '>=',
        '<',
        '<=',
        'STARTS_WITH',
        'CONTAINS',
        'ENDS_WITH',
        'IN',
        'NOT IN',
        'BETWEEN',
        'NOT BETWEEN',
        'IS NULL',
        'IS NOT NULL',
    ];
    
    /**
     * The field to be evaluated.
     *
     * @var string
     */
    protected $field;
    
    /**
     * The condition operator.
     *
     * @var string
     */
    protected $operator;
    
    /**
     * The value against which the field should be evaluated.
     *
     * @var mixed
     */
    protected $value;
    
    /**
     * Constructs a new EntityCondition object.
     */
    public function __construct($field, $value, $operator = NULL) {
        $this->field = $field;
        $this->value = $value;
        $this->operator = $operator ? $operator : '=';
    }
    
    /**
     * The field to be evaluated.
     *
     * @return string
     *   The field upon which to evaluate the condition.
     */
    public function field() {
        return $this->field;
    }
    
    /**
     * The comparison operator to use for the evaluation.
     *
     * For a list of allowed operators:
     *
     * @see \Drupal\jsonapi\Query\EntityCondition::allowedOperators
     *
     * @return string
     *   The condition operator.
     */
    public function operator() {
        return $this->operator;
    }
    
    /**
     * The value against which the condition should be evaluated.
     *
     * @return mixed
     *   The condition comparison value.
     */
    public function value() {
        return $this->value;
    }
    
    /**
     * Creates an EntityCondition object from a query parameter.
     *
     * @param mixed $parameter
     *   The `filter[condition]` query parameter from the request.
     *
     * @return self
     *   An EntityCondition object with defaults.
     */
    public static function createFromQueryParameter($parameter) {
        static::validate($parameter);
        $field = $parameter[static::PATH_KEY];
        $value = isset($parameter[static::VALUE_KEY]) ? $parameter[static::VALUE_KEY] : NULL;
        $operator = isset($parameter[static::OPERATOR_KEY]) ? $parameter[static::OPERATOR_KEY] : NULL;
        return new static($field, $value, $operator);
    }
    
    /**
     * Validates the filter has the required fields.
     */
    protected static function validate($parameter) {
        $valid_key_combinations = [
            [
                static::PATH_KEY,
                static::VALUE_KEY,
            ],
            [
                static::PATH_KEY,
                static::OPERATOR_KEY,
            ],
            [
                static::PATH_KEY,
                static::VALUE_KEY,
                static::OPERATOR_KEY,
            ],
        ];
        $given_keys = array_keys($parameter);
        $valid_key_set = array_reduce($valid_key_combinations, function ($valid, $set) use ($given_keys) {
            return $valid ? $valid : count(array_diff($set, $given_keys)) === 0;
        }, FALSE);
        $has_operator_key = isset($parameter[static::OPERATOR_KEY]);
        $has_path_key = isset($parameter[static::PATH_KEY]);
        $has_value_key = isset($parameter[static::VALUE_KEY]);
        $cacheability = (new CacheableMetadata())->addCacheContexts([
            'url.query_args:filter',
        ]);
        if (!$valid_key_set) {
            // Try to provide a more specific exception is a key is missing.
            if (!$has_operator_key) {
                if (!$has_path_key) {
                    throw new CacheableBadRequestHttpException($cacheability, "Filter parameter is missing a '" . static::PATH_KEY . "' key.");
                }
                if (!$has_value_key) {
                    throw new CacheableBadRequestHttpException($cacheability, "Filter parameter is missing a '" . static::VALUE_KEY . "' key.");
                }
            }
            // Catchall exception.
            $reason = "You must provide a valid filter condition. Check that you have set the required keys for your filter.";
            throw new CacheableBadRequestHttpException($cacheability, $reason);
        }
        if ($has_operator_key) {
            $operator = $parameter[static::OPERATOR_KEY];
            if (!in_array($operator, static::$allowedOperators)) {
                $reason = "The '" . $operator . "' operator is not allowed in a filter parameter.";
                throw new CacheableBadRequestHttpException($cacheability, $reason);
            }
            if (in_array($operator, [
                'IS NULL',
                'IS NOT NULL',
            ]) && $has_value_key) {
                $reason = "Filters using the '" . $operator . "' operator should not provide a value.";
                throw new CacheableBadRequestHttpException($cacheability, $reason);
            }
        }
    }

}

Classes

Title Deprecated Summary
EntityCondition A condition object for the EntityQuery.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.