diff --git a/entity.info b/entity.info index 958e28cd772eb9db4c549774f90221c602ae8ce6..56895549c67fb7fcebb5d1128aa79b282c06014e 100644 --- a/entity.info +++ b/entity.info @@ -10,3 +10,4 @@ files[] = entity.features.inc files[] = entity.info.inc files[] = entity.rules.inc files[] = entity.test +files[] = views/handlers/entity_views_handler_relationship_by_bundle.inc diff --git a/views/entity.views.inc b/views/entity.views.inc index a509407f59134da4a9197f47ba58e27ad6284ff0..6f2f8360679ded7df2c9355e43ba0e369c5f9a12 100644 --- a/views/entity.views.inc +++ b/views/entity.views.inc @@ -160,7 +160,7 @@ class EntityDefaultViewsController { 'help' => t("Associated @label via the @label's @property.", array('@label' => $label_lowercase, '@property' => $property_label_lowercase)), 'relationship' => array( 'label' => $this->info['label'], - 'handler' => 'views_handler_relationship', + 'handler' => $this->getRelationshipHandlerClass($this->type, $type), 'base' => $this->info['base table'], 'base field' => $views_field_name, 'relationship field' => isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'], @@ -169,7 +169,7 @@ class EntityDefaultViewsController { $return['relationship'] = array( 'label' => drupal_ucfirst($info['label']), - 'handler' => 'views_handler_relationship', + 'handler' => $this->getRelationshipHandlerClass($type, $this->type), 'base' => $info['base table'], 'base field' => isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'], 'relationship field' => $views_field_name, @@ -308,6 +308,28 @@ class EntityDefaultViewsController { return $return; } + /** + * Determines the handler to use for a relationship to an entity type. + * + * @param $entity_type + * The entity type to join to. + * @param $left_type + * The data type from which to join. + */ + function getRelationshipHandlerClass($entity_type, $left_type) { + // Look for an entity type which is used as bundle for the given entity + // type. If there is one, allow filtering the relation by bundle by using + // our own handler. + foreach (entity_get_info() as $type => $info) { + // In case we already join from the bundle entity we do not need to filter + // by bundle entity any more, so we stay with the general handler. + if (!empty($info['bundle of']) && $info['bundle of'] == $entity_type && $type != $left_type) { + return 'entity_views_handler_relationship_by_bundle'; + } + } + return 'views_handler_relationship'; + } + /** * A callback returning property options, suitable to be used as views options callback. */ diff --git a/views/handlers/entity_views_handler_relationship_by_bundle.inc b/views/handlers/entity_views_handler_relationship_by_bundle.inc new file mode 100644 index 0000000000000000000000000000000000000000..f5457a0f042ce7ee011d889fc5b310a923069289 --- /dev/null +++ b/views/handlers/entity_views_handler_relationship_by_bundle.inc @@ -0,0 +1,116 @@ + array()); + + return $options; + } + + /** + * Add an entity type option. + */ + function options_form(&$form, &$form_state) { + parent::options_form($form, $form_state); + + // Get the entity type and info from the table data for the base on the + // right hand side of the relationship join. + $table_data = views_fetch_data($this->definition['base']); + $entity_type = $table_data['table']['entity type']; + $entity_info = entity_get_info($entity_type); + + // Get the info of the bundle entity. + foreach (entity_get_info() as $type => $info) { + if (isset($info['bundle of']) && $info['bundle of'] == $entity_type) { + $entity_bundle_info = $info; + break; + } + } + + $plural_label = isset($entity_bundle_info['plural label']) ? $entity_bundle_info['plural label'] : $entity_bundle_info['label'] . 's'; + $bundle_options = array(); + foreach ($entity_info['bundles'] as $name => $info) { + $bundle_options[$name] = $info['label']; + } + + $form['bundle_types'] = array( + '#title' => $plural_label, + '#type' => 'checkboxes', + '#description' => t('Restrict this relationship to one or more @bundles.', array('@bundles' => strtolower($entity_bundle_info['plural label']))), + '#options' => $bundle_options, + '#default_value' => $this->options['bundle_types'], + ); + } + + /** + * Make sure only checked bundle types are left. + */ + function options_submit(&$form, &$form_state) { + $form_state['values']['bundle_types'] = array_filter($form_state['values']['bundle_types']); + parent::options_submit($form, $form_state); + } + + /** + * Called to implement a relationship in a query. + * + * Mostly the same as the parent method, except we add an extra clause to + * the join. + */ + function query() { + $table_data = views_fetch_data($this->definition['base']); + $base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field']; + $this->ensure_my_table(); + + $def = $this->definition; + $def['table'] = $this->definition['base']; + $def['field'] = $base_field; + $def['left_table'] = $this->table_alias; + $def['left_field'] = $this->field; + if (!empty($this->options['required'])) { + $def['type'] = 'INNER'; + } + + // Add an extra clause to the join if there are bundle types selected. + if ($this->options['bundle_types']) { + $def['extra'] = array( + array( + // The table and the IN operator are implicit. + 'field' => 'type', + 'value' => $this->options['bundle_types'], + ), + ); + } + + if (!empty($def['join_handler']) && class_exists($def['join_handler'])) { + $join = new $def['join_handler']; + } + else { + $join = new views_join(); + } + + $join->definition = $def; + $join->construct(); + $join->adjusted = TRUE; + + // Use a short alias for this. + $alias = $def['table'] . '_' . $this->table; + $this->alias = $this->query->add_relationship($alias, $join, $this->definition['base'], $this->relationship); + } +}