access_policy-1.0.x-dev/src/AccessRuleQueryHandler/EntityFieldBase.php
src/AccessRuleQueryHandler/EntityFieldBase.php
<?php namespace Drupal\access_policy\AccessRuleQueryHandler; use Drupal\access_policy\Validation\ExecutionContext; use Drupal\Core\Entity\EntityInterface; /** * Field value access rule query handler. */ class EntityFieldBase extends AccessRuleQueryHandlerBase { /** * {@inheritdoc} */ public function query() { $this->ensureMyTable(); $values = $this->getValue(); switch ($this->definition->getSetting('empty_behavior')) { case 'deny': $this->queryDenyEmpty($values); break; case 'allow': $this->queryAllowEmpty($values); break; case 'ignore': $this->queryIgnoreEmpty($values); break; default: $this->queryCallback($values, $this->query); break; } } /** * Perform query when empty values are not allowed. * * @param array $values * Array of values. */ public function queryDenyEmpty(array $values) { // If values are empty then add a 0 because that will never be matched // and it will hide it from results. if (empty($values)) { $values = [0]; $this->query->condition($this->realField, $values, 'IN'); } else { $this->queryCallback($values, $this->query); } } /** * Perform regular query when empty values are ignored. * * @param array $values * Array of values. */ public function queryIgnoreEmpty(array $values) { // Ignore will mostly be used with other access rules. However, if there // is only one access rule and its set to ignore it's equivalent to // allow. $access_policy = $this->definition->getAccessPolicy(); $policy = $this->entityTypeManager->getStorage('access_policy')->load($access_policy); $rules = $policy->getAccessRules(); if (count($rules) == 1) { $this->queryAllowEmpty($values); } // If there is more than one access rule then we still want to filter // content that has values. else { if (!empty($values)) { $this->queryCallback($values, $this->query); } else { // If this access rule doesn't have any values, nor do any of the other // access rules have values then we can add an OR IS NULL to be less // restrictive. if (!$this->hasOtherAccessRuleValues($policy)) { $orGroup = $this->query->orConditionGroup(); $orGroup->condition($this->realField, NULL, 'IS NULL'); $this->query->condition($orGroup); } } } } /** * Determine whether any of the other access rules have values. * * If other access rules have values then we need to be more restrictive with * the ignore behavior. If they don't have values then we can be more * forgiving. * * @param \Drupal\Core\Entity\EntityInterface $access_policy * The current access policy. * * @return bool * TRUE if other access rules have values; FALSE otherwise. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ protected function hasOtherAccessRuleValues(EntityInterface $access_policy) { $access_rules = \Drupal::service('plugin.manager.access_policy.access_rule') ->getHandlersFromPolicy($access_policy); $context = ExecutionContext::create('view', ['user' => $this->currentUser]); foreach ($access_rules as $id => $plugin) { $is_not_same_plugin = $id != $this->definition->getId(); if ($is_not_same_plugin) { $plugin->setContext($context); $values = $plugin->getValue(); if (!empty($values)) { return TRUE; } } } return FALSE; } /** * Perform query when empty values are allowed. * * @param array $values * Array of values. */ public function queryAllowEmpty(array $values) { $orGroup = $this->query->orConditionGroup(); $orGroup->condition($this->realField, NULL, 'IS NULL'); if (!empty($values)) { $this->queryCallback($values, $orGroup); } $this->query->condition($orGroup); } /** * Query method callback. * * @param array $values * The array of values. * @param \Drupal\access_policy\QueryInterface|\Drupal\Core\Database\Query\ConditionInterface $query * The condition/query object. This is set to NULL because the query could * happen to a condition group or to the main query, they do not implement * the same interface. */ public function queryCallback(array $values, $query = NULL) { $operator = $this->getOperator(); $info = $this->operators(); if (!empty($info[$operator]['method'])) { $this->{$info[$operator]['method']}($values, $query); } } /** * {@inheritdoc} */ public function ensureMyTable() { $base_field_placeholder = $this->query->getBaseTable() . '.' . $this->query->getBaseField(); $this->query->leftJoin($this->tableAlias, $this->tableAlias, $this->tableAlias . "." . $this->joinField . ' = ' . $base_field_placeholder); } /** * The default operators. * * @return array * Array of operators and their methods. */ public function operators() { return [ 'in' => [ 'method' => 'opIn', ], 'not in' => [ 'method' => 'opNotIn', ], ]; } /** * IN operator. * * @param array $values * The current values. * @param \Drupal\access_policy\QueryInterface|\Drupal\Core\Database\Query\ConditionInterface $query * The query or condition object. */ public function opIn(array $values, $query) { $query->condition($this->realField, $values, 'IN'); } /** * NOT IN operator. * * @param array $values * The current values. * @param \Drupal\access_policy\QueryInterface|\Drupal\Core\Database\Query\ConditionInterface $query * The query or condition object. */ public function opNotIn(array $values, $query) { $query->condition($this->realField, $values, 'NOT IN'); } }