Newer
Older
require_once dirname(__FILE__) . '/relation.ctools.inc';
* Describes relations between entities.
/**
* Implements hook_entity_info().
*/
function relation_entity_info() {
$entities['relation'] = array(
'label' => t('Relation'),
'base table' => 'relation',
naught101
committed
'revision table' => 'relation_revision',
'controller class' => 'DrupalDefaultEntityController',
dixon_
committed
'save callback' => 'relation_save',
'creation callback' => 'relation_rules_create',
'deletion callback' => 'relation_delete',
dixon_
committed
'access callback' => 'relation_rules_access',
'uri callback' => 'relation_uri',
'entity keys' => array(
naught101
committed
'revision' => 'vid',
),
'bundle keys' => array(
'view modes' => array(),
);
foreach (relation_get_types() as $type => $info) {
$entities['relation']['bundles'][$type] = (array) $info;
$entities['relation']['bundles'][$type]['admin'] = array(
'path' => 'admin/structure/relation/manage/%relation_type',
'real path' => 'admin/structure/relation/manage/' . $type,
'bundle argument' => 4,
'access arguments' => array('administer relation types'),
return $entities;
}
dixon_
committed
/**
* Implements hook_entity_property_info().
dixon_
committed
*/
function relation_entity_property_info() {
$info = array();
$properties = &$info['relation']['properties'];
Claes Gyllensvärd
committed
$properties = array(
chx
committed
'rid' => array(
'label' => t('Relation ID'),
'description' => t('The internal numeric ID of the relation.'),
'type' => 'integer',
'schema field' => 'rid',
),
Claes Gyllensvärd
committed
'relation_type' => array(
'label' => t('Relation type'),
'type' => 'token',
'description' => t('The type of the relation.'),
Claes Gyllensvärd
committed
'setter callback' => 'entity_property_verbatim_set',
'setter permission' => 'administer nodes',
'options list' => 'relation_rules_get_type_options',
'required' => TRUE,
'schema field' => 'relation_type',
),
'endpoints' => array(
'label' => t('Endpoints'),
'type' => 'list<entity>',
'description' => t('The endpoints of the relation.'),
Claes Gyllensvärd
committed
'setter callback' => 'relation_rules_set_endpoints',
'getter callback' => 'relation_rules_get_endpoints',
'setter permission' => 'administer nodes',
'required' => TRUE,
),
dixon_
committed
return $info;
}
chx
committed
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
* Implements hook_entity_property_info_alter().
*/
function relation_entity_property_info_alter(&$info) {
// Add relation type-specific information, so that it is easily available
// for modules that need to introspect the
// relation structure (ie. Search API).
foreach (relation_get_types() as $relation_type => $relation) {
foreach ($relation->source_bundles as $key => $bundles) {
list($entity_type, $bundle) = explode(':', $bundles, 2);
$info['relation']['bundles'][$bundle]['properties']['endpoints_source_' . $entity_type] = array(
'label' => t('@type (source endpoint)', array('@type' => $entity_type)),
'type' => 'list<' . $entity_type . '>',
'getter callback' => 'relation_rules_get_specific_endpoints',
'endpoint_type' => 'source',
'relation_directional' => $relation->directional,
);
}
foreach ($relation->target_bundles as $key => $bundles) {
list($entity_type, $bundle) = explode(':', $bundles, 2);
$info['relation']['bundles'][$bundle]['properties']['endpoints_target_' . $entity_type] = array(
'label' => t('@type (target endpoint)', array('@type' => $entity_type)),
'type' => 'list<' . $entity_type . '>',
'getter callback' => 'relation_rules_get_specific_endpoints',
'endpoint_type' => 'target',
'relation_directional' => $relation->directional,
);
}
$source_bundles = $relation->source_bundles;
$original_sb = array_values($source_bundles);
$directional = FALSE;
// If its a directional relation, merge the source and target bundles.
if (count($relation->target_bundles) >= 1) {
$original_tb = array_values($relation->target_bundles);
$target_bundles = array_merge($source_bundles, $relation->target_bundles);
$source_bundles = $target_bundles;
$directional = TRUE;
}
else {
$target_bundles = $source_bundles;
}
foreach ($target_bundles as $target_key => $target_bundle) {
list($entity_target) = explode(':', $target_bundle, 2);
foreach ($source_bundles as $source_key => $source_bundle) {
$property_reverse = ($directional && !in_array($source_bundle, $original_sb) && !in_array($target_bundle, $original_tb));
$property = ($property_reverse) ? 'relation_' . $relation_type . '_' . $entity_target . '_reverse' : 'relation_' . $relation_type . '_' . $entity_target;
list($entity_source) = explode(':', $source_bundle, 2);
if (!$directional || ($property_reverse) || (in_array($target_bundle, $original_tb) && in_array($source_bundle, $original_sb))) {
$info[$entity_source]['properties'][$property] = array(
'label' => t('Relation @relation_type (to @entity' . (($property_reverse) ? ' reverse)' : ')'), array('@relation_type' => $relation_type, '@entity' => $entity_target)),
'type' => 'list<' . $entity_target . '>',
'relation_type' => $relation_type,
'target_type' => $entity_target,
'description' => t("A list of entities related."),
'getter callback' => 'relation_rules_get_related_entities',
);
}
}
}
}
}
*/
function relation_permission() {
return array(
'administer relation types' => array(
'title' => t('Administer Relation types'),
'description' => t('Create, edit, delete, and perform administration tasks for relation types.'),
),
'export relation types' => array(
'title' => t('Export Relation types'),
'description' => t('Export relation types.'),
),
'title' => t('View Relations'),
'description' => t('Grant access to view the endpoints of Relations.'),
),
'create relations' => array(
'title' => t('Create Relations'),
'description' => t('Create Relations between entities.'),
),
'edit relations' => array(
'title' => t('Edit Relations'),
'description' => t('Edit fields on existing relations.'),
),
'delete relations' => array(
'title' => t('Delete Relations'),
'description' => t('Delete existing relations.'),
'administer relations' => array(
'title' => t('Administer Relations'),
'description' => t('Administer existing relations.'),
),
);
}
/**
function relation_menu() {
$items['relation/%relation'] = array(
'title callback' => 'relation_page_title',
'title arguments' => array(1),
'access arguments' => array('access relations'),
'page callback' => 'relation_page',
'page arguments' => array(1),
);
$items['relation/%relation/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['relation/%relation/edit'] = array(
'title' => 'Edit',
'access arguments' => array('edit relations'),
'page callback' => 'drupal_get_form',
'page arguments' => array('relation_edit_form', 1),
$items['relation/%relation/delete'] = array(
'title' => 'Delete',
'page callback' => 'drupal_get_form',
'page arguments' => array('relation_delete_confirm', 1),
'access arguments' => array('delete relations'),
'weight' => 10,
'type' => MENU_LOCAL_TASK,
);
$items['admin/structure/relation'] = array(
'access arguments' => array('administer relation types'),
naught101
committed
'description' => 'Manage relation types, including relation properties (directionality, transitivity etc), available bundles, and fields.',
$items['admin/structure/relation/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/structure/relation/add'] = array(
'access arguments' => array('administer relation types'),
'page callback' => 'drupal_get_form',
'page arguments' => array('relation_type_form'),
'type' => MENU_LOCAL_ACTION,
$items['admin/structure/relation/import'] = array(
'title' => 'Import relation type',
'access arguments' => array('administer relation types'),
'page callback' => 'drupal_get_form',
'page arguments' => array('relation_type_import'),
'type' => MENU_LOCAL_ACTION,
'file' => 'relation.admin.inc',
);
$items['admin/structure/relation/manage/%relation_type'] = array(
naught101
committed
'title callback' => 'relation_type_page_title',
'title arguments' => array(4),
'access arguments' => array('administer relation types'),
'page callback' => 'drupal_get_form',
'page arguments' => array('relation_type_form', 4),
$items['admin/structure/relation/manage/%relation_type/edit'] = array(
'title' => 'Edit',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/structure/relation/manage/%relation_type/delete'] = array(
'title' => 'Delete',
'page arguments' => array('relation_type_delete_confirm', 4),
'access arguments' => array('administer relation types'),
'type' => MENU_LOCAL_TASK,
'file' => 'relation.admin.inc',
'weight' => 20,
$items['admin/structure/relation/manage/%relation_type/export'] = array(
'title' => 'Export',
'page callback' => 'drupal_get_form',
'page arguments' => array('relation_export_relation_type', 4),
'access arguments' => array('export relation types'),
'type' => MENU_LOCAL_TASK,
'file' => 'relation.ctools.inc',
'weight' => 10,
);
naught101
committed
$items['admin/config/development/generate/relation'] = array(
'title' => 'Generate relations',
'access arguments' => array('administer relation types'),
'page callback' => 'drupal_get_form',
'page arguments' => array('relation_generate_form'),
'file' => 'relation.admin.inc',
);
// Relations listing.
$items['admin/content/relation'] = array(
'title' => 'Relations',
'page callback' => 'relation_admin_content',
'access arguments' => array('administer relations'),
'description' => 'View, edit and delete all the available relations on your site.',
'file' => 'relation.admin.inc',
'type' => MENU_LOCAL_TASK,
);
naught101
committed
/**
naught101
committed
*
* @param $info
* Array of relation type settings. All but relation_type are optional,
* although if the bundles arrays are empty, relations might be difficult to
* create! Keys are:
* - relation_type: Relation type machine name (string).
* - label: Relation type human-readable name (string). Defaults to
* duplicating relation_type.
* - directional: whether relation is directional (boolean). Defaults to
* FALSE.
* - transitive: whether relation is transitive (boolean). Defaults to FALSE.
* - r_unique: whether relations of this type are unique (boolean). Defaults
* to FALSE.
* - min_arity: minimum number of entities in relations of this type
* (int >= 2). Defaults to 2.
* - max_arity: maximum number of entities in relations of this type
* (int >= min_arity). Defaults to 2.
naught101
committed
* - source_bundles: array containing allowed bundle keys. This is used for
* both directional and non-directional relations. Bundle key arrays are
naught101
committed
* of the form 'entity:bundle', eg. 'node:article', or 'entity:*' for all
* bundles of the type.
naught101
committed
* - target_bundles: array containing arrays allowed target bundle keys.
* This is the same format as source_bundles, but is only used for
* directional relations.
*
* @return
* Relation type object, or FALSE if creation fails.
naught101
committed
*/
function relation_type_create($info = array()) {
$info = (array) $info;
if (empty($info['relation_type'])) {
return FALSE;
$info += array(
'min_arity' => 2,
'max_arity' => 2,
'directional' => FALSE,
'transitive' => FALSE,
'r_unique' => FALSE,
'source_bundles' => array(),
'target_bundles' => array(),
if (empty($info['label'])) {
$info['label'] = $info['relation_type'];
if (empty($info['reverse_label'])) {
// Directional relations should have a reverse label, but if they don't,
// or if they are symmetric:
$info['reverse_label'] = $info['label'];
}
return (object) $info;
}
/**
* Saves a relation bundle.
*
* @param $relation_type
* stdClass object with relation type properties. See relation_type_create().
* @param $write_record_keys
* Array containing the primary key of the relation ('relation_type'), if we are
* updating a relation, or an empty array if we are creating a new relation.
*/
function relation_type_save($relation_type, $write_record_keys = array()) {
// Make sure all keys are populated.
$relation_type = relation_type_create($relation_type);
$type = $relation_type->relation_type;
$source_bundles = $relation_type->source_bundles;
$target_bundles = $relation_type->target_bundles;
unset($relation_type->source_bundles, $relation_type->target_bundles);
$transaction = db_transaction();
drupal_write_record('relation_type', $relation_type, $write_record_keys);
// Remove all existing bundles from the relation type before re-adding.
db_delete('relation_bundles')
->condition('relation_type', $type)
->execute();
$query = db_insert('relation_bundles')
->fields(array('relation_type', 'entity_type', 'bundle', 'r_index'));
foreach ($source_bundles as $entity_bundles) {
list($entity_type, $bundle) = explode(':', $entity_bundles, 2);
$query->values(array($type, $entity_type, $bundle, 0));
if ($relation_type->directional) {
foreach ($target_bundles as $entity_bundles) {
list($entity_type, $bundle) = explode(':', $entity_bundles, 2);
$query->values(array($type, $entity_type, $bundle, 1));
$query->execute();
relation_type_ensure_instance($type);
menu_rebuild();
}
/**
* Make sure the instance exists for this type.
*/
function relation_type_ensure_instance($type) {
if (!field_info_instance('relation', 'endpoints', $type)) {
$instance = array(
'field_name' => 'endpoints',
'entity_type' => 'relation',
'bundle' => $type,
/**
naught101
committed
* The machine name of the relation type (bundle) to be loaded.
* @return
* A relation type record (as an Array) in the same format as expected by
* relation_type_save().
*/
function relation_type_load($relation_type) {
naught101
committed
$types = relation_get_types(array($relation_type));
return isset($types[$relation_type]) ? $types[$relation_type] : FALSE;
naught101
committed
}
/**
* Loads a relation type (bundle), or all relation bundles.
*
naught101
committed
* @param $types
* An array of machine names of the relation types to be loaded. If $types
* is empty, load all relation types.
naught101
committed
*
* @return
* A an array of relation type records in the same format as expected by
* relation_type_save(), keyed by relation_type.
naught101
committed
*/
naught101
committed
function relation_get_types($types = array()) {
$return = $types ? ctools_export_crud_load_multiple('relation_type', $types) : ctools_export_crud_load_all('relation_type');
static $recurse = FALSE;
$recurse = TRUE;
foreach ($return as $type => $data) {
if (!empty($data->in_code_only)) {
relation_type_ensure_instance($type);
}
}
}
return $return;
/**
* Returns all relation types in a way which can be used
* on form options.
*/
function relation_get_types_options() {
$types = relation_get_types();
$options = array();
foreach ($types as $type => $relation_type) {
$options[$type] = $relation_type->label;
}
return $options;
}
* Helper function. Attaches bundles to relation type objects in an array.
function _relation_get_types_bundles(&$relation_types) {
foreach ($relation_types as &$relation_type) {
if (empty($relation_type->in_code_only) && empty($relation_type->bundles_loaded)) {
// If overridden or not exported at all, reset the bundles before
// loading from the database to avoid duplication.
$relation_type->source_bundles = array();
$relation_type->target_bundles = array();
foreach (db_query('SELECT relation_type, entity_type, bundle, r_index FROM {relation_bundles} WHERE relation_type = :relation_type', array(':relation_type' => $relation_type->relation_type)) as $record) {
$endpoint = $record->r_index ? 'target_bundles' : 'source_bundles';
$relation_type->{$endpoint}[] = "$record->entity_type:$record->bundle";
}
// Do not run this twice. ctools static caches the types but runs the
// subrecord callback on the whole cache, every already loaded relation
// type.
$relation_type->bundles_loaded = TRUE;
/**
* Lists all relation types.
*
* @return
* Array of relation type names in the format "Label (type)", keyed by
* relation_type.
*/
function relation_list_types() {
$results = db_select('relation_type', 'rt')
->fields('rt', array('relation_type', 'label'))
->execute()->fetchAllAssoc('relation_type');
$relation_types = array();
foreach ($results as $type => $relation_type) {
$relation_types[$type] = $relation_type->label . ' (' . $type . ')';
}
return $relation_types;
}
/**
* Deletes a relation type (bundle).
*
* The machine name of the relation type (bundle) to be deleted.
*/
function relation_type_delete($relation_type) {
$endpoints_field = field_read_instance('relation', 'endpoints', $relation_type);
naught101
committed
field_delete_instance($endpoints_field, FALSE);
field_attach_delete_bundle('relation', $relation_type);
naught101
committed
db_delete('relation_type')->condition('relation_type', $relation_type)->execute();
db_delete('relation_bundles')->condition('relation_type', $relation_type)->execute();
naught101
committed
/**
* Loads a relation from a relation id.
naught101
committed
*
* @param $rid
* Numerical id of the relation to be loaded.
naught101
committed
*
* @return
* Loaded relation object. Relation objects are stdClass Object of the form:
* - rid: numeric relation id.
* - relation_type: relation bundle machine name.
* - arity: the number of entities in the relation
* - rdf_mapping: not yet implemented (empty array)
* - endpoints: Field holding the entities that make up the relation.
* Field columns are:
* - entity_type: The type of the entity (eg. node).
* - entity_id: Numeric entity ID.
naught101
committed
*/
naught101
committed
function relation_load($rid, $vid = NULL, $reset = FALSE) {
$conditions = (isset($vid) ? array('vid' => $vid) : array());
$relations = relation_load_multiple(array($rid), $conditions, $reset);
return reset($relations);
naught101
committed
/**
* Loads a set of relations from an array of relation ids.
naught101
committed
*
* @param $rids
* Array of numerical relation ids of the relations to be loaded.
naught101
committed
*
* @return
* Associative array of loaded relation objects, keyed by relation id.
*
* @see relation_load()
naught101
committed
*/
function relation_load_multiple($rids, $conditions = array(), $reset = FALSE) {
// Entity load handles field_attach_load for us.
return entity_load('relation', $rids, $conditions, $reset);
naught101
committed
/**
* Relation display page. Currently only displays related entities.
*
* @TODO: implement directionality, possibly give more details on entities?
*/
function relation_page($relation) {
return relation_view($relation);
}
function relation_view($relation, $view_mode = 'full') {
$entity_type = 'relation';
$entity = $relation;
$entities = array($relation->rid => $relation);
field_attach_prepare_view($entity_type, $entities, $view_mode);
entity_prepare_view($entity_type, $entities);
$build = field_attach_view($entity_type, $entity, $view_mode);
$build += array(
'#entity' => $relation,
'#view_mode' => $view_mode,
'#language' => LANGUAGE_NONE,
);
module_invoke_all('entity_view', $entity, $entity_type, $view_mode, LANGUAGE_NONE);
drupal_alter('entity_view', $build, $entity_type);
function relation_multiple_view($relations, $view_mode) {
$build = array();
foreach ($relations as $relation) {
$build['relation'][$relation->rid] = relation_view($relation, $view_mode);
}
return $build;
}
naught101
committed
* Relation display page title callback.
*/
function relation_page_title($relation) {
return 'Relation ' . $relation->rid;
naught101
committed
/**
* Relation type display/edit page title callback.
*/
function relation_type_page_title($type) {
return $type->label;
}
function relation_edit_form($form, &$form_state, $relation) {
$form_state['relation'] = $relation;
field_attach_form('relation', $relation, $form, $form_state);
$form['actions']['#weight'] = 100;
$form['actions']['save'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
function relation_edit_form_submit($form, &$form_state) {
$relation = $form_state['relation'];
entity_form_submit_build_entity('relation', $relation, $form, $form_state);
$rid = relation_save($relation);
if ($rid) {
$form_state['redirect'] = 'relation/' . $rid;
}
/**
*
* The following example demonstrates how to check if a relation of type
* 'likes' exists between two entities, user 17 and node 253.
*
* @code
* $entity_keys = array(
* array('entity_type' => 'user', 'entity_id' => 17),
* array('entity_type' => 'node', 'entity_id' => 253),
* );
* $relation_type = 'likes';
* $results = relation_relation_exists($entity_keys, $relation_type);
* @endcode
*
* @param $entity_keys
* The entity keys of the relation to found. Entity_keys are arrays keyed by
* 'entity_type' and 'entity_id'.
* @param $relation_type
* (Optional) The relation type (bundle) of the relation to be checked.
*
* @return
* Return FALSE if no relation exists, or an array of matching relations.
*/
function relation_relation_exists($entity_keys, $relation_type = NULL) {
foreach ($entity_keys as $entity_key) {
$query->related($entity_key['entity_type'], $entity_key['entity_id']);
}
if ($relation_type) {
$query->propertyCondition('relation_type', $relation_type);
}
$query->propertyCondition('arity', count($entity_keys));
$relation_ids = $query->execute();
return $relation_ids ? $relation_ids : FALSE;
}
* Constructs a relation from a relation type machine name and a list of endpoints.
* The relation type (bundle) of the relation to be created.
* @param $endpoints
* A list of endpoint entities. Each endpoint is defined by an associate
* array, with an entity_type and entity_id key. For example:
* @code
* array(
* array('entity_type' => 'node', 'entity_id' => 1),
* array('entity_type' => 'user', 'entity_id' => 5),
* array);
* @endcode
*
* @return
naught101
committed
* The new relation object.
function relation_create($type, $endpoints, $account = NULL) {
if (!isset($account)) {
$account = $GLOBALS['user'];
}
$relation = new stdClass();
$relation->is_new = TRUE;
$relation->uid = $account->uid;
$relation->endpoints[LANGUAGE_NONE] = $endpoints;
naught101
committed
return $relation;
}
/**
* Saves a relation.
*
* @param $relation
* The relation entity data object. See relation_create() for the appropriate
* format (or just use it).
*
* @return
* The new relation id.
*/
function relation_save($relation) {
try {
field_attach_validate('relation', $relation);
}
catch (FieldValidationException $e) {
return FALSE;
}
$relation->arity = count($relation->endpoints[LANGUAGE_NONE]);
// use time() instead of REQUEST_TIME, because otherwise tests
// RelationQuery::order() are impossible.
$relation->changed = time();
if (empty($relation->is_new) && !empty($relation->rid)) {
$keys = array('rid');
$op = 'update';
naught101
committed
}
else {
$keys = array();
$op = 'insert';
field_attach_presave('relation', $relation);
module_invoke_all('entity_presave', $relation, 'relation');
unset($relation->vid);
drupal_write_record('relation_revision', $relation);
chx
committed
drupal_write_record('relation', $relation, $keys);
call_user_func("field_attach_$op", 'relation', $relation);
module_invoke_all('entity_' . $op, $relation, 'relation');
module_invoke('rules', 'invoke_event', 'relation_' . $op, $relation);
drupal_static_reset('relation_get_related_entity');
naught101
committed
return $relation->rid;
chx
committed
* Insert a new relation in the database.
chx
committed
function relation_insert($type, $endpoints) {
$relation = relation_create($type, $endpoints);
return relation_save($relation);
}
/**
* Updates a relation.
*/
function relation_update($relation) {
relation_save($relation);
}
/**
* Deletes a relation.
*
* @param $rid
* The numeric id of the relation to be deleted.
function relation_delete($rid) {
relation_multiple_delete(array($rid));
}
/**
* Deletes a relation.
*
* @param $rid
* An array of numeric ids of the relation to be deleted.
function relation_multiple_delete($rids) {
$relations = relation_load_multiple($rids);
foreach ($relations as $rid => $relation) {
db_delete('relation')->condition('rid', $rid)->execute();
db_delete('relation_revision')->condition('rid', $rid)->execute();
module_invoke_all('entity_delete', $relation, 'relation');
field_attach_delete('relation', $relation);
* Menu callback: ask for confirmation of relation deletion.
*/
function relation_delete_confirm($form, &$form_state, $relation) {
$form['#relation'] = $relation;
// Always provide entity id in the same form key as in the entity edit form.
$form['rid'] = array('#type' => 'value', '#value' => $relation->rid);
t('Are you sure you want to delete relation %rid?', array('%rid' => $relation->rid)),
'relation/' . $relation->rid,
t('This action cannot be undone.'),
t('Delete'),
t('Cancel')
);
}
/**
*/
function relation_delete_confirm_submit($form, &$form_state) {
if ($form_state['values']['confirm']) {
$relation = $form['#relation'];
relation_delete($form_state['values']['rid']);
watchdog('relation', '@type: deleted %title.', array('@type' => $relation->relation_type, '%title' => $relation->rid));
drupal_set_message(t('@type %title has been deleted.', array('@type' => $relation->relation_type, '%title' => $relation->rid)));
}
$form_state['redirect'] = '<front>';
}
*
* @see entity_uri()
*/
function relation_uri($relation) {
return array('path' => 'relation/' . $relation->rid);
/**
* Returns a query object to find related entities.
*
* @param $entity_type
* The entity type of one of the endpoints.
* @param $entity_id
* The entity id of one of the endpoints.
*
* @return RelationQuery
* The query object itself.
*/
function relation_query($entity_type = NULL, $entity_id = NULL, $index = NULL) {
return new RelationQuery($entity_type, $entity_id, $index);
}
naught101
committed
/**
* Returns the entity object of the first other entity in the first relation
* that matches the given conditions. Do not expect to get exactly what you
* want, especially if you have multiple relations of the same type on the
* search entity.
*
* @param $entity_type
* The entity type of one of the endpoints.
* @param $entity_id
* The entity id of one of the endpoints.
* @param $relation_type
* (optional) The relation type of the relation to find.
* @param $r_index
* (optional) The index of the search entity in the relation to be found
* (0 = source, 1 = target).
*
* @return
* The entity object from the other endpoint.
*/
function relation_get_related_entity($entity_type, $entity_id, $relation_type = NULL, $r_index = NULL) {
// Static cache the results of relation_query() and relation_load() to avoid
// duplicate queries if this is called multiple times with the same arguments
// during a request.
$items = &drupal_static(__FUNCTION__);
$request_key = "$entity_type:$entity_id";
$cache_key = "$request_key:$relation_type:$r_index";
if (isset($items[$cache_key])) {
$entities = $items[$cache_key];
else {
$query = relation_query($entity_type, $entity_id, $r_index);
if ($relation_type) {
$query->entityCondition('bundle', $relation_type);
}
$results = $query->execute();
$result = reset($results);
if ($result) {
$relation = relation_load($result->rid);
$entities = field_get_items('relation', $relation, 'endpoints');
}
else {
$entities = FALSE;
}
$items[$cache_key] = $entities;
naught101
committed
}
if ($entities) {
$first_entity_key = $entities[0]['entity_type'] . ':' . $entities[0]['entity_id'];
if (isset($r_index)) {
$request_key = $request_key . ':' . $r_index;
$first_entity_key = $first_entity_key . ':' . $entities[0]['r_index'];
}
if ($request_key == $first_entity_key) {
$other_endpoints = entity_load($entities[1]['entity_type'], array($entities[1]['entity_id']));
return reset($other_endpoints);
}
$other_endpoints = entity_load($entities[0]['entity_type'], array($entities[0]['entity_id']));
naught101
committed
return reset($other_endpoints);
}
return FALSE;
naught101
committed
}
naught101
committed
/**
* Returns an array of the relation types that can have the given entity as
* as an endpoint.
*
* @param $entity_type
* The entity type of the endpoint.
* @param $bundle
* The bundle of the endpoint.
* @param $endpoint
* (optional) the type of endpoint. This is only used for directional
* relation types. Possible options are 'source', 'target', or 'both'.
*
* @return
* An array of relation types, keyed by relation_type.
*/
function relation_get_available_types($entity_type, $bundle, $endpoint = 'source') {
$bundle_key = $entity_type . ':' . $bundle;
$all_bundle_key = $entity_type . ':*';
$relation_types = relation_get_types();
foreach ($relation_types as $type => $relation_type) {
naught101
committed
if ($endpoint == 'source' || $endpoint == 'both') {
if (in_array($bundle_key, $relation_type->source_bundles) || in_array($all_bundle_key, $relation_type->source_bundles)) {
$available = TRUE;
naught101
committed
}
}
if ($endpoint == 'target' || $endpoint == 'both') {
if (in_array($bundle_key, $relation_type->target_bundles) || in_array($all_bundle_key, $relation_type->target_bundles)) {
$available = TRUE;
naught101
committed
}
}
if (!$available) {
unset($relation_types[$type]);
}
}
return $relation_types;
}
/**
*/
function relation_entity_delete($entity, $entity_type) {
list($id) = entity_extract_ids($entity_type, $entity);
$relations = relation_query($entity_type, $id)->execute();
relation_multiple_delete(array_keys($relations));
naught101
committed
if ($relations) {
drupal_set_message(t('Relations !relations have been deleted.', array('!relations' => implode(', ', array_keys($relations)))));
}
/**
* Implements hook_views_api().
*/
function relation_views_api() {
return array(
'api' => 3.0,
'path' => drupal_get_path('module', 'relation') . '/views',
);
}
/**
* Gets the label of the relation type of the given relation
*
* @param $relation
* A relation object.
* @param $reverse
* optional: whether to get the reverse label (boolean).
*
* @return
* The label of the relation type.
*/
function relation_get_type_label($relation, $reverse = FALSE) {
$type = relation_type_load($relation->relation_type);
if ($type->directional && $reverse) {
return $type->reverse_label;
}
else {
return $type->label;
}
naught101
committed
}
/**
* Implements hook_theme().
*/
function relation_theme() {
$theme = array(
// relation.admin.inc.
'relation_admin_content' => array(
'variables' => array('relations' => NULL),
'file' => 'relation.admin.inc',
),
);
return $theme;
}