Issue #2068655 by Berdir, chx, martin107, longwave, webflo, damiankloip, lokapujya, JacobSanford: Entity fields do not support case sensitive queries
parent
85764e4e43
commit
88a46ae942
|
@ -449,6 +449,9 @@ field.storage_settings.string:
|
|||
max_length:
|
||||
type: integer
|
||||
label: 'Maximum length'
|
||||
case_sensitive:
|
||||
type: boolean
|
||||
label: 'Case sensitive'
|
||||
|
||||
field.field_settings.string:
|
||||
type: mapping
|
||||
|
@ -467,6 +470,10 @@ field.value.string:
|
|||
field.storage_settings.string_long:
|
||||
type: mapping
|
||||
label: 'String (long) settings'
|
||||
mapping:
|
||||
case_sensitive:
|
||||
type: boolean
|
||||
label: 'Case sensitive'
|
||||
|
||||
field.field_settings.string_long:
|
||||
type: mapping
|
||||
|
@ -489,6 +496,9 @@ field.storage_settings.uri:
|
|||
max_length:
|
||||
type: integer
|
||||
label: 'Maximum length'
|
||||
case_sensitive:
|
||||
type: boolean
|
||||
label: 'Case sensitive'
|
||||
|
||||
field.field_settings.uri:
|
||||
type: mapping
|
||||
|
|
|
@ -271,3 +271,4 @@ field.formatter.settings.uri_link:
|
|||
field.formatter.settings.timestamp_ago:
|
||||
type: mapping
|
||||
label: 'Timestamp ago display format settings'
|
||||
|
||||
|
|
|
@ -229,6 +229,7 @@ class Connection extends DatabaseConnection {
|
|||
// In PostgreSQL, 'LIKE' is case-sensitive. For case-insensitive LIKE
|
||||
// statements, we need to use ILIKE instead.
|
||||
'LIKE' => array('operator' => 'ILIKE'),
|
||||
'LIKE BINARY' => array('operator' => 'LIKE'),
|
||||
'NOT LIKE' => array('operator' => 'NOT ILIKE'),
|
||||
'REGEXP' => array('operator' => '~*'),
|
||||
);
|
||||
|
|
|
@ -24,6 +24,19 @@ interface ConditionInterface {
|
|||
* Do not use this method to test for NULL values. Instead, use
|
||||
* QueryConditionInterface::isNull() or QueryConditionInterface::isNotNull().
|
||||
*
|
||||
* Drupal considers LIKE case insensitive and the following is often used
|
||||
* to tell the database that case insensitive equivalence is desired:
|
||||
* @code
|
||||
* db_select('users')
|
||||
* ->condition('name', db_like($name), 'LIKE')
|
||||
* @endcode
|
||||
* Use 'LIKE BINARY' instead of 'LIKE' for case sensitive queries.
|
||||
*
|
||||
* Note: When using MySQL, the exact behavior also depends on the used
|
||||
* collation. if the field is set to binary, then a LIKE condition will also
|
||||
* be case sensitive and when a case insensitive collation is used, the =
|
||||
* operator will also be case insensitive.
|
||||
*
|
||||
* @param $field
|
||||
* The name of the field to check. If you would like to add a more complex
|
||||
* condition involving operators or functions, use where().
|
||||
|
@ -33,8 +46,8 @@ interface ConditionInterface {
|
|||
* the array is dependent on the $operator.
|
||||
* @param $operator
|
||||
* The comparison operator, such as =, <, or >=. It also accepts more
|
||||
* complex options such as IN, LIKE, or BETWEEN. Defaults to IN if $value is
|
||||
* an array, and = otherwise.
|
||||
* complex options such as IN, LIKE, LIKE BINARY, or BETWEEN. Defaults to IN
|
||||
* if $value is an array, and = otherwise.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Query\ConditionInterface
|
||||
* The called object.
|
||||
|
|
|
@ -498,6 +498,13 @@ class Select extends Query implements SelectInterface {
|
|||
return $this->union;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function escapeLike($string) {
|
||||
return $this->connection->escapeLike($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -984,4 +991,5 @@ class Select extends Query implements SelectInterface {
|
|||
$this->union[$key]['query'] = clone($aggregate['query']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -171,6 +171,13 @@ class SelectExtender implements SelectInterface {
|
|||
return $this->query->getUnion();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function escapeLike($string) {
|
||||
return $this->query->escapeLike($string);
|
||||
}
|
||||
|
||||
public function getArguments(PlaceholderInterface $queryPlaceholder = NULL) {
|
||||
return $this->query->getArguments($queryPlaceholder);
|
||||
}
|
||||
|
|
|
@ -125,6 +125,19 @@ interface SelectInterface extends ConditionInterface, AlterableInterface, Extend
|
|||
*/
|
||||
public function &getUnion();
|
||||
|
||||
/**
|
||||
* Escapes characters that work as wildcard characters in a LIKE pattern.
|
||||
*
|
||||
* @param $string
|
||||
* The string to escape.
|
||||
*
|
||||
* @return string
|
||||
* The escaped string.
|
||||
*
|
||||
* @see \Drupal\Core\Database\Connection::escapeLike()
|
||||
*/
|
||||
public function escapeLike($string);
|
||||
|
||||
/**
|
||||
* Compiles and returns an associative array of the arguments for this prepared statement.
|
||||
*
|
||||
|
|
|
@ -54,7 +54,7 @@ interface QueryInterface extends AlterableInterface {
|
|||
* same delta within that field.
|
||||
* @param $value
|
||||
* The value for $field. In most cases, this is a scalar and it's treated as
|
||||
* case-insensitive. For more complex options, it is an array. The meaning
|
||||
* case-insensitive. For more complex operators, it is an array. The meaning
|
||||
* of each element in the array is dependent on $operator.
|
||||
* @param $operator
|
||||
* Possible values:
|
||||
|
|
|
@ -33,20 +33,20 @@ class Condition extends ConditionBase {
|
|||
// SQL query object is only necessary to pass to Query::addField() so it
|
||||
// can join tables as necessary. On the other hand, conditions need to be
|
||||
// added to the $conditionContainer object to keep grouping.
|
||||
$sqlQuery = $conditionContainer instanceof SelectInterface ? $conditionContainer : $conditionContainer->sqlQuery;
|
||||
$tables = $this->query->getTables($sqlQuery);
|
||||
$sql_query = $conditionContainer instanceof SelectInterface ? $conditionContainer : $conditionContainer->sqlQuery;
|
||||
$tables = $this->query->getTables($sql_query);
|
||||
foreach ($this->conditions as $condition) {
|
||||
if ($condition['field'] instanceOf ConditionInterface) {
|
||||
$sqlCondition = new SqlCondition($condition['field']->getConjunction());
|
||||
$sql_condition = new SqlCondition($condition['field']->getConjunction());
|
||||
// Add the SQL query to the object before calling this method again.
|
||||
$sqlCondition->sqlQuery = $sqlQuery;
|
||||
$condition['field']->compile($sqlCondition);
|
||||
$sqlQuery->condition($sqlCondition);
|
||||
$sql_condition->sqlQuery = $sql_query;
|
||||
$condition['field']->compile($sql_condition);
|
||||
$sql_query->condition($sql_condition);
|
||||
}
|
||||
else {
|
||||
$type = strtoupper($this->conjunction) == 'OR' || $condition['operator'] == 'IS NULL' ? 'LEFT' : 'INNER';
|
||||
$this->translateCondition($condition);
|
||||
$field = $tables->addField($condition['field'], $type, $condition['langcode']);
|
||||
static::translateCondition($condition, $sql_query, $tables->isFieldCaseSensitive($condition['field']));
|
||||
$conditionContainer->condition($field, $condition['value'], $condition['operator']);
|
||||
}
|
||||
}
|
||||
|
@ -70,24 +70,70 @@ class Condition extends ConditionBase {
|
|||
* Translates the string operators to SQL equivalents.
|
||||
*
|
||||
* @param array $condition
|
||||
* The condition array.
|
||||
* @param \Drupal\Core\Database\Query\SelectInterface $sql_query
|
||||
* Select query instance.
|
||||
* @param bool|null $case_sensitive
|
||||
* If the condition should be case sensitive or not, NULL if the field does
|
||||
* not define it.
|
||||
*
|
||||
* @see \Drupal\Core\Database\Query\ConditionInterface::condition()
|
||||
*/
|
||||
protected function translateCondition(&$condition) {
|
||||
public static function translateCondition(&$condition, SelectInterface $sql_query, $case_sensitive) {
|
||||
// There is nothing we can do for IN ().
|
||||
if (is_array($condition['value'])) {
|
||||
return;
|
||||
}
|
||||
// Ensure that the default operator is set to simplify the cases below.
|
||||
if (empty($condition['operator'])) {
|
||||
$condition['operator'] = '=';
|
||||
}
|
||||
switch ($condition['operator']) {
|
||||
case '=':
|
||||
// If a field explicitly requests that queries should not be case
|
||||
// sensitive, use the LIKE operator, otherwise keep =.
|
||||
if ($case_sensitive === FALSE) {
|
||||
$condition['value'] = $sql_query->escapeLike($condition['value']);
|
||||
$condition['operator'] = 'LIKE';
|
||||
}
|
||||
break;
|
||||
case '<>':
|
||||
// If a field explicitly requests that queries should not be case
|
||||
// sensitive, use the NOT LIKE operator, otherwise keep <>.
|
||||
if ($case_sensitive === FALSE) {
|
||||
$condition['value'] = $sql_query->escapeLike($condition['value']);
|
||||
$condition['operator'] = 'NOT LIKE';
|
||||
}
|
||||
break;
|
||||
case 'STARTS_WITH':
|
||||
$condition['value'] .= '%';
|
||||
$condition['operator'] = 'LIKE';
|
||||
if ($case_sensitive) {
|
||||
$condition['operator'] = 'LIKE BINARY';
|
||||
}
|
||||
else {
|
||||
$condition['operator'] = 'LIKE';
|
||||
}
|
||||
$condition['value'] = $sql_query->escapeLike($condition['value']) . '%';
|
||||
break;
|
||||
|
||||
case 'CONTAINS':
|
||||
$condition['value'] = '%' . $condition['value'] . '%';
|
||||
$condition['operator'] = 'LIKE';
|
||||
if ($case_sensitive) {
|
||||
$condition['operator'] = 'LIKE BINARY';
|
||||
}
|
||||
else {
|
||||
$condition['operator'] = 'LIKE';
|
||||
}
|
||||
$condition['value'] = '%' . $sql_query->escapeLike($condition['value']) . '%';
|
||||
break;
|
||||
|
||||
case 'ENDS_WITH':
|
||||
$condition['value'] = '%' . $condition['value'];
|
||||
$condition['operator'] = 'LIKE';
|
||||
if ($case_sensitive) {
|
||||
$condition['operator'] = 'LIKE BINARY';
|
||||
}
|
||||
else {
|
||||
$condition['operator'] = 'LIKE';
|
||||
}
|
||||
$condition['value'] = '%' . $sql_query->escapeLike($condition['value']);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace Drupal\Core\Entity\Query\Sql;
|
|||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Entity\Query\ConditionAggregateBase;
|
||||
use Drupal\Core\Entity\Query\ConditionAggregateInterface;
|
||||
use Drupal\Core\Database\Query\Condition as SqlCondition;
|
||||
|
||||
/**
|
||||
* Defines the aggregate condition for sql based storage.
|
||||
|
@ -29,7 +30,7 @@ class ConditionAggregate extends ConditionAggregateBase {
|
|||
$tables = new Tables($sql_query);
|
||||
foreach ($this->conditions as $condition) {
|
||||
if ($condition['field'] instanceOf ConditionAggregateInterface) {
|
||||
$sql_condition = new Condition($condition['field']->getConjunction());
|
||||
$sql_condition = new SqlCondition($condition['field']->getConjunction());
|
||||
// Add the SQL query to the object before calling this method again.
|
||||
$sql_condition->sqlQuery = $sql_query;
|
||||
$condition['field']->compile($sql_condition);
|
||||
|
@ -37,8 +38,8 @@ class ConditionAggregate extends ConditionAggregateBase {
|
|||
}
|
||||
else {
|
||||
$type = ((strtoupper($this->conjunction) == 'OR') || ($condition['operator'] == 'IS NULL')) ? 'LEFT' : 'INNER';
|
||||
$this->translateCondition($condition);
|
||||
$field = $tables->addField($condition['field'], $type, $condition['langcode']);
|
||||
Condition::translateCondition($condition, $sql_query, $tables->isFieldCaseSensitive($condition['field']));
|
||||
$function = $condition['function'];
|
||||
$placeholder = ':db_placeholder_' . $conditionContainer->nextPlaceholder();
|
||||
$conditionContainer->having("$function($field) {$condition['operator']} $placeholder", array($placeholder => $condition['value']));
|
||||
|
@ -60,29 +61,4 @@ class ConditionAggregate extends ConditionAggregateBase {
|
|||
return $this->condition($field, $function, NULL, 'IS NULL', $langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the string operators to SQL equivalents.
|
||||
*
|
||||
* @param array $condition
|
||||
* An associative array containing the following keys:
|
||||
* - value: The value to filter by
|
||||
* - operator: The operator to use for comparison, for example "=".
|
||||
*/
|
||||
protected function translateCondition(&$condition) {
|
||||
switch ($condition['operator']) {
|
||||
case 'STARTS_WITH':
|
||||
$condition['value'] .= '%';
|
||||
$condition['operator'] = 'LIKE';
|
||||
break;
|
||||
case 'CONTAINS':
|
||||
$condition['value'] = '%' . $condition['value'] . '%';
|
||||
$condition['operator'] = 'LIKE';
|
||||
break;
|
||||
case 'ENDS_WITH':
|
||||
$condition['value'] = '%' . $condition['value'];
|
||||
$condition['operator'] = 'LIKE';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -48,6 +48,13 @@ class Tables implements TablesInterface {
|
|||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* List of case sensitive fields.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $caseSensitiveFields = array();
|
||||
|
||||
/**
|
||||
* @param \Drupal\Core\Database\Query\SelectInterface $sql_query
|
||||
*/
|
||||
|
@ -139,6 +146,10 @@ class Tables implements TablesInterface {
|
|||
}
|
||||
$table = $this->ensureFieldTable($index_prefix, $field_storage, $type, $langcode, $base_table, $entity_id_field, $field_id_field);
|
||||
$sql_column = $table_mapping->getFieldColumnName($field_storage, $column);
|
||||
$property_definitions = $field_storage->getPropertyDefinitions();
|
||||
if (isset($property_definitions[$column])) {
|
||||
$this->caseSensitiveFields[$field] = $property_definitions[$column]->getSetting('case_sensitive');
|
||||
}
|
||||
}
|
||||
// The field is stored in a shared table.
|
||||
else {
|
||||
|
@ -155,6 +166,17 @@ class Tables implements TablesInterface {
|
|||
$entity_tables[$entity_base_table] = $this->getTableMapping($entity_base_table, $entity_type_id);
|
||||
$sql_column = $specifier;
|
||||
$table = $this->ensureEntityTable($index_prefix, $specifier, $type, $langcode, $base_table, $entity_id_field, $entity_tables);
|
||||
|
||||
// If there is a field storage (some specifiers are not, like
|
||||
// default_langcode), check for case sensitivity.
|
||||
if ($field_storage) {
|
||||
$column = $field_storage->getMainPropertyName();
|
||||
$base_field_property_definitions = $field_storage->getPropertyDefinitions();
|
||||
if (isset($base_field_property_definitions[$column])) {
|
||||
$this->caseSensitiveFields[$field] = $base_field_property_definitions[$column]->getSetting('case_sensitive');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// If there are more specifiers to come, it's a relationship.
|
||||
if ($field_storage && $key < $count) {
|
||||
|
@ -186,6 +208,15 @@ class Tables implements TablesInterface {
|
|||
return "$table.$sql_column";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFieldCaseSensitive($field_name) {
|
||||
if (isset($this->caseSensitiveFields[$field_name])) {
|
||||
return $this->caseSensitiveFields[$field_name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join entity table if necessary and return the alias for it.
|
||||
*
|
||||
|
|
|
@ -20,8 +20,8 @@ interface TablesInterface {
|
|||
* then entity property name.
|
||||
* @param string $type
|
||||
* Join type, can either be INNER or LEFT.
|
||||
* @param $langcode
|
||||
* The language code the field values are to be shown in.
|
||||
* @param string $langcode
|
||||
* The language code the field values are to be queried in.
|
||||
*
|
||||
* @throws \Drupal\Core\Entity\Query\QueryException
|
||||
* If $field specifies an invalid relationship.
|
||||
|
@ -33,4 +33,18 @@ interface TablesInterface {
|
|||
*/
|
||||
public function addField($field, $type, $langcode);
|
||||
|
||||
/**
|
||||
* Returns whether the given field is case sensitive.
|
||||
*
|
||||
* This information can only be provided after it was added with addField().
|
||||
*
|
||||
* @param string $field_name
|
||||
* The name of the field.
|
||||
*
|
||||
* @return bool|null
|
||||
* TRUE if the field is case sensitive, FALSE if not. Returns NULL when the
|
||||
* field did not define if it is case sensitive or not.
|
||||
*/
|
||||
public function isFieldCaseSensitive($field_name);
|
||||
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ class StringItem extends StringItemBase {
|
|||
'type' => 'varchar',
|
||||
'length' => (int) $field_definition->getSetting('max_length'),
|
||||
'not null' => FALSE,
|
||||
'binary' => $field_definition->getSetting('case_sensitive'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -17,6 +17,15 @@ use Drupal\Core\TypedData\DataDefinition;
|
|||
*/
|
||||
abstract class StringItemBase extends FieldItemBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function defaultStorageSettings() {
|
||||
return array(
|
||||
'case_sensitive' => FALSE,
|
||||
) + parent::defaultStorageSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -24,7 +33,8 @@ abstract class StringItemBase extends FieldItemBase {
|
|||
// This is called very early by the user entity roles field. Prevent
|
||||
// early t() calls by using the TranslationWrapper.
|
||||
$properties['value'] = DataDefinition::create('string')
|
||||
->setLabel(new TranslationWrapper('Text value'));
|
||||
->setLabel(new TranslationWrapper('Text value'))
|
||||
->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'));
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class StringLongItem extends StringItemBase {
|
|||
return array(
|
||||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'text',
|
||||
'type' => $field_definition->getSetting('case_sensitive') ? 'blob' : 'text',
|
||||
'size' => 'big',
|
||||
),
|
||||
),
|
||||
|
|
|
@ -41,7 +41,8 @@ class UriItem extends StringItem {
|
|||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
$properties['value'] = DataDefinition::create('uri')
|
||||
->setLabel(t('URI value'));
|
||||
->setLabel(t('URI value'))
|
||||
->setSetting('case_sensitive', $field_definition->getSetting('case_sensitive'));
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
@ -56,6 +57,7 @@ class UriItem extends StringItem {
|
|||
'type' => 'varchar',
|
||||
'length' => (int) $field_definition->getSetting('max_length'),
|
||||
'not null' => TRUE,
|
||||
'binary' => $field_definition->getSetting('case_sensitive'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -260,7 +260,8 @@ class File extends ContentEntityBase implements FileInterface {
|
|||
$fields['uri'] = BaseFieldDefinition::create('uri')
|
||||
->setLabel(t('URI'))
|
||||
->setDescription(t('The URI to access the file (either local or remote).'))
|
||||
->setSetting('max_length', 255);
|
||||
->setSetting('max_length', 255)
|
||||
->setSetting('case_sensitive', TRUE);
|
||||
|
||||
$fields['filemime'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('File MIME type'))
|
||||
|
|
|
@ -31,9 +31,6 @@ class FileStorageSchema extends SqlContentEntityStorageSchema {
|
|||
|
||||
case 'uri':
|
||||
$this->addSharedTableFieldUniqueKey($storage_definition, $schema, TRUE);
|
||||
// @todo There should be a 'binary' field type or setting:
|
||||
// https://www.drupal.org/node/2068655.
|
||||
$schema['fields'][$field_name]['binary'] = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace Drupal\file\Tests;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\file\Entity\File;
|
||||
|
||||
/**
|
||||
* File saving tests.
|
||||
|
@ -17,7 +17,7 @@ use Drupal\Core\Language\LanguageInterface;
|
|||
class SaveTest extends FileManagedUnitTestBase {
|
||||
function testFileSave() {
|
||||
// Create a new file entity.
|
||||
$file = entity_create('file', array(
|
||||
$file = File::create(array(
|
||||
'uid' => 1,
|
||||
'filename' => 'druplicon.txt',
|
||||
'uri' => 'public://druplicon.txt',
|
||||
|
@ -59,7 +59,7 @@ class SaveTest extends FileManagedUnitTestBase {
|
|||
|
||||
// Try to insert a second file with the same name apart from case insensitivity
|
||||
// to ensure the 'uri' index allows for filenames with different cases.
|
||||
$file = entity_create('file', array(
|
||||
$uppercase_file = File::create(array(
|
||||
'uid' => 1,
|
||||
'filename' => 'DRUPLICON.txt',
|
||||
'uri' => 'public://DRUPLICON.txt',
|
||||
|
@ -68,7 +68,16 @@ class SaveTest extends FileManagedUnitTestBase {
|
|||
'changed' => 1,
|
||||
'status' => FILE_STATUS_PERMANENT,
|
||||
));
|
||||
file_put_contents($file->getFileUri(), 'hello world');
|
||||
$file->save();
|
||||
file_put_contents($uppercase_file->getFileUri(), 'hello world');
|
||||
$uppercase_file->save();
|
||||
|
||||
// Ensure that file URI entity queries are case sensitive.
|
||||
$fids = \Drupal::entityQuery('file')
|
||||
->condition('uri', $uppercase_file->getFileUri())
|
||||
->execute();
|
||||
|
||||
$this->assertEqual(1, count($fids));
|
||||
$this->assertEqual(array($uppercase_file->id() => $uppercase_file->id()), $fids);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class EntityQueryAggregateTest extends EntityUnitTestBase {
|
|||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->entityStorage = $this->container->get('entity.manager')->getStorage('entity_test');
|
||||
$this->entityStorage = $this->entityManager->getStorage('entity_test');
|
||||
$this->factory = $this->container->get('entity.query');
|
||||
|
||||
// Add some fieldapi fields to be used in the test.
|
||||
|
|
|
@ -9,6 +9,9 @@ namespace Drupal\system\Tests\Entity;
|
|||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
|
@ -490,7 +493,7 @@ class EntityQueryTest extends EntityUnitTestBase {
|
|||
*
|
||||
* The tags and metadata should propagate to the SQL query object.
|
||||
*/
|
||||
function testMetaData() {
|
||||
public function testMetaData() {
|
||||
$query = \Drupal::entityQuery('entity_test_mulrev');
|
||||
$query
|
||||
->addTag('efq_metadata_test')
|
||||
|
@ -500,4 +503,167 @@ class EntityQueryTest extends EntityUnitTestBase {
|
|||
global $efq_test_metadata;
|
||||
$this->assertEqual($efq_test_metadata, 'bar', 'Tag and metadata propagated to the SQL query object.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case sensitive and in-sensitive query conditions.
|
||||
*/
|
||||
public function testCaseSensitivity() {
|
||||
$bundle = $this->randomMachineName();
|
||||
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_ci',
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'type' => 'string',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
'settings' => array(
|
||||
'case_sensitive' => FALSE,
|
||||
)
|
||||
));
|
||||
$field_storage->save();
|
||||
|
||||
FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $bundle,
|
||||
))->save();
|
||||
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_cs',
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'type' => 'string',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
'settings' => array(
|
||||
'case_sensitive' => TRUE,
|
||||
),
|
||||
));
|
||||
$field_storage->save();
|
||||
|
||||
FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $bundle,
|
||||
))->save();
|
||||
|
||||
$fixtures = array();
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$string = $this->randomMachineName();
|
||||
$fixtures[] = array(
|
||||
'original' => $string,
|
||||
'uppercase' => Unicode::strtoupper($string),
|
||||
'lowercase' => Unicode::strtolower($string),
|
||||
);
|
||||
}
|
||||
|
||||
EntityTestMulRev::create(array(
|
||||
'type' => $bundle,
|
||||
'name' => $this->randomMachineName(),
|
||||
'langcode' => 'en',
|
||||
'field_ci' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'],
|
||||
'field_cs' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
|
||||
))->save();
|
||||
|
||||
// Check the case insensitive field, = operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case insensitive, mixed.');
|
||||
|
||||
// Check the case sensitive field, = operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, uppercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
|
||||
|
||||
// Check the case insensitive field, STARTS_WITH operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[0]['lowercase'], 'STARTS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[0]['uppercase'], 'STARTS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
|
||||
|
||||
// Check the case sensitive field, STARTS_WITH operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[0]['lowercase'], 'STARTS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[0]['uppercase'], 'STARTS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
|
||||
|
||||
|
||||
// Check the case insensitive field, ENDS_WITH operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[1]['lowercase'], 'ENDS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[1]['uppercase'], 'ENDS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
|
||||
|
||||
// Check the case sensitive field, ENDS_WITH operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[1]['lowercase'], 'ENDS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[1]['uppercase'], 'ENDS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
|
||||
|
||||
|
||||
// Check the case insensitive field, CONTAINS operator, use the inner 8
|
||||
// characters of the uppercase and lowercase strings.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
|
||||
|
||||
// Check the case sensitive field, CONTAINS operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,17 +81,6 @@ class TermStorage extends SqlContentEntityStorage implements TermStorageInterfac
|
|||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildPropertyQuery(QueryInterface $entity_query, array $values) {
|
||||
if (isset($values['name'])) {
|
||||
$entity_query->condition('name', $values['name'], 'LIKE');
|
||||
unset($values['name']);
|
||||
}
|
||||
parent::buildPropertyQuery($entity_query, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -91,6 +91,7 @@ class TextItem extends TextItemBase {
|
|||
'#min' => 1,
|
||||
'#disabled' => $has_data,
|
||||
);
|
||||
$element += parent::storageSettingsForm($form, $form_state, $has_data);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue