/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); /** @var $index \Drupal\search_api\IndexInterface */ $index = $this->getEntity(); $index->setOptions($form_state->getValue('options', array()) + $this->originalEntity->getOptions()); $datasources = $form_state->getValue('datasources', array()); /** @var \Drupal\search_api\Datasource\DatasourceInterface[] $datasource_plugins */ $datasource_plugins = $this->originalEntity->getDatasources(FALSE); $datasource_settings = array(); foreach ($datasources as $datasource_id) { $datasource = $datasource_plugins[$datasource_id]; $datasource_form = !empty($form['datasource_configs'][$datasource_id]) ? $form['datasource_configs'][$datasource_id] : array(); $datasource_form_state = new SubFormState($form_state, array('datasource_configs', $datasource_id)); $datasource->submitConfigurationForm($datasource_form, $datasource_form_state); $datasource_settings[$datasource_id] = $datasource; } $index->setDatasources($datasource_settings); // Call submitConfigurationForm() for the (possibly new) tracker. // @todo It seems if we change the tracker, we would validate/submit the old // tracker's form using the new tracker. Shouldn't be done, of course. // Similar above for datasources, though there of course the values will // just always be empty (because datasources have their plugin ID in the // form structure). $tracker_id = $form_state->getValue('tracker', NULL); if ($this->originalEntity->getTrackerId() == $tracker_id) { $tracker = $this->originalEntity->getTrackerInstance(); } else { $tracker = $this->trackerPluginManager->createInstance($tracker_id, array('index' => $this->originalEntity)); } $tracker_form_state = new SubFormState($form_state, array('tracker_config')); $tracker->submitConfigurationForm($form['tracker_config'], $tracker_form_state); $index->setTracker($tracker); }
/** * Tests a tracker with a dependency that gets removed. * * @param bool $remove_dependency * Whether to remove the dependency from the tracker when the object * depended on is deleted. * * @dataProvider dependencyTestDataProvider */ public function testTrackerDependency($remove_dependency) { // Set the tracker for the index and save it. The tracker configuration // contains the dependencies it will return – in our case, we use the test // server. $dependency_key = $this->dependency->getConfigDependencyKey(); $dependency_name = $this->dependency->getConfigDependencyName(); $tracker = \Drupal::getContainer()->get('plugin.manager.search_api.tracker')->createInstance('search_api_test_dependencies', array($dependency_key => array($dependency_name))); $this->index->setTracker($tracker); $this->index->save(); // Check the dependencies were calculated correctly. $dependencies = $this->index->getDependencies(); $this->assertContains($dependency_name, $dependencies[$dependency_key], 'Tracker dependency correctly inserted'); // Set our magic state key to let the test plugin know whether the // dependency should be removed or not. See // \Drupal\search_api_test_dependencies\Plugin\search_api\tracker\TestTracker::onDependencyRemoval(). $key = 'search_api_test_dependencies.tracker.remove'; \Drupal::state()->set($key, $remove_dependency); // If the index resets the tracker, it needs to know the ID of the default // tracker. if (!$remove_dependency) { \Drupal::configFactory()->getEditable('search_api.settings')->set('default_tracker', 'default')->save(); } // Delete the tracker's dependency. $this->dependency->delete(); // Reload the index and check it's still there. $this->reloadIndex(); $this->assertInstanceOf('Drupal\\search_api\\IndexInterface', $this->index, 'Index not removed'); // Make sure the dependency has been removed, one way or the other. $dependencies = $this->index->getDependencies(); $dependencies += array($dependency_key => array()); $this->assertNotContains($dependency_name, $dependencies[$dependency_key], 'Tracker dependency removed from index'); // Depending on whether the plugin should have removed the dependency or // not, make sure the right action was taken. $tracker_instance = $this->index->getTrackerInstance(); $tracker_id = $tracker_instance->getPluginId(); $tracker_config = $tracker_instance->getConfiguration(); if ($remove_dependency) { $this->assertEquals('search_api_test_dependencies', $tracker_id, 'Tracker not reset'); $this->assertEmpty($tracker_config, 'Tracker settings adapted'); } else { $this->assertEquals('default', $tracker_id, 'Tracker was reset'); $this->assertEmpty($tracker_config, 'Tracker settings were cleared'); } }
/** * Processes an index batch operation. * * @param \Drupal\search_api\IndexInterface $index * The index on which items should be indexed. * @param int $batch_size * The maximum number of items to index per batch pass. * @param int $limit * The maximum number of items to index in total, or -1 to index all items. * @param array $context * The current batch context, as defined in the * @link batch Batch operations @endlink documentation. */ public static function process(IndexInterface $index, $batch_size, $limit, array &$context) { // Check if the sandbox should be initialized. if (!isset($context['sandbox']['limit'])) { // Initialize the sandbox with data which is shared among the batch runs. $context['sandbox']['limit'] = $limit; $context['sandbox']['batch_size'] = $batch_size; $context['sandbox']['progress'] = 0; } // Check if the results should be initialized. if (!isset($context['results']['indexed'])) { // Initialize the results with data which is shared among the batch runs. $context['results']['indexed'] = 0; $context['results']['not indexed'] = 0; } // Get the remaining item count. When no valid tracker is available then // the value will be set to zero which will cause the batch process to // stop. $remaining_item_count = $index->hasValidTracker() ? $index->getTrackerInstance()->getRemainingItemsCount() : 0; // Check if an explicit limit needs to be used. if ($context['sandbox']['limit'] > -1) { // Calculate the remaining amount of items that can be indexed. Note that // a minimum is taking between the allowed number of items and the // remaining item count to prevent incorrect reporting of not indexed // items. $actual_limit = min($context['sandbox']['limit'] - $context['sandbox']['progress'], $remaining_item_count); } else { // Use the remaining item count as actual limit. $actual_limit = $remaining_item_count; } // Store original count of items to be indexed to show progress properly. if (empty($context['sandbox']['original_item_count'])) { $context['sandbox']['original_item_count'] = min($remaining_item_count, $actual_limit); } // Determine the number of items to index for this run. $to_index = min($actual_limit, $context['sandbox']['batch_size']); // Catch any exception that may occur during indexing. try { // Index items limited by the given count. $indexed = $index->indexItems($to_index); // Increment the indexed result and progress. $context['results']['indexed'] += $indexed; $context['results']['not indexed'] += $to_index - $indexed; $context['sandbox']['progress'] += $to_index; // Display progress message. if ($indexed > 0) { $context['message'] = static::formatPlural($context['results']['indexed'], 'Successfully indexed 1 item.', 'Successfully indexed @count items.'); } // Everything has been indexed? if ($indexed === 0 || $context['sandbox']['progress'] >= $context['sandbox']['original_item_count']) { $context['finished'] = 1; } else { $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['original_item_count']; } } catch (\Exception $ex) { // Log exception to watchdog and abort the batch job. watchdog_exception('search_api', $ex); $context['message'] = static::t('An error occurred during indexing: @message', array('@message' => $ex->getMessage())); $context['finished'] = 1; $context['results']['not indexed'] += $context['sandbox']['limit'] - $context['sandbox']['progress']; } }
/** * {@inheritdoc} */ public function getTrackerInstance() { return $this->entity->getTrackerInstance(); }
/** * Tests translation handling of the content entity datasource. */ public function testItemTranslations() { // Test retrieving language and translations when no translations are // available. /** @var \Drupal\entity_test\Entity\EntityTestMul $entity_1 */ $entity_1 = EntityTestMul::create(array('id' => 1, 'name' => 'test 1', 'user_id' => $this->container->get('current_user')->id())); $entity_1->save(); $this->assertEquals('en', $entity_1->language()->getId(), new FormattableMarkup('%entity_type: Entity language set to site default.', array('%entity_type' => $this->testEntityTypeId))); $this->assertFalse($entity_1->getTranslationLanguages(FALSE), new FormattableMarkup('%entity_type: No translations are available', array('%entity_type' => $this->testEntityTypeId))); /** @var \Drupal\entity_test\Entity\EntityTestMul $entity_2 */ $entity_2 = EntityTestMul::create(array('id' => 2, 'name' => 'test 2', 'user_id' => $this->container->get('current_user')->id())); $entity_2->save(); $this->assertEquals('en', $entity_2->language()->getId(), new FormattableMarkup('%entity_type: Entity language set to site default.', array('%entity_type' => $this->testEntityTypeId))); $this->assertFalse($entity_2->getTranslationLanguages(FALSE), new FormattableMarkup('%entity_type: No translations are available', array('%entity_type' => $this->testEntityTypeId))); // Test that the datasource returns the correct item IDs. $datasource = $this->index->getDatasource('entity:' . $this->testEntityTypeId); $datasource_item_ids = $datasource->getItemIds(); sort($datasource_item_ids); $expected = array('1:en', '2:en'); $this->assertEquals($expected, $datasource_item_ids, 'Datasource returns correct item ids.'); // Test indexing the new entity. $this->assertEquals(0, $this->index->getTrackerInstance()->getIndexedItemsCount(), 'The index is empty.'); $this->assertEquals(2, $this->index->getTrackerInstance()->getTotalItemsCount(), 'There are two items to be indexed.'); $this->index->indexItems(); $this->assertEquals(2, $this->index->getTrackerInstance()->getIndexedItemsCount(), 'Two items have been indexed.'); // Now, make the first entity language-specific by assigning a language. $default_langcode = $this->langcodes[0]; $entity_1->get('langcode')->setValue($default_langcode); $entity_1->save(); $this->assertEquals(\Drupal::languageManager()->getLanguage($this->langcodes[0]), $entity_1->language(), new FormattableMarkup('%entity_type: Entity language retrieved.', array('%entity_type' => $this->testEntityTypeId))); $this->assertFalse($entity_1->getTranslationLanguages(FALSE), new FormattableMarkup('%entity_type: No translations are available', array('%entity_type' => $this->testEntityTypeId))); // Test that the datasource returns the correct item IDs. $datasource_item_ids = $datasource->getItemIds(); sort($datasource_item_ids); $expected = array('1:' . $this->langcodes[0], '2:en'); $this->assertEquals($expected, $datasource_item_ids, 'Datasource returns correct item ids.'); // Test that the index needs to be updated. $this->assertEquals(1, $this->index->getTrackerInstance()->getIndexedItemsCount(), 'The updated item needs to be reindexed.'); $this->assertEquals(2, $this->index->getTrackerInstance()->getTotalItemsCount(), 'There are two items in total.'); // Set two translations for the first entity and test that the datasource // returns three separate item IDs, one for each translation. $translation = $entity_1->addTranslation($this->langcodes[1]); $translation->save(); $translation = $entity_1->addTranslation($this->langcodes[2]); $translation->save(); $this->assertTrue($entity_1->getTranslationLanguages(FALSE), new FormattableMarkup('%entity_type: Translations are available', array('%entity_type' => $this->testEntityTypeId))); $datasource_item_ids = $datasource->getItemIds(); sort($datasource_item_ids); $expected = array('1:' . $this->langcodes[0], '1:' . $this->langcodes[1], '1:' . $this->langcodes[2], '2:en'); $this->assertEquals($expected, $datasource_item_ids, 'Datasource returns correct item ids for a translated entity.'); // Test that the index needs to be updated. $this->assertEquals(1, $this->index->getTrackerInstance()->getIndexedItemsCount(), 'The updated items needs to be reindexed.'); $this->assertEquals(4, $this->index->getTrackerInstance()->getTotalItemsCount(), 'There are four items in total.'); // Delete one translation and test that the datasource returns only three // items. $entity_1->removeTranslation($this->langcodes[2]); $entity_1->save(); $datasource_item_ids = $datasource->getItemIds(); sort($datasource_item_ids); $expected = array('1:' . $this->langcodes[0], '1:' . $this->langcodes[1], '2:en'); $this->assertEquals($expected, $datasource_item_ids, 'Datasource returns correct item ids for a translated entity.'); // Test reindexing. $this->assertEquals(3, $this->index->getTrackerInstance()->getTotalItemsCount(), 'There are three items in total.'); $this->assertEquals(1, $this->index->getTrackerInstance()->getIndexedItemsCount(), 'The updated items needs to be reindexed.'); $this->index->indexItems(); $this->assertEquals(3, $this->index->getTrackerInstance()->getIndexedItemsCount(), 'Three items are indexed.'); }