You are here

class EntityCondition in Drupal 10

Same name and namespace in other branches
  1. 8 core/modules/jsonapi/src/Query/EntityCondition.php \Drupal\jsonapi\Query\EntityCondition
  2. 9 core/modules/jsonapi/src/Query/EntityCondition.php \Drupal\jsonapi\Query\EntityCondition

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.

Hierarchy

Expanded class hierarchy of EntityCondition

See also

https://www.drupal.org/project/drupal/issues/3032787

jsonapi.api.php

2 files declare their use of EntityCondition
EntityConditionTest.php in core/modules/jsonapi/tests/src/Unit/Query/EntityConditionTest.php
TemporaryQueryGuard.php in core/modules/jsonapi/src/Access/TemporaryQueryGuard.php

File

core/modules/jsonapi/src/Query/EntityCondition.php, line 17

Namespace

Drupal\jsonapi\Query
View source
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);
      }
    }
  }

}

Members