#392494 by yched and bjaspan: Provide a query API for Field API. This is necessary because we can't assume fields will be stored in a database, due to pluggable storage backends.
parent
1f9077ee92
commit
7442dc060f
|
@ -238,6 +238,10 @@ function hook_field_load($obj_type, $objects, $field, $instances, &$items, $age)
|
||||||
* The type of $object.
|
* The type of $object.
|
||||||
* @param $object
|
* @param $object
|
||||||
* The object for the operation.
|
* The object for the operation.
|
||||||
|
* Note that this might not be a full-fledged 'object'. When invoked through
|
||||||
|
* field_attach_query(), the $object will only include properties that the
|
||||||
|
* Field API knows about: bundle, id, revision id, and field values (no node
|
||||||
|
* title, user name...).
|
||||||
* @param $field
|
* @param $field
|
||||||
* The field structure for the operation.
|
* The field structure for the operation.
|
||||||
* @param $instance
|
* @param $instance
|
||||||
|
@ -503,12 +507,17 @@ function hook_field_attach_pre_load($obj_type, $objects, $age, &$skip_fields) {
|
||||||
* This hook is invoked after the field module has performed the operation.
|
* This hook is invoked after the field module has performed the operation.
|
||||||
*
|
*
|
||||||
* Unlike other field_attach hooks, this hook accounts for 'multiple loads'.
|
* Unlike other field_attach hooks, this hook accounts for 'multiple loads'.
|
||||||
* It takes an array of objects indexed by object id as its first parameter.
|
* Instead of the usual $object parameter, it accepts an array of objects,
|
||||||
* For performance reasons, information for all available objects should be
|
* indexed by object id. For performance reasons, information for all available
|
||||||
* loaded in a single query where possible.
|
* objects should be loaded in a single query where possible.
|
||||||
*
|
*
|
||||||
* Note that the changes made to the objects' field values get cached by the
|
* Note that $objects might not be full-fledged 'objects'. When invoked through
|
||||||
* field cache for subsequent loads.
|
* field_attach_query(), each object only includes properties that the Field
|
||||||
|
* API knows about: bundle, id, revision id, and field values (no node title,
|
||||||
|
* user name...)
|
||||||
|
|
||||||
|
* The changes made to the objects' field values get cached by the field cache
|
||||||
|
* for subsequent loads.
|
||||||
*
|
*
|
||||||
* See field_attach_load() for details and arguments.
|
* See field_attach_load() for details and arguments.
|
||||||
*/
|
*/
|
||||||
|
@ -585,6 +594,38 @@ function hook_field_attach_pre_insert($obj_type, $object, &$skip_fields) {
|
||||||
function hook_field_attach_pre_update($obj_type, $object, &$skip_fields) {
|
function hook_field_attach_pre_update($obj_type, $object, &$skip_fields) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Act on field_attach_pre_query.
|
||||||
|
*
|
||||||
|
* This hook should be implemented by modules that use
|
||||||
|
* hook_field_attach_pre_load(), hook_field_attach_pre_insert() and
|
||||||
|
* hook_field_attach_pre_update() to bypass the regular storage engine, to
|
||||||
|
* handle field queries.
|
||||||
|
*
|
||||||
|
* @param $field_name
|
||||||
|
* The name of the field to query.
|
||||||
|
* @param $conditions
|
||||||
|
* See field_attach_query().
|
||||||
|
* A storage module that doesn't support querying a given column should raise
|
||||||
|
* a FieldQueryException. Incompatibilities should be mentioned on the module
|
||||||
|
* project page.
|
||||||
|
* @param $result_format
|
||||||
|
* See field_attach_query().
|
||||||
|
* @param $age
|
||||||
|
* - FIELD_LOAD_CURRENT: query the most recent revisions for all
|
||||||
|
* objects. The results will be keyed by object type and object id.
|
||||||
|
* - FIELD_LOAD_REVISION: query all revisions. The results will be keyed by
|
||||||
|
* object type and object revision id.
|
||||||
|
* @param $skip_field
|
||||||
|
* Boolean, always coming as FALSE.
|
||||||
|
* @return
|
||||||
|
* See field_attach_query().
|
||||||
|
* The $skip_field parameter should be set to TRUE if the query has been
|
||||||
|
* handled.
|
||||||
|
*/
|
||||||
|
function hook_field_attach_pre_query($field_name, $conditions, $result_format, $age, &$skip_field) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Act on field_attach_delete.
|
* Act on field_attach_delete.
|
||||||
*
|
*
|
||||||
|
@ -736,6 +777,26 @@ function hook_field_storage_delete($obj_type, $object) {
|
||||||
function hook_field_storage_delete_revision($obj_type, $object) {
|
function hook_field_storage_delete_revision($obj_type, $object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a field query.
|
||||||
|
*
|
||||||
|
* @param $field_name
|
||||||
|
* The name of the field to query.
|
||||||
|
* @param $conditions
|
||||||
|
* See field_attach_query().
|
||||||
|
* A storage module that doesn't support querying a given column should raise
|
||||||
|
* a FieldQueryException. Incompatibilities should be mentioned on the module
|
||||||
|
* project page.
|
||||||
|
* @param $result_format
|
||||||
|
* See field_attach_query().
|
||||||
|
* @param $age
|
||||||
|
* See field_attach_query().
|
||||||
|
* @return
|
||||||
|
* See field_attach_query().
|
||||||
|
*/
|
||||||
|
function hook_field_storage_query($field_name, $conditions, $result_format, $age) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Act on creation of a new bundle.
|
* Act on creation of a new bundle.
|
||||||
*
|
*
|
||||||
|
|
|
@ -13,8 +13,7 @@
|
||||||
// Should all iteration through available fields be done here instead of in Field?
|
// Should all iteration through available fields be done here instead of in Field?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception class thrown by field_attach_validate() when field
|
* Exception thrown by field_attach_validate() on field validation errors.
|
||||||
* validation errors occur.
|
|
||||||
*/
|
*/
|
||||||
class FieldValidationException extends FieldException {
|
class FieldValidationException extends FieldException {
|
||||||
var $errors;
|
var $errors;
|
||||||
|
@ -37,6 +36,15 @@ class FieldValidationException extends FieldException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown by field_attach_query() on unsupported query syntax.
|
||||||
|
*
|
||||||
|
* Some storage modules might not support the full range of the syntax for
|
||||||
|
* conditions, and will raise a FieldQueryException when an usupported
|
||||||
|
* condition was specified.
|
||||||
|
*/
|
||||||
|
class FieldQueryException extends FieldException {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @defgroup field_storage Field Storage API
|
* @defgroup field_storage Field Storage API
|
||||||
* @{
|
* @{
|
||||||
|
@ -146,47 +154,77 @@ define('FIELD_STORAGE_INSERT', 'insert');
|
||||||
* @param $b
|
* @param $b
|
||||||
* - The $form_state in the 'submit' operation.
|
* - The $form_state in the 'submit' operation.
|
||||||
* - Otherwise NULL.
|
* - Otherwise NULL.
|
||||||
*
|
* @param $options
|
||||||
* @param $default
|
* An associative array of additional options, with the following keys:
|
||||||
* - TRUE: use the default field implementation of the field hook.
|
* - 'field_name'
|
||||||
* - FALSE: use the field module's implementation of the field hook.
|
* The name of the field whose operation should be invoked. By default, the
|
||||||
|
* operation is invoked on all the fields in the object's bundle.
|
||||||
|
* - 'default'
|
||||||
|
* A boolean value, specifying which implementation of the operation should
|
||||||
|
* be invoked.
|
||||||
|
* - if FALSE (default), the field types implementation of the operation
|
||||||
|
* will be invoked (hook_field_[op])
|
||||||
|
* - If TRUE, the default field implementation of the field operation
|
||||||
|
* will be invoked (field_default_[op])
|
||||||
|
* Internal use only. Do not explicitely set to TRUE, but use
|
||||||
|
* _field_invoke_default() instead.
|
||||||
*/
|
*/
|
||||||
function _field_invoke($op, $obj_type, $object, &$a = NULL, &$b = NULL, $default = FALSE) {
|
function _field_invoke($op, $obj_type, $object, &$a = NULL, &$b = NULL, $options = array()) {
|
||||||
list(, , $bundle) = field_attach_extract_ids($obj_type, $object);
|
// Merge default options.
|
||||||
$instances = field_info_instances($bundle);
|
$default_options = array(
|
||||||
|
'default' => FALSE,
|
||||||
|
);
|
||||||
|
$options += $default_options;
|
||||||
|
|
||||||
|
// Iterate through the object's field instances.
|
||||||
$return = array();
|
$return = array();
|
||||||
foreach ($instances as $instance) {
|
list(, , $bundle) = field_attach_extract_ids($obj_type, $object);
|
||||||
|
foreach (field_info_instances($bundle) as $instance) {
|
||||||
$field_name = $instance['field_name'];
|
$field_name = $instance['field_name'];
|
||||||
|
|
||||||
|
// When in 'single field' mode, only act on the specified field.
|
||||||
|
if (empty($options['field_name']) || $options['field_name'] == $field_name) {
|
||||||
$field = field_info_field($field_name);
|
$field = field_info_field($field_name);
|
||||||
|
|
||||||
|
// Extract the field values into a separate variable, easily accessed by
|
||||||
|
// hook implementations.
|
||||||
$items = isset($object->$field_name) ? $object->$field_name : array();
|
$items = isset($object->$field_name) ? $object->$field_name : array();
|
||||||
|
|
||||||
$function = $default ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
|
// Invoke the field hook and collect results.
|
||||||
|
$function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
|
||||||
if (drupal_function_exists($function)) {
|
if (drupal_function_exists($function)) {
|
||||||
$result = $function($obj_type, $object, $field, $instance, $items, $a, $b);
|
$result = $function($obj_type, $object, $field, $instance, $items, $a, $b);
|
||||||
|
if (isset($result)) {
|
||||||
|
// For hooks with array results, we merge results together.
|
||||||
|
// For hooks with scalar results, we collect results in an array.
|
||||||
if (is_array($result)) {
|
if (is_array($result)) {
|
||||||
$return = array_merge($return, $result);
|
$return = array_merge($return, $result);
|
||||||
}
|
}
|
||||||
elseif (isset($result)) {
|
else {
|
||||||
$return[] = $result;
|
$return[] = $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Populate field values back in the object, but avoid replacing missing
|
// Populate field values back in the object, but avoid replacing missing
|
||||||
// fields with an empty array (those are not equivalent on update).
|
// fields with an empty array (those are not equivalent on update).
|
||||||
if ($items !== array() || property_exists($object, $field_name)) {
|
if ($items !== array() || property_exists($object, $field_name)) {
|
||||||
$object->$field_name = $items;
|
$object->$field_name = $items;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke a field operation across fields on multiple objects.
|
* Invoke a field hook across fields on multiple objects.
|
||||||
*
|
*
|
||||||
* @param $op
|
* @param $op
|
||||||
* Possible operations include:
|
* Possible operations include:
|
||||||
* - load
|
* - load
|
||||||
|
* For all other operations, use _field_invoke() / field_invoke_default()
|
||||||
|
* instead.
|
||||||
* @param $obj_type
|
* @param $obj_type
|
||||||
* The type of $object; e.g. 'node' or 'user'.
|
* The type of $object; e.g. 'node' or 'user'.
|
||||||
* @param $objects
|
* @param $objects
|
||||||
|
@ -196,46 +234,69 @@ function _field_invoke($op, $obj_type, $object, &$a = NULL, &$b = NULL, $default
|
||||||
* - Otherwise NULL.
|
* - Otherwise NULL.
|
||||||
* @param $b
|
* @param $b
|
||||||
* Currently always NULL.
|
* Currently always NULL.
|
||||||
* @param $default
|
* @param $options
|
||||||
* - TRUE: use the default field implementation of the field hook.
|
* An associative array of additional options, with the following keys:
|
||||||
* - FALSE: use the field module's implementation of the field hook.
|
* - 'field_name'
|
||||||
|
* The name of the field whose operation should be invoked. By default, the
|
||||||
|
* operation is invoked on all the fields in the objects' bundles.
|
||||||
|
* - 'default'
|
||||||
|
* A boolean value, specifying which implementation of the operation should
|
||||||
|
* be invoked.
|
||||||
|
* - if FALSE (default), the field types implementation of the operation
|
||||||
|
* will be invoked (hook_field_[op])
|
||||||
|
* - If TRUE, the default field implementation of the field operation
|
||||||
|
* will be invoked (field_default_[op])
|
||||||
|
* Internal use only. Do not explicitely set to TRUE, but use
|
||||||
|
* _field_invoke_multiple_default() instead.
|
||||||
* @return
|
* @return
|
||||||
* An array of returned values keyed by object id.
|
* An array of returned values keyed by object id.
|
||||||
*/
|
*/
|
||||||
function _field_invoke_multiple($op, $obj_type, $objects, &$a = NULL, &$b = NULL, $default = FALSE) {
|
function _field_invoke_multiple($op, $obj_type, $objects, &$a = NULL, &$b = NULL, $options = array()) {
|
||||||
|
// Merge default options.
|
||||||
|
$default_options = array(
|
||||||
|
'default' => FALSE,
|
||||||
|
);
|
||||||
|
$options += $default_options;
|
||||||
|
|
||||||
$fields = array();
|
$fields = array();
|
||||||
$grouped_instances = array();
|
$grouped_instances = array();
|
||||||
$grouped_objects = array();
|
$grouped_objects = array();
|
||||||
$grouped_items = array();
|
$grouped_items = array();
|
||||||
$return = array();
|
$return = array();
|
||||||
|
|
||||||
// Preparation:
|
// Go through the objects and collect the fields on which the hook should be
|
||||||
// - Get the list of fields contained in the various bundles.
|
// invoked.
|
||||||
// - For each field, group the corresponding instances, objects and field
|
|
||||||
// values.
|
|
||||||
// - Initialize the return value for each object.
|
|
||||||
foreach ($objects as $object) {
|
foreach ($objects as $object) {
|
||||||
list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);
|
list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);
|
||||||
foreach (field_info_instances($bundle) as $instance) {
|
foreach (field_info_instances($bundle) as $instance) {
|
||||||
$field_name = $instance['field_name'];
|
$field_name = $instance['field_name'];
|
||||||
if (!isset($grouped_fields[$field_name])) {
|
// When in 'single field' mode, only act on the specified field.
|
||||||
|
if (empty($options['field_name']) || $options['field_name'] == $field_name) {
|
||||||
|
// Add the field to the list of fields to invoke the hook on.
|
||||||
|
if (!isset($fields[$field_name])) {
|
||||||
$fields[$field_name] = field_info_field($field_name);
|
$fields[$field_name] = field_info_field($field_name);
|
||||||
}
|
}
|
||||||
|
// Group the corresponding instances and objects.
|
||||||
$grouped_instances[$field_name][$id] = $instance;
|
$grouped_instances[$field_name][$id] = $instance;
|
||||||
$grouped_objects[$field_name][$id] = $objects[$id];
|
$grouped_objects[$field_name][$id] = $objects[$id];
|
||||||
|
// Extract the field values into a separate variable, easily accessed
|
||||||
|
// by hook implementations.
|
||||||
$grouped_items[$field_name][$id] = isset($object->$field_name) ? $object->$field_name : array();
|
$grouped_items[$field_name][$id] = isset($object->$field_name) ? $object->$field_name : array();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// Initialize the return value for each object.
|
||||||
$return[$id] = array();
|
$return[$id] = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call each field's operation.
|
// For each field, invoke the field hook and collect results.
|
||||||
foreach ($fields as $field_name => $field) {
|
foreach ($fields as $field_name => $field) {
|
||||||
if (!empty($field)) {
|
$function = $options['default'] ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
|
||||||
$function = $default ? 'field_default_' . $op : $field['module'] . '_field_' . $op;
|
|
||||||
if (drupal_function_exists($function)) {
|
if (drupal_function_exists($function)) {
|
||||||
$results = $function($obj_type, $grouped_objects[$field_name], $field, $grouped_instances[$field_name], $grouped_items[$field_name], $a, $b);
|
$results = $function($obj_type, $grouped_objects[$field_name], $field, $grouped_instances[$field_name], $grouped_items[$field_name], $a, $b);
|
||||||
// Merge results by object.
|
|
||||||
if (isset($results)) {
|
if (isset($results)) {
|
||||||
|
// Collect results by object.
|
||||||
|
// For hooks with array results, we merge results together.
|
||||||
|
// For hooks with scalar results, we collect results in an array.
|
||||||
foreach ($results as $id => $result) {
|
foreach ($results as $id => $result) {
|
||||||
if (is_array($result)) {
|
if (is_array($result)) {
|
||||||
$return[$id] = array_merge($return[$id], $result);
|
$return[$id] = array_merge($return[$id], $result);
|
||||||
|
@ -246,7 +307,6 @@ function _field_invoke_multiple($op, $obj_type, $objects, &$a = NULL, &$b = NULL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Populate field values back in the objects, but avoid replacing missing
|
// Populate field values back in the objects, but avoid replacing missing
|
||||||
// fields with an empty array (those are not equivalent on update).
|
// fields with an empty array (those are not equivalent on update).
|
||||||
|
@ -262,16 +322,30 @@ function _field_invoke_multiple($op, $obj_type, $objects, &$a = NULL, &$b = NULL
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke field.module's version of a field hook.
|
* Invoke field.module's version of a field hook.
|
||||||
|
*
|
||||||
|
* This function invokes the field_default_[op]() function.
|
||||||
|
* Use _field_invoke() to invoke the field type implementation,
|
||||||
|
* hook_field_[op]().
|
||||||
|
*
|
||||||
|
* @see _field_invoke().
|
||||||
*/
|
*/
|
||||||
function _field_invoke_default($op, $obj_type, $object, &$a = NULL, &$b = NULL) {
|
function _field_invoke_default($op, $obj_type, $object, &$a = NULL, &$b = NULL, $options = array()) {
|
||||||
return _field_invoke($op, $obj_type, $object, $a, $b, TRUE);
|
$options['default'] = TRUE;
|
||||||
|
return _field_invoke($op, $obj_type, $object, $a, $b, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke field.module's version of a field hook on multiple objects.
|
* Invoke field.module's version of a field hook on multiple objects.
|
||||||
|
*
|
||||||
|
* This function invokes the field_default_[op]() function.
|
||||||
|
* Use _field_invoke_multiple() to invoke the field type implementation,
|
||||||
|
* hook_field_[op]().
|
||||||
|
*
|
||||||
|
* @see _field_invoke_multiple().
|
||||||
*/
|
*/
|
||||||
function _field_invoke_multiple_default($op, $obj_type, $objects, &$a = NULL, &$b = NULL) {
|
function _field_invoke_multiple_default($op, $obj_type, $objects, &$a = NULL, &$b = NULL, $options = array()) {
|
||||||
return _field_invoke_multiple($op, $obj_type, $objects, $a, $b, TRUE);
|
$options['default'] = TRUE;
|
||||||
|
return _field_invoke_multiple($op, $obj_type, $objects, $a, $b, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -651,6 +725,154 @@ function field_attach_delete_revision($obj_type, $object) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve objects matching a given set of conditions.
|
||||||
|
*
|
||||||
|
* Note that the query 'conditions' only apply to the stored values.
|
||||||
|
* In a regular field_attach_load() call, field values additionally go through
|
||||||
|
* hook_field_load() and hook_field_attach_load() invocations, which can add
|
||||||
|
* to or affect the raw stored values. The results of field_attach_query()
|
||||||
|
* might therefore differ from what could be expected by looking at a regular,
|
||||||
|
* fully loaded object.
|
||||||
|
*
|
||||||
|
* @param $field_name
|
||||||
|
* The name of the field to query.
|
||||||
|
* @param $conditions
|
||||||
|
* An array of query conditions. Each condition is a numerically indexed
|
||||||
|
* array, in the form: array(column, value, operator).
|
||||||
|
* Not all storage engines are required to support queries on all column, or
|
||||||
|
* with all operators below. A FieldQueryException will be raised if an
|
||||||
|
* unsupported condition is specified.
|
||||||
|
* Supported columns:
|
||||||
|
* - any of the columns for $field_name's field type: condition on field
|
||||||
|
* value,
|
||||||
|
* - 'type': condition on object type (e.g. 'node', 'user'...),
|
||||||
|
* - 'bundle': condition on object bundle (e.g. node type),
|
||||||
|
* - 'entity_id': condition on object id (e.g node nid, user uid...),
|
||||||
|
* The field_attach_query_revisions() function additionally supports:
|
||||||
|
* - 'revision_id': condition on object revision id (e.g node vid).
|
||||||
|
* Supported operators:
|
||||||
|
* - '=', '!=', '>', '>=', '<', '<=', 'STARTS_WITH', 'ENDS_WITH',
|
||||||
|
* 'CONTAINS': these operators expect the value as a literal of the same
|
||||||
|
* type as the column,
|
||||||
|
* - 'IN': this operator expects the value as an array of literals of the
|
||||||
|
* same type as the column.
|
||||||
|
* - 'BETWEEN': this operator expects the value as an array of two literals
|
||||||
|
* of the same type as the column.
|
||||||
|
* The operator can be ommitted, and will default to 'IN' if the value is
|
||||||
|
* an array, or to '=' otherwise.
|
||||||
|
* Example values for $conditions:
|
||||||
|
* @code
|
||||||
|
* array(
|
||||||
|
* array('type', 'node'),
|
||||||
|
* );
|
||||||
|
* array(
|
||||||
|
* array('bundle', array('article', 'page')),
|
||||||
|
* array('value', 12, '>'),
|
||||||
|
* );
|
||||||
|
* @endcode
|
||||||
|
* @param $result_format
|
||||||
|
* - FIELD_QUERY_RETURN_IDS (default): return the ids of the objects matching the
|
||||||
|
* conditions.
|
||||||
|
* - FIELD_QUERY_RETURN_VALUES: return the values for the field.
|
||||||
|
* @param $age
|
||||||
|
* Internal use only. Use field_attach_query_revisions() instead of passing
|
||||||
|
* FIELD_LOAD_REVISION.
|
||||||
|
* - FIELD_LOAD_CURRENT (default): query the most recent revisions for all
|
||||||
|
* objects. The results will be keyed by object type and object id.
|
||||||
|
* - FIELD_LOAD_REVISION: query all revisions. The results will be keyed by
|
||||||
|
* object type and object revision id.
|
||||||
|
* @return
|
||||||
|
* An array keyed by object type (e.g. 'node', 'user'...), then by object id
|
||||||
|
* or revision id (depending of the value of the $age parameter), and whose
|
||||||
|
* values depend on the $result_format parameter:
|
||||||
|
* - FIELD_QUERY_RETURN_IDS: the object id.
|
||||||
|
* - FIELD_QUERY_RETURN_VALUES: a pseudo-object with values for the
|
||||||
|
* $field_name field. This only includes values matching the conditions,
|
||||||
|
* and thus might not contain all actual values and actual delta sequence
|
||||||
|
* (although values oprder is preserved).
|
||||||
|
* The pseudo-objects only include properties that the Field API knows
|
||||||
|
* about: bundle, id, revision id, and field values (no node title, user
|
||||||
|
* name...).
|
||||||
|
* Throws a FieldQueryException if the field's storage doesn't support the
|
||||||
|
* specified conditions.
|
||||||
|
*/
|
||||||
|
function field_attach_query($field_name, $conditions, $result_format = FIELD_QUERY_RETURN_IDS, $age = FIELD_LOAD_CURRENT) {
|
||||||
|
// Give a chance to 3rd party modules that bypass the storage engine to
|
||||||
|
// handle the query.
|
||||||
|
$skip_field = FALSE;
|
||||||
|
foreach (module_implements('field_attach_pre_query') as $module) {
|
||||||
|
$function = $module . '_field_attach_pre_query';
|
||||||
|
$results = $function($field_name, $conditions, $result_format, $age, $skip_field);
|
||||||
|
// Stop as soon as a module claims it handled the query.
|
||||||
|
if ($skip_field) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the request hasn't been handled, let the storage engine handle it.
|
||||||
|
if (!$skip_field) {
|
||||||
|
$results = module_invoke(variable_get('field_storage_module', 'field_sql_storage'), 'field_storage_query', $field_name, $conditions, $result_format, $age);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($result_format == FIELD_QUERY_RETURN_VALUES) {
|
||||||
|
foreach ($results as $obj_type => $pseudo_objects) {
|
||||||
|
if ($age == FIELD_LOAD_CURRENT) {
|
||||||
|
// Invoke hook_field_load().
|
||||||
|
$b = NULL;
|
||||||
|
_field_invoke_multiple('load', $obj_type, $pseudo_objects, $age, $b, array('field_name' => $field_name));
|
||||||
|
|
||||||
|
// Invoke hook_field_attach_load().
|
||||||
|
foreach (module_implements('field_attach_load') as $module) {
|
||||||
|
$function = $module . '_field_attach_load';
|
||||||
|
$function($obj_type, $pseudo_objects, $age);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// The 'multiple' hooks expect an array of objects keyed by object id,
|
||||||
|
// and thus cannot be used directly when querying revisions. The hooks
|
||||||
|
// are therefore called on each object separately, which might cause
|
||||||
|
// performance issues when large numbers of revisions are retrieved.
|
||||||
|
foreach ($pseudo_objects as $vid => $pseudo_object) {
|
||||||
|
list($id) = _field_attach_extract_ids($obj_type, $pseudo_object);
|
||||||
|
$objects = array($id => $pseudo_object);
|
||||||
|
|
||||||
|
// Invoke hook_field_load().
|
||||||
|
$b = NULL;
|
||||||
|
_field_invoke_multiple('load', $obj_type, $objects, $age, $b, array('field_name' => $field_name));
|
||||||
|
|
||||||
|
// Invoke hook_field_attach_load().
|
||||||
|
foreach (module_implements('field_attach_load') as $module) {
|
||||||
|
$function = $module . '_field_attach_load';
|
||||||
|
$function($obj_type, $objects, $age);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve object revisions matching a given set of conditions.
|
||||||
|
*
|
||||||
|
* See field_attach_query() for more informations.
|
||||||
|
*
|
||||||
|
* @param $field_name
|
||||||
|
* The name of the field to query.
|
||||||
|
* @param $conditions
|
||||||
|
* See field_attach_query().
|
||||||
|
* @param $result_format
|
||||||
|
* See field_attach_query().
|
||||||
|
* Note that the FIELD_QUERY_RETURN_VALUES option might cause performance
|
||||||
|
* issues with field_attach_query_revisions().
|
||||||
|
* @return
|
||||||
|
* See field_attach_query().
|
||||||
|
*/
|
||||||
|
function field_attach_query_revisions($field_name, $conditions, $result_format = FIELD_QUERY_RETURN_IDS) {
|
||||||
|
return field_attach_query($field_name, $conditions, $result_format, FIELD_LOAD_REVISION);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate and return a structured content array tree suitable for
|
* Generate and return a structured content array tree suitable for
|
||||||
* drupal_render() for all of the fields on an object. The format of
|
* drupal_render() for all of the fields on an object. The format of
|
||||||
|
@ -811,6 +1033,35 @@ function field_attach_extract_ids($object_type, $object) {
|
||||||
return array($id, $vid, $bundle, $cacheable);
|
return array($id, $vid, $bundle, $cacheable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to assemble an object structure with initial ids.
|
||||||
|
*
|
||||||
|
* This function can be seen as reciprocal to field_attach_extract_ids().
|
||||||
|
*
|
||||||
|
* @param $obj_type
|
||||||
|
* The type of $object; e.g. 'node' or 'user'.
|
||||||
|
* @param $ids
|
||||||
|
* A numerically indexed array, as returned by field_attach_extract_ids(),
|
||||||
|
* containing these elements:
|
||||||
|
* 0: primary id of the object
|
||||||
|
* 1: revision id of the object, or NULL if $obj_type is not versioned
|
||||||
|
* 2: bundle name of the object
|
||||||
|
* @return
|
||||||
|
* An $object structure, initialized with the ids provided.
|
||||||
|
*/
|
||||||
|
function field_attach_create_stub_object($obj_type, $ids) {
|
||||||
|
$object = new stdClass();
|
||||||
|
$info = field_info_fieldable_types($obj_type);
|
||||||
|
$object->{$info['id key']} = $ids[0];
|
||||||
|
if (isset($info['revision key']) && !is_null($ids[1])) {
|
||||||
|
$object->{$info['revision key']} = $ids[1];
|
||||||
|
}
|
||||||
|
if ($info['bundle key']) {
|
||||||
|
$object->{$info['bundle key']} = $ids[2];
|
||||||
|
}
|
||||||
|
return $object;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @} End of "defgroup field_attach"
|
* @} End of "defgroup field_attach"
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -87,6 +87,25 @@ define('FIELD_LOAD_CURRENT', 'FIELD_LOAD_CURRENT');
|
||||||
*/
|
*/
|
||||||
define('FIELD_LOAD_REVISION', 'FIELD_LOAD_REVISION');
|
define('FIELD_LOAD_REVISION', 'FIELD_LOAD_REVISION');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Field query flags
|
||||||
|
* @{
|
||||||
|
* Flags for use in the $result_format parameter in field_attach_query().
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result format argument for field_attach_query().
|
||||||
|
*/
|
||||||
|
define('FIELD_QUERY_RETURN_VALUES', 'FIELD_QUERY_RETURN_VALUES');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result format argument for field_attach_query().
|
||||||
|
*/
|
||||||
|
define('FIELD_QUERY_RETURN_IDS', 'FIELD_QUERY_RETURN_IDS');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @} End of "Field query flags".
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all exceptions thrown by Field API functions.
|
* Base class for all exceptions thrown by Field API functions.
|
||||||
|
|
|
@ -261,6 +261,216 @@ class FieldAttachTestCase extends DrupalWebTestCase {
|
||||||
$this->assertEqual($entity->{$this->field_name}, $values, t('Insert: missing field results in default value saved'));
|
$this->assertEqual($entity->{$this->field_name}, $values, t('Insert: missing field results in default value saved'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test field_attach_query().
|
||||||
|
*/
|
||||||
|
function testFieldAttachQuery() {
|
||||||
|
$cardinality = $this->field['cardinality'];
|
||||||
|
|
||||||
|
// Create an additional bundle with an instance of the field.
|
||||||
|
field_test_create_bundle('test_bundle_1', 'Test Bundle 1');
|
||||||
|
$this->instance2 = $this->instance;
|
||||||
|
$this->instance2['bundle'] = 'test_bundle_1';
|
||||||
|
field_create_instance($this->instance2);
|
||||||
|
|
||||||
|
// Create two test objects, using two different types and bundles.
|
||||||
|
$entity_types = array(1 => 'test_entity', 2 => 'test_cacheable_entity');
|
||||||
|
$entities = array(1 => field_test_create_stub_entity(1, 1, 'test_bundle'), 2 => field_test_create_stub_entity(2, 2, 'test_bundle_1'));
|
||||||
|
|
||||||
|
// Create first test object with random (distinct) values.
|
||||||
|
$values = array();
|
||||||
|
for ($delta = 0; $delta < $cardinality; $delta++) {
|
||||||
|
do {
|
||||||
|
$value = mt_rand(1, 127);
|
||||||
|
} while (in_array($value, $values));
|
||||||
|
$values[$delta] = $value;
|
||||||
|
$entities[1]->{$this->field_name}[$delta] = array('value' => $values[$delta]);
|
||||||
|
}
|
||||||
|
field_attach_insert($entity_types[1], $entities[1]);
|
||||||
|
|
||||||
|
// Create second test object, sharing a value with the first one.
|
||||||
|
$common_value = $values[$cardinality - 1];
|
||||||
|
$entities[2]->{$this->field_name} = array(array('value' => $common_value));
|
||||||
|
field_attach_insert($entity_types[2], $entities[2]);
|
||||||
|
|
||||||
|
// Query on the object's values.
|
||||||
|
for ($delta = 0; $delta < $cardinality; $delta++) {
|
||||||
|
$conditions = array(array('value', $values[$delta]));
|
||||||
|
$result = field_attach_query($this->field_name, $conditions);
|
||||||
|
$this->assertTrue(isset($result[$entity_types[1]][1]), t('Query on value %delta returns the object', array('%delta' => $delta)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query on a value that is not in the object.
|
||||||
|
do {
|
||||||
|
$different_value = mt_rand(1, 127);
|
||||||
|
} while (in_array($different_value, $values));
|
||||||
|
$conditions = array(array('value', $different_value));
|
||||||
|
$result = field_attach_query($this->field_name, $conditions);
|
||||||
|
$this->assertFalse(isset($result[$entity_types[1]][1]), t("Query on a value that is not in the object doesn't return the object"));
|
||||||
|
|
||||||
|
// Query on the value shared by both objects, and discriminate using
|
||||||
|
// additional conditions.
|
||||||
|
|
||||||
|
$conditions = array(array('value', $common_value));
|
||||||
|
$result = field_attach_query($this->field_name, $conditions);
|
||||||
|
$this->assertTrue(isset($result[$entity_types[1]][1]) && isset($result[$entity_types[2]][2]), t('Query on a value common to both objects returns both objects'));
|
||||||
|
|
||||||
|
$conditions = array(array('type', $entity_types[1]), array('value', $common_value));
|
||||||
|
$result = field_attach_query($this->field_name, $conditions);
|
||||||
|
$this->assertTrue(isset($result[$entity_types[1]][1]) && !isset($result[$entity_types[2]][2]), t("Query on a value common to both objects and a 'type' condition only returns the relevant object"));
|
||||||
|
|
||||||
|
$conditions = array(array('bundle', $entities[1]->fttype), array('value', $common_value));
|
||||||
|
$result = field_attach_query($this->field_name, $conditions);
|
||||||
|
$this->assertTrue(isset($result[$entity_types[1]][1]) && !isset($result[$entity_types[2]][2]), t("Query on a value common to both objects and a 'bundle' condition only returns the relevant object"));
|
||||||
|
|
||||||
|
$conditions = array(array('entity_id', $entities[1]->ftid), array('value', $common_value));
|
||||||
|
$result = field_attach_query($this->field_name, $conditions);
|
||||||
|
$this->assertTrue(isset($result[$entity_types[1]][1]) && !isset($result[$entity_types[2]][2]), t("Query on a value common to both objects and an 'entity_id' condition only returns the relevant object"));
|
||||||
|
|
||||||
|
// Test FIELD_QUERY_RETURN_IDS result format.
|
||||||
|
$conditions = array(array('value', $values[0]));
|
||||||
|
$result = field_attach_query($this->field_name, $conditions);
|
||||||
|
$expected = array(
|
||||||
|
$entity_types[1] => array(
|
||||||
|
$entities[1]->ftid => $entities[1]->ftid,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$this->assertEqual($result, $expected, t('FIELD_QUERY_RETURN_IDS result format returns the expect result'));
|
||||||
|
|
||||||
|
// Test FIELD_QUERY_RETURN_VALUES result format.
|
||||||
|
// Configure the instances so that we test hook_field_load() (see
|
||||||
|
// field_test_field_load() in field_test.module).
|
||||||
|
$this->instance['settings']['test_hook_field_load'] = TRUE;
|
||||||
|
field_update_instance($this->instance);
|
||||||
|
$this->instance2['settings']['test_hook_field_load'] = TRUE;
|
||||||
|
field_update_instance($this->instance2);
|
||||||
|
|
||||||
|
// Query for one of the values in the 1st object and the value shared by
|
||||||
|
// both objects.
|
||||||
|
$conditions = array(array('value', array($values[0], $common_value)));
|
||||||
|
$result = field_attach_query($this->field_name, $conditions, FIELD_QUERY_RETURN_VALUES);
|
||||||
|
$expected = array(
|
||||||
|
$entity_types[1] => array(
|
||||||
|
$entities[1]->ftid => (object) array(
|
||||||
|
'ftid' => $entities[1]->ftid,
|
||||||
|
'ftvid' => $entities[1]->ftvid,
|
||||||
|
'fttype' => $entities[1]->fttype,
|
||||||
|
$this->field_name => array(
|
||||||
|
array('value' => $values[0], 'additional_key' => 'additional_value'),
|
||||||
|
array('value' => $common_value, 'additional_key' => 'additional_value'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
$entity_types[2] => array(
|
||||||
|
$entities[2]->ftid => (object) array(
|
||||||
|
'ftid' => $entities[2]->ftid,
|
||||||
|
'ftvid' => $entities[2]->ftvid,
|
||||||
|
'fttype' => $entities[2]->fttype,
|
||||||
|
$this->field_name => array(
|
||||||
|
array('value' => $common_value, 'additional_key' => 'additional_value'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$this->assertEqual($result, $expected, t('FIELD_QUERY_RETURN_VALUES result format returns the expect result'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test field_attach_query_revisions().
|
||||||
|
*/
|
||||||
|
function testFieldAttachQueryRevisions() {
|
||||||
|
$cardinality = $this->field['cardinality'];
|
||||||
|
|
||||||
|
// Create first object revision with random (distinct) values.
|
||||||
|
$entity_type = 'test_entity';
|
||||||
|
$entities = array(1 => field_test_create_stub_entity(1, 1), 2 => field_test_create_stub_entity(1, 2));
|
||||||
|
$values = array();
|
||||||
|
for ($delta = 0; $delta < $cardinality; $delta++) {
|
||||||
|
do {
|
||||||
|
$value = mt_rand(1, 127);
|
||||||
|
} while (in_array($value, $values));
|
||||||
|
$values[$delta] = $value;
|
||||||
|
$entities[1]->{$this->field_name}[$delta] = array('value' => $values[$delta]);
|
||||||
|
}
|
||||||
|
field_attach_insert($entity_type, $entities[1]);
|
||||||
|
|
||||||
|
// Create second object revision, sharing a value with the first one.
|
||||||
|
$common_value = $values[$cardinality - 1];
|
||||||
|
$entities[2]->{$this->field_name}[0] = array('value' => $common_value);
|
||||||
|
field_attach_update($entity_type, $entities[2]);
|
||||||
|
|
||||||
|
// Query on the object's values.
|
||||||
|
for ($delta = 0; $delta < $cardinality; $delta++) {
|
||||||
|
$conditions = array(array('value', $values[$delta]));
|
||||||
|
$result = field_attach_query_revisions($this->field_name, $conditions);
|
||||||
|
$this->assertTrue(isset($result[$entity_type][1]), t('Query on value %delta returns the object', array('%delta' => $delta)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query on a value that is not in the object.
|
||||||
|
do {
|
||||||
|
$different_value = mt_rand(1, 127);
|
||||||
|
} while (in_array($different_value, $values));
|
||||||
|
$conditions = array(array('value', $different_value));
|
||||||
|
$result = field_attach_query_revisions($this->field_name, $conditions);
|
||||||
|
$this->assertFalse(isset($result[$entity_type][1]), t("Query on a value that is not in the object doesn't return the object"));
|
||||||
|
|
||||||
|
// Query on the value shared by both objects, and discriminate using
|
||||||
|
// additional conditions.
|
||||||
|
|
||||||
|
$conditions = array(array('value', $common_value));
|
||||||
|
$result = field_attach_query_revisions($this->field_name, $conditions);
|
||||||
|
$this->assertTrue(isset($result[$entity_type][1]) && isset($result[$entity_type][2]), t('Query on a value common to both objects returns both objects'));
|
||||||
|
|
||||||
|
$conditions = array(array('revision_id', $entities[1]->ftvid), array('value', $common_value));
|
||||||
|
$result = field_attach_query_revisions($this->field_name, $conditions);
|
||||||
|
$this->assertTrue(isset($result[$entity_type][1]) && !isset($result[$entity_type][2]), t("Query on a value common to both objects and a 'revision_id' condition only returns the relevant object"));
|
||||||
|
|
||||||
|
// Test FIELD_QUERY_RETURN_IDS result format.
|
||||||
|
$conditions = array(array('value', $values[0]));
|
||||||
|
$result = field_attach_query_revisions($this->field_name, $conditions);
|
||||||
|
$expected = array(
|
||||||
|
$entity_type => array(
|
||||||
|
$entities[1]->ftvid => $entities[1]->ftid,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$this->assertEqual($result, $expected, t('FIELD_QUERY_RETURN_IDS result format returns the expect result'));
|
||||||
|
|
||||||
|
// Test FIELD_QUERY_RETURN_VALUES result format.
|
||||||
|
// Configure the instance so that we test hook_field_load() (see
|
||||||
|
// field_test_field_load() in field_test.module).
|
||||||
|
$this->instance['settings']['test_hook_field_load'] = TRUE;
|
||||||
|
field_update_instance($this->instance);
|
||||||
|
|
||||||
|
// Query for one of the values in the 1st object and the value shared by
|
||||||
|
// both objects.
|
||||||
|
$conditions = array(array('value', array($values[0], $common_value)));
|
||||||
|
$result = field_attach_query_revisions($this->field_name, $conditions, FIELD_QUERY_RETURN_VALUES);
|
||||||
|
$expected = array(
|
||||||
|
$entity_type => array(
|
||||||
|
$entities[1]->ftvid => (object) array(
|
||||||
|
'ftid' => $entities[1]->ftid,
|
||||||
|
'ftvid' => $entities[1]->ftvid,
|
||||||
|
'fttype' => $entities[1]->fttype,
|
||||||
|
$this->field_name => array(
|
||||||
|
array('value' => $values[0], 'additional_key' => 'additional_value'),
|
||||||
|
array('value' => $common_value, 'additional_key' => 'additional_value'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
$entities[2]->ftvid => (object) array(
|
||||||
|
'ftid' => $entities[2]->ftid,
|
||||||
|
'ftvid' => $entities[2]->ftvid,
|
||||||
|
'fttype' => $entities[2]->fttype,
|
||||||
|
$this->field_name => array(
|
||||||
|
array('value' => $common_value, 'additional_key' => 'additional_value'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$this->assertEqual($result, $expected, t('FIELD_QUERY_RETURN_VALUES result format returns the expect result'));
|
||||||
|
|
||||||
|
// TODO : test
|
||||||
|
}
|
||||||
|
|
||||||
function testFieldAttachViewAndPreprocess() {
|
function testFieldAttachViewAndPreprocess() {
|
||||||
$entity_type = 'test_entity';
|
$entity_type = 'test_entity';
|
||||||
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
|
$entity = field_test_create_stub_entity(0, 0, $this->instance['bundle']);
|
||||||
|
|
|
@ -238,7 +238,8 @@ function field_sql_storage_field_storage_load($obj_type, &$objects, $age, $skip_
|
||||||
// For each column declared by the field, populate the item
|
// For each column declared by the field, populate the item
|
||||||
// from the prefixed database column.
|
// from the prefixed database column.
|
||||||
foreach ($field['columns'] as $column => $attributes) {
|
foreach ($field['columns'] as $column => $attributes) {
|
||||||
$item[$column] = $row->{_field_sql_storage_columnname($field_name, $column)};
|
$column_name = _field_sql_storage_columnname($field_name, $column);
|
||||||
|
$item[$column] = $row->$column_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the item to the field values for the entity.
|
// Add the item to the field values for the entity.
|
||||||
|
@ -350,6 +351,108 @@ function field_sql_storage_field_storage_delete($obj_type, $object) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement hook_field_storage_query().
|
||||||
|
*/
|
||||||
|
function field_sql_storage_field_storage_query($field_name, $conditions, $result_format, $age) {
|
||||||
|
$load_values = $result_format == FIELD_QUERY_RETURN_VALUES;
|
||||||
|
$load_current = $age == FIELD_LOAD_CURRENT;
|
||||||
|
|
||||||
|
$field = field_info_field($field_name);
|
||||||
|
$table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
|
||||||
|
$field_columns = array_keys($field['columns']);
|
||||||
|
|
||||||
|
// Build the query.
|
||||||
|
$query = db_select($table, 't');
|
||||||
|
$query->join('field_config_entity_type', 'e', 't.etid = e.etid');
|
||||||
|
$query
|
||||||
|
->fields('e', array('type'))
|
||||||
|
->condition('deleted', 0)
|
||||||
|
->orderBy('delta');
|
||||||
|
// Add fields, depending on the return format.
|
||||||
|
if ($load_values) {
|
||||||
|
$query->fields('t');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$query->fields('t', array('entity_id', 'revision_id'));
|
||||||
|
}
|
||||||
|
// Add conditions.
|
||||||
|
foreach ($conditions as $condition) {
|
||||||
|
// A condition is either a (column, value, operator) triple, or a
|
||||||
|
// (column, value) pair with implied operator.
|
||||||
|
@list($column, $value, $operator) = $condition;
|
||||||
|
// Translate operator and value if needed.
|
||||||
|
switch ($operator) {
|
||||||
|
case NULL:
|
||||||
|
$operator = is_array($value) ? 'IN' : '=';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'STARTS_WITH':
|
||||||
|
$operator = 'LIKE';
|
||||||
|
$value .= '%';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ENDS_WITH':
|
||||||
|
$operator = 'LIKE';
|
||||||
|
$value = "$value%";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'CONTAINS':
|
||||||
|
$operator = 'LIKE';
|
||||||
|
$value = "%$value%";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Translate field columns into prefixed db columns.
|
||||||
|
if (in_array($column, $field_columns)) {
|
||||||
|
$column = _field_sql_storage_columnname($field_name, $column);
|
||||||
|
}
|
||||||
|
$query->condition($column, $value, $operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = $query->execute();
|
||||||
|
|
||||||
|
// Build results.
|
||||||
|
$return = array();
|
||||||
|
$delta_count = array();
|
||||||
|
foreach ($results as $row) {
|
||||||
|
// If querying all revisions and the entity type has revisions, we need to
|
||||||
|
// key the results by revision_ids.
|
||||||
|
$entity_type = field_info_fieldable_types($row->type);
|
||||||
|
$id = ($load_current || empty($entity_type['revision key'])) ? $row->entity_id : $row->revision_id;
|
||||||
|
|
||||||
|
if ($load_values) {
|
||||||
|
// Populate actual field values.
|
||||||
|
if (!isset($delta_count[$row->type][$id])) {
|
||||||
|
$delta_count[$row->type][$id] = 0;
|
||||||
|
}
|
||||||
|
if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->type][$id] < $field['cardinality']) {
|
||||||
|
$item = array();
|
||||||
|
// For each column declared by the field, populate the item
|
||||||
|
// from the prefixed database column.
|
||||||
|
foreach ($field['columns'] as $column => $attributes) {
|
||||||
|
$column_name = _field_sql_storage_columnname($field_name, $column);
|
||||||
|
$item[$column] = $row->$column_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the 'pseudo object' if needed.
|
||||||
|
if (!isset($return[$row->type][$id])) {
|
||||||
|
$return[$row->type][$id] = field_attach_create_stub_object($row->type, array($row->entity_id, $row->revision_id, $row->bundle));
|
||||||
|
}
|
||||||
|
// Add the item to the field values for the entity.
|
||||||
|
$return[$row->type][$id]->{$field_name}[] = $item;
|
||||||
|
$delta_count[$row->type][$id]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Simply return the list of selected ids.
|
||||||
|
$return[$row->type][$id] = $row->entity_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement hook_field_storage_delete_revision().
|
* Implement hook_field_storage_delete_revision().
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue