示例#1
0
 /**
  * Test Views admin UI and field handlers.
  */
 public function testViewsAdmin()
 {
     $admin_user = $this->drupalCreateUser(array('administer search_api', 'access administration pages', 'administer views'));
     $this->drupalLogin($admin_user);
     $this->drupalGet('admin/structure/views/view/search_api_test_view');
     $this->assertResponse(200);
     // Switch to "Fields" row style.
     $this->clickLink($this->t('Rendered entity'));
     $this->assertResponse(200);
     $edit = array('row[type]' => 'fields');
     $this->drupalPostForm(NULL, $edit, $this->t('Apply'));
     $this->assertResponse(200);
     $this->drupalPostForm(NULL, array(), $this->t('Apply'));
     $this->assertResponse(200);
     // Add the "User ID" relationship.
     $this->clickLink($this->t('Add relationships'));
     $edit = array('name[search_api_datasource_database_search_index_entity_entity_test.user_id]' => 'search_api_datasource_database_search_index_entity_entity_test.user_id');
     $this->drupalPostForm(NULL, $edit, $this->t('Add and configure relationships'));
     $this->drupalPostForm(NULL, array(), $this->t('Apply'));
     // Add new fields. First check that the listing seems correct.
     $this->clickLink($this->t('Add fields'));
     $this->assertResponse(200);
     $this->assertText($this->t('Test entity datasource'));
     $this->assertText($this->t('Authored on'));
     $this->assertText($this->t('Body (indexed field)'));
     $this->assertText($this->t('Index Test index'));
     $this->assertText($this->t('Entity ID'));
     $this->assertText($this->t('Excerpt'));
     $this->assertText($this->t('The search result excerpted to show found search terms'));
     $this->assertText($this->t('Relevance'));
     $this->assertText($this->t('The relevance of this search result with respect to the query'));
     $this->assertText($this->t('Language code'));
     $this->assertText($this->t('The user language code.'));
     $this->assertText($this->t('(No description available)'));
     $this->assertNoText($this->t('Error: missing help'));
     // Then add some fields.
     $fields = array('views.counter', 'search_api_datasource_database_search_index_entity_entity_test.id', 'search_api_index_database_search_index.search_api_datasource', 'search_api_datasource_database_search_index_entity_entity_test.body', 'search_api_index_database_search_index.category', 'search_api_index_database_search_index.keywords', 'search_api_datasource_database_search_index_entity_entity_test.user_id');
     $edit = array();
     foreach ($fields as $field) {
         $edit["name[{$field}]"] = $field;
     }
     $this->drupalPostForm(NULL, $edit, $this->t('Add and configure fields'));
     $this->assertResponse(200);
     for ($i = 0; $i < count($fields); ++$i) {
         $this->submitFieldsForm();
     }
     // Save the view.
     $this->drupalPostForm(NULL, array(), $this->t('Save'));
     $this->assertResponse(200);
     // Check the results.
     $this->drupalGet('search-api-test');
     $this->assertResponse(200);
     foreach ($this->entities as $id => $entity) {
         $fields = array('search_api_datasource', 'id', 'body', 'category', 'keywords');
         foreach ($fields as $field) {
             if ($field != 'search_api_datasource') {
                 $data = Utility::extractFieldValues($entity->get($field));
                 if (!$data) {
                     $data = array('[EMPTY]');
                 }
             } else {
                 $data = array('entity:entity_test');
             }
             $prefix = "#{$id} [{$field}] ";
             $text = $prefix . implode("|{$prefix}", $data);
             $this->assertText($text, "Correct value displayed for field {$field} on entity #{$id} (\"{$text}\")");
         }
     }
 }
 /**
  * Runs before any fields are rendered.
  *
  * This gives the handlers some time to set up before any handler has
  * been rendered.
  *
  * @param \Drupal\views\ResultRow[] $values
  *   An array of all ResultRow objects returned from the query.
  *
  * @see \Drupal\views\Plugin\views\field\FieldHandlerInterface::preRender()
  */
 public function preRender(&$values)
 {
     // We deal with the properties one by one, always loading the necessary
     // values for any nested properties coming afterwards.
     // @todo This works quite well, but will load each item/entity individually.
     //   Instead, we should exploit the workflow of proceeding by each property
     //   on its own to multi-load as much as possible (maybe even entities of
     //   the same type from different properties).
     // @todo Also, this will unnecessarily load items/entities even if all
     //   required fields are provided in the results. However, to solve this,
     //   expandRequiredProperties() would have to provide more information, or
     //   provide a separate properties list for each row.
     foreach ($this->expandRequiredProperties() as $datasource_id => $properties) {
         foreach ($properties as $property_path => $combined_property_path) {
             // Determine the path of the parent property, and the property key to
             // take from it for this property. If the name is "_object", we just
             // wanted the parent object to be loaded, so we might be done – except
             // when the parent is empty, in which case we wanted to load the
             // original search result, which we haven't done yet.
             list($parent_path, $name) = Utility::splitPropertyPath($property_path);
             if ($parent_path && $name == '_object') {
                 continue;
             }
             // Now go through all rows and add the property to them, if necessary.
             foreach ($values as $i => $row) {
                 // Bail for rows with the wrong datasource for this property, or for
                 // which this field doesn't even apply (which will usually be the
                 // same, though).
                 if ($datasource_id != $row->search_api_datasource || !$this->isActiveForRow($row)) {
                     continue;
                 }
                 // Check whether there are parent objects present. If no, either load
                 // them (in case the parent is the result item itself) or bail.
                 if (empty($row->_relationship_objects[$parent_path])) {
                     if ($parent_path) {
                         continue;
                     } else {
                         $row->_relationship_objects[$parent_path] = array($row->_item->getOriginalObject());
                     }
                 }
                 // If the property key is "_object", we just needed to load the search
                 // result item, so we're now done.
                 if ($name == '_object') {
                     continue;
                 }
                 // Determine whether we want to set field values for this property on
                 // this row. This is the case if the property is one of the explicitly
                 // retrieved properties and not yet set on the result row object.
                 $set_values = isset($this->retrievedProperties[$datasource_id][$property_path]) && !isset($row->{$combined_property_path});
                 if (empty($row->_relationship_objects[$property_path])) {
                     // Iterate over all parent objects to get their typed data for this
                     // property and to extract their values.
                     $row->_relationship_objects[$property_path] = array();
                     foreach ($row->_relationship_objects[$parent_path] as $parent) {
                         // Follow references.
                         while ($parent instanceof DataReferenceInterface) {
                             $parent = $parent->getTarget();
                         }
                         // At this point we need the parent to be a complex item,
                         // otherwise it can't have any children (and thus, our property
                         // can't be present).
                         if (!$parent instanceof ComplexDataInterface) {
                             continue;
                         }
                         // Add the typed data for the property to our relationship objects
                         // for this property path. To treat list properties correctly
                         // regarding possible child properties, add all the list items
                         // individually.
                         try {
                             $typed_data = $parent->get($name);
                             // If the typed data is an entity, check whether the current
                             // user can access it.
                             $value = $typed_data->getValue();
                             if ($value instanceof EntityInterface) {
                                 if (!isset($account)) {
                                     $account = $this->getQuery()->getAccessAccount();
                                 }
                                 if (!$value->access('view', $account)) {
                                     continue;
                                 }
                             }
                             if ($typed_data instanceof ListInterface) {
                                 foreach ($typed_data as $item) {
                                     $row->_relationship_objects[$property_path][] = $item;
                                 }
                             } else {
                                 $row->_relationship_objects[$property_path][] = $typed_data;
                             }
                         } catch (\InvalidArgumentException $e) {
                             // This can easily happen, e.g., when requesting a field that
                             // only exists on a different bundle. Unfortunately, there is no
                             // ComplexDataInterface::hasProperty() method, so we can only
                             // catch and ignore the exception.
                         }
                     }
                 }
                 // Initially the array of values, if we want to set them.
                 if ($set_values) {
                     $row->{$combined_property_path} = array();
                 }
                 // Iterate over the typed data objects, extract their values and set
                 // the relationship objects for the next iteration of the outer loop
                 // over properties.
                 foreach ($row->_relationship_objects[$property_path] as $typed_data) {
                     if ($set_values) {
                         $row->{$combined_property_path}[] = Utility::extractFieldValues($typed_data);
                     }
                 }
                 // If we just set any field values on the result row, clean them up
                 // by merging them together (currently it's an array of arrays, but it
                 // should be just a flat array).
                 if ($set_values && $row->{$combined_property_path}) {
                     $row->{$combined_property_path} = call_user_func_array('array_merge', $row->{$combined_property_path});
                 }
             }
         }
     }
 }