Skip to content
Snippets Groups Projects
Commit c2858a0f authored by catch's avatar catch
Browse files

Reverting patch that was accidentally committed five weeks ago...

parent 1f4c2cf3
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
...@@ -48,15 +48,32 @@ public function execute() { ...@@ -48,15 +48,32 @@ public function execute() {
$entity_type = $this->entityType; $entity_type = $this->entityType;
$entity_info = entity_get_info($entity_type); $entity_info = entity_get_info($entity_type);
if (!isset($entity_info['base_table'])) { if (!isset($entity_info['base_table'])) {
throw new QueryException("No base table, invalid query."); throw new QueryException("No base table, nothing to query.");
} }
$configurable_fields = array_map(function ($data) use ($entity_type) {
return isset($data['bundles'][$entity_type]);
}, field_info_field_map());
$base_table = $entity_info['base_table']; $base_table = $entity_info['base_table'];
// Assemble a list of entity tables, primarily for use in
// \Drupal\field_sql_storage\Entity\Tables::ensureEntityTable().
$entity_tables = array();
$simple_query = TRUE; $simple_query = TRUE;
// ensureEntityTable() decides whether an entity property will be queried
// from the data table or the base table based on where it finds the
// property first. The data table is prefered, which is why it gets added
// before the base table.
if (isset($entity_info['data_table'])) { if (isset($entity_info['data_table'])) {
$entity_tables[$entity_info['data_table']] = drupal_get_schema($entity_info['data_table']);
$simple_query = FALSE; $simple_query = FALSE;
} }
$entity_tables[$base_table] = drupal_get_schema($base_table);
$sqlQuery = $this->connection->select($base_table, 'base_table', array('conjunction' => $this->conjunction)); $sqlQuery = $this->connection->select($base_table, 'base_table', array('conjunction' => $this->conjunction));
$sqlQuery->addMetaData('configurable_fields', $configurable_fields);
$sqlQuery->addMetaData('entity_type', $entity_type); $sqlQuery->addMetaData('entity_type', $entity_type);
// Determines the key of the column to join on. This is either the entity
// id key or the revision id key, depending on whether the entity type
// supports revisions.
$id_key = 'id';
$id_field = $entity_info['entity_keys']['id']; $id_field = $entity_info['entity_keys']['id'];
$fields[$id_field] = TRUE; $fields[$id_field] = TRUE;
if (empty($entity_info['entity_keys']['revision'])) { if (empty($entity_info['entity_keys']['revision'])) {
...@@ -70,6 +87,10 @@ public function execute() { ...@@ -70,6 +87,10 @@ public function execute() {
$revision_field = $entity_info['entity_keys']['revision']; $revision_field = $entity_info['entity_keys']['revision'];
$fields[$revision_field] = TRUE; $fields[$revision_field] = TRUE;
$sqlQuery->addField('base_table', $revision_field); $sqlQuery->addField('base_table', $revision_field);
// Now revision id is column 0 and the value column is 1.
if ($this->age == FIELD_LOAD_CURRENT) {
$id_key = 'revision';
}
} }
// Now add the value column for fetchAllKeyed(). This is always the // Now add the value column for fetchAllKeyed(). This is always the
// entity id. // entity id.
...@@ -95,7 +116,14 @@ public function execute() { ...@@ -95,7 +116,14 @@ public function execute() {
} }
// This now contains first the table containing entity properties and // This now contains first the table containing entity properties and
// last the entity base table. They might be the same. // last the entity base table. They might be the same.
$sqlQuery->addMetaData('entity_tables', $entity_tables);
$sqlQuery->addMetaData('age', $this->age); $sqlQuery->addMetaData('age', $this->age);
// This contains the relevant SQL field to be used when joining entity
// tables.
$sqlQuery->addMetaData('entity_id_field', $entity_info['entity_keys'][$id_key]);
// This contains the relevant SQL field to be used when joining field
// tables.
$sqlQuery->addMetaData('field_id_field', $id_key == 'id' ? 'entity_id' : 'revision_id');
$sqlQuery->addMetaData('simple_query', $simple_query); $sqlQuery->addMetaData('simple_query', $simple_query);
$this->condition->compile($sqlQuery); $this->condition->compile($sqlQuery);
if ($this->count) { if ($this->count) {
......
...@@ -18,7 +18,7 @@ function __construct(Connection $connection) { ...@@ -18,7 +18,7 @@ function __construct(Connection $connection) {
$this->connection = $connection; $this->connection = $connection;
} }
function get($entity_type, $conjunction = 'AND') { function get($entity_type, $conjunction) {
return new Query($entity_type, $conjunction, $this->connection); return new Query($entity_type, $conjunction, $this->connection);
} }
} }
...@@ -59,145 +59,19 @@ function __construct(SelectInterface $sql_query) { ...@@ -59,145 +59,19 @@ function __construct(SelectInterface $sql_query) {
* of this in a query for a condition or sort. * of this in a query for a condition or sort.
*/ */
function addField($field, $type, $langcode) { function addField($field, $type, $langcode) {
$entity_type = $this->sqlQuery->getMetaData('entity_type'); $parts = explode('.', $field);
$age = $this->sqlQuery->getMetaData('age'); $property = $parts[0];
// This variable ensures grouping works correctly. For example: $configurable_fields = $this->sqlQuery->getMetaData('configurable_fields');
// ->condition('tags', 2, '>') if (!empty($configurable_fields[$property]) || substr($property, 0, 3) == 'id:') {
// ->condition('tags', 20, '<') $field_name = $property;
// ->condition('node_reference.nid.entity.tags', 2) $table = $this->ensureFieldTable($field_name, $type, $langcode);
// The first two should use the same table but the last one needs to be a // Default to .value.
// new table. So for the first two, the table array index will be 'tags' $column = isset($parts[1]) ? $parts[1] : 'value';
// while the third will be 'node_reference.nid.tags'. $sql_column = _field_sql_storage_columnname($field_name, $column);
$index_prefix = '';
$specifiers = explode('.', $field);
$base_table = 'base_table';
$count = count($specifiers) - 1;
// This will contain the definitions of the last specifier seen by the
// system.
$propertyDefinitions = array();
$entity_info = entity_get_info($entity_type);
for ($key = 0; $key <= $count; $key ++) {
// If there is revision support and only the current revision is being
// queried then use the revision id. Otherwise, the entity id will do.
if (!empty($entity_info['entity_keys']['revision']) && $age == FIELD_LOAD_CURRENT) {
// This contains the relevant SQL field to be used when joining entity
// tables.
$entity_id_field = $entity_info['entity_keys']['revision'];
// This contains the relevant SQL field to be used when joining field
// tables.
$field_id_field = 'revision_id';
}
else {
$entity_id_field = $entity_info['entity_keys']['id'];
$field_id_field = 'entity_id';
}
// This can either be the name of an entity property (non-configurable
// field), a field API field (a configurable field).
$specifier = $specifiers[$key];
// First, check for field API fields by trying to retrieve the field specified.
// Normally it is a field name, but field_purge_batch() is passing in
// id:$field_id so check that first.
if (substr($specifier, 0, 3) == 'id:') {
$field = field_info_field_by_id(substr($specifier, 3));
}
else {
$field = field_info_field($specifier);
}
// If we managed to retrieve the field, process it.
if ($field) {
// Find the field column.
$column = FALSE;
if ($key < $count) {
$next = $specifiers[$key + 1];
// Is this a field column?
if (isset($field['columns'][$next]) || in_array($next, field_reserved_columns())) {
// Use it.
$column = $next;
// Do not process it again.
$key++;
}
// If there are more specifiers, the next one must be a
// relationship. Either the field name followed by a relationship
// specifier, for example $node->field_image->entity. Or a field
// column followed by a relationship specifier, for example
// $node->field_image->fid->entity. In both cases, prepare the
// property definitions for the relationship. In the first case,
// also use the property definitions for column.
if ($key < $count) {
$relationship_specifier = $specifiers[$key + 1];
$propertyDefinitions = typed_data()
->create(array('type' => $field['type'] . '_field'))
->getPropertyDefinitions();
// If the column is not yet known, ie. the
// $node->field_image->entity case then use the id source as the
// column.
if (!$column && isset($propertyDefinitions[$relationship_specifier]['settings']['id source'])) {
// If this is a valid relationship, use the id source.
// Otherwise, the code executing the relationship will throw an
// exception anyways so no need to do it here.
$column = $propertyDefinitions[$relationship_specifier]['settings']['id source'];
}
// Prepare the next index prefix.
$next_index_prefix = "$relationship_specifier.$column";
}
} }
else { else {
// If this is the last specifier, default to value. $sql_column = $property;
$column = 'value'; $table = $this->ensureEntityTable($property, $type, $langcode);
}
$table = $this->ensureFieldTable($index_prefix, $field, $type, $langcode, $base_table, $entity_id_field, $field_id_field);
$sql_column = _field_sql_storage_columnname($field['field_name'], $column);
}
// This is an entity property (non-configurable field).
else {
// ensureEntityTable() decides whether an entity property will be
// queried from the data table or the base table based on where it
// finds the property first. The data table is prefered, which is why
// it gets added before the base table.
$entity_tables = array();
if (isset($entity_info['data_table'])) {
$this->sqlQuery->addMetaData('simple_query', FALSE);
$entity_tables[$entity_info['data_table']] = drupal_get_schema($entity_info['data_table']);
}
$entity_tables[$entity_info['base_table']] = drupal_get_schema($entity_info['base_table']);
$sql_column = $specifier;
$table = $this->ensureEntityTable($index_prefix, $specifier, $type, $langcode, $base_table, $entity_id_field, $entity_tables);
}
// If there are more specifiers to come, it's a relationship.
if ($key < $count) {
// Computed fields have prepared their property definition already, do
// it for properties as well.
if (!$propertyDefinitions) {
// Create a relevant entity to find the definition for this
// property.
$values = array();
// If there are bundles, pick one. It does not matter which,
// properties exist on all bundles.
if (!empty($entity_info['entity keys']['bundle'])) {
$bundles = array_keys($entity_info['bundles']);
$values[$entity_info['entity keys']['bundle']] = reset($bundles);
}
$entity = entity_create($entity_type, $values);
$propertyDefinitions = $entity->$specifier->getPropertyDefinitions();
$relationship_specifier = $specifiers[$key + 1];
$next_index_prefix = $relationship_specifier;
}
// Check for a valid relationship.
if (isset($propertyDefinitions[$relationship_specifier]['constraints']['entity type']) && isset($propertyDefinitions[$relationship_specifier]['settings']['id source'])) {
// If it is, use the entity type.
$entity_type = $propertyDefinitions[$relationship_specifier]['constraints']['entity type'];
$entity_info = entity_get_info($entity_type);
// Add the new entity base table using the table and sql column.
$join_condition= '%alias.' . $entity_info['entity_keys']['id'] . " = $table.$sql_column";
$base_table = $this->sqlQuery->leftJoin($entity_info['base_table'], NULL, $join_condition);
$propertyDefinitions = array();
$key++;
$index_prefix .= "$next_index_prefix.";
}
else {
throw new QueryException(format_string('Invalid specifier @next.', array('@next' => $next)));
}
}
} }
return "$table.$sql_column"; return "$table.$sql_column";
} }
...@@ -209,13 +83,18 @@ function addField($field, $type, $langcode) { ...@@ -209,13 +83,18 @@ function addField($field, $type, $langcode) {
* @return string * @return string
* @throws \Drupal\Core\Entity\Query\QueryException * @throws \Drupal\Core\Entity\Query\QueryException
*/ */
protected function ensureEntityTable($index_prefix, $property, $type, $langcode, $base_table, $id_field, $entity_tables) { protected function ensureEntityTable($property, $type, $langcode) {
$entity_tables = $this->sqlQuery->getMetaData('entity_tables');
if (!$entity_tables) {
throw new QueryException('Can not query entity properties without entity tables.');
}
foreach ($entity_tables as $table => $schema) { foreach ($entity_tables as $table => $schema) {
if (isset($schema['fields'][$property])) { if (isset($schema['fields'][$property])) {
if (!isset($this->entityTables[$index_prefix . $table])) { if (!isset($this->entityTables[$table])) {
$this->entityTables[$index_prefix . $table] = $this->addJoin($type, $table, "%alias.$id_field = $base_table.$id_field", $langcode); $id_field = $this->sqlQuery->getMetaData('entity_id_field');
$this->entityTables[$table] = $this->addJoin($type, $table, "%alias.$id_field = base_table.$id_field", $langcode);
} }
return $this->entityTables[$index_prefix . $table]; return $this->entityTables[$table];
} }
} }
throw new QueryException(format_string('@property not found', array('@property' => $property))); throw new QueryException(format_string('@property not found', array('@property' => $property)));
...@@ -229,17 +108,31 @@ protected function ensureEntityTable($index_prefix, $property, $type, $langcode, ...@@ -229,17 +108,31 @@ protected function ensureEntityTable($index_prefix, $property, $type, $langcode,
* @return string * @return string
* @throws \Drupal\Core\Entity\Query\QueryException * @throws \Drupal\Core\Entity\Query\QueryException
*/ */
protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $base_table, $entity_id_field, $field_id_field) { protected function ensureFieldTable(&$field_name, $type, $langcode) {
if (!isset($this->fieldTables[$field_name])) {
// This is field_purge_batch() passing in a field id.
if (substr($field_name, 0, 3) == 'id:') {
$field = field_info_field_by_id(substr($field_name, 3));
}
else {
$field = field_info_field($field_name);
}
if (!$field) {
throw new QueryException(format_string('field @field_name not found', array('@field_name' => $field_name)));
}
// This is really necessary only for the id: case but it can't be run
// before throwing the exception.
$field_name = $field['field_name']; $field_name = $field['field_name'];
if (!isset($this->fieldTables[$index_prefix . $field_name])) {
$table = $this->sqlQuery->getMetaData('age') == FIELD_LOAD_CURRENT ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field); $table = $this->sqlQuery->getMetaData('age') == FIELD_LOAD_CURRENT ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
$field_id_field = $this->sqlQuery->getMetaData('field_id_field');
$entity_id_field = $this->sqlQuery->getMetaData('entity_id_field');
if ($field['cardinality'] != 1) { if ($field['cardinality'] != 1) {
$this->sqlQuery->addMetaData('simple_query', FALSE); $this->sqlQuery->addMetaData('simple_query', FALSE);
} }
$entity_type = $this->sqlQuery->getMetaData('entity_type'); $entity_type = $this->sqlQuery->getMetaData('entity_type');
$this->fieldTables[$index_prefix . $field_name] = $this->addJoin($type, $table, "%alias.$field_id_field = $base_table.$entity_id_field AND %alias.entity_type = '$entity_type'", $langcode); $this->fieldTables[$field_name] = $this->addJoin($type, $table, "%alias.$field_id_field = base_table.$entity_id_field AND %alias.entity_type = '$entity_type'", $langcode);
} }
return $this->fieldTables[$index_prefix . $field_name]; return $this->fieldTables[$field_name];
} }
protected function addJoin($type, $table, $join_condition, $langcode) { protected function addJoin($type, $table, $join_condition, $langcode) {
......
<?php
/**
* @file
* Definition of Drupal\Core\Entity\Tests\EntityQueryRelationshipTest.
*/
namespace Drupal\system\Tests\Entity;
use Drupal\simpletest\WebTestBase;
/**
* Tests Entity Query API relationship functionality.
*/
class EntityQueryRelationshipTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('entity_test', 'taxonomy');
/**
* @var \Drupal\field_sql_storage\Entity\QueryFactory
*/
protected $factory;
/**
* Term entities.
*
* @var array
*/
protected $terms;
/**
* User entities.
*
* @var array
*/
public $accounts;
/**
* entity_test entities.
*
* @var array
*/
protected $entities;
/**
* The name of the taxonomy field used for test.
*
* @var string
*/
protected $fieldName;
/**
* The results returned by EntityQuery.
*
* @var array
*/
protected $queryResults;
public static function getInfo() {
return array(
'name' => 'Entity Query relationship',
'description' => 'Tests the Entity Query relationship API',
'group' => 'Entity API',
);
}
protected function setUp() {
parent::setUp();
// We want a taxonomy term reference field. It needs a vocabulary, terms,
// a field and an instance. First, create the vocabulary.
$vocabulary = entity_create('taxonomy_vocabulary', array(
'vid' => drupal_strtolower($this->randomName()),
));
$vocabulary->save();
// Second, create the field.
$this->fieldName = strtolower($this->randomName());
$field = array(
'field_name' => $this->fieldName,
'type' => 'taxonomy_term_reference',
);
$field['settings']['allowed_values']['vocabulary'] = $vocabulary->id();
field_create_field($field);
// Third, create the instance.
$instance = array(
'entity_type' => 'entity_test',
'field_name' => $this->fieldName,
'bundle' => 'entity_test',
);
field_create_instance($instance);
// Create two terms and also two accounts.
for ($i = 0; $i <= 1; $i++) {
$term = entity_create('taxonomy_term', array(
'name' => $this->randomName(),
'vid' => $vocabulary->id(),
));
$term->save();
$this->terms[] = $term;
$this->accounts[] = $this->drupalCreateUser();
}
// Create three entity_test entities, the 0th entity will point to the
// 0th account and 0th term, the 1st and 2nd entity will point to the
// 1st account and 1st term.
for ($i = 0; $i <= 2; $i++) {
$entity = entity_create('entity_test', array());
$entity->name->value = $this->randomName();
$index = $i ? 1 : 0;
$entity->user_id->value = $this->accounts[$index]->uid;
$entity->{$this->fieldName}->tid = $this->terms[$index]->tid;
$entity->save();
$this->entities[] = $entity;
}
$this->factory = drupal_container()->get('entity.query');
}
/**
* Tests querying.
*/
public function testQuery() {
// This returns the 0th entity as that's only one pointing to the 0th
// account.
$this->queryResults = $this->factory->get('entity_test')
->condition("user_id.entity.name", $this->accounts[0]->name)
->execute();
$this->assertResults(array(0));
// This returns the 1st and 2nd entity as those point to the 1st account.
$this->queryResults = $this->factory->get('entity_test')
->condition("user_id.entity.name", $this->accounts[0]->name, '<>')
->execute();
$this->assertResults(array(1, 2));
// This returns all three entities because all of them point to an
// account.
$this->queryResults = $this->factory->get('entity_test')
->exists("user_id.entity.name")
->execute();
$this->assertResults(array(0, 1, 2));
// This returns no entities because all of them point to an account.
$this->queryResults = $this->factory->get('entity_test')
->notExists("user_id.entity.name")
->execute();
$this->assertEqual(count($this->queryResults), 0);
// This returns the 0th entity as that's only one pointing to the 0th
// term (test without specifying the field column).
$this->queryResults = $this->factory->get('entity_test')
->condition("$this->fieldName.entity.name", $this->terms[0]->name)
->execute();
$this->assertResults(array(0));
// This returns the 0th entity as that's only one pointing to the 0th
// term (test with specifying the column name).
$this->queryResults = $this->factory->get('entity_test')
->condition("$this->fieldName.tid.entity.name", $this->terms[0]->name)
->execute();
$this->assertResults(array(0));
// This returns the 1st and 2nd entity as those point to the 1st term.
$this->queryResults = $this->factory->get('entity_test')
->condition("$this->fieldName.entity.name", $this->terms[0]->name, '<>')
->execute();
$this->assertResults(array(1, 2));
}
/**
* Assert the results.
*
* @param array $expected
* A list of indexes in the $this->entities array.
*/
protected function assertResults($expected) {
$this->assertEqual(count($this->queryResults), count($expected));
foreach ($expected as $key) {
$id = $this->entities[$key]->id();
$this->assertEqual($this->queryResults[$id], $id);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment