Drupal 8: дефолтный поиск и типы содержимого

В Drupal 8 у дефолтного поиска отсутствует возможность исключить из индексации тот или иной тип содержимого. Иссуй есть, а возможности — нет.

Можно сразу удариться в Search API, но для небольших сайтов может быть проще добавить ограничение по типам. Это не так уж трудно. Есть способ, подразумевающий вставку условий через хук query_alter по тегу search_node_search, добавляемому в поисковые запросы. Правда, тогда материалы не будут выводиться, а индексироваться — будут.

Но можно и запретить индексацию. Посколько D8 весь оопнутый и прогрессивный, отбросим теплые ламповые хуки и сделаем свой нанотехнологичный плагин поиска, унаследовав его от дефолтного NodeSearch.

Для этого создадим новый класс в нашем модуле в mymodule/src/Plugin/Search/NodeRestrictedSearch и наследуем его от уже существующего плагина. Это удобно, не нужно копировать ничего лишнего, достаточно добавить новое свойство, перечисляющее типы, которые мы ограничиваем (в данном примере это ноды типа store и secret_content):

<?php

namespace Drupal\mymodule\Plugin\Search;

use Drupal\Core\Access\AccessibleInterface;
use Drupal\node\Plugin\Search\NodeSearch;
use Drupal\search\Plugin\SearchIndexingInterface;

/**
 * @SearchPlugin(
 *   id = "node_restricted_search",
 *   title = @Translation("Content Restricted")
 * )
 */
class NodeRestrictedSearch extends NodeSearch implements AccessibleInterface, SearchIndexingInterface {

  protected $restrictedTypes = ['store', 'secret_content'];

И переопределить пару методов. Во-первых, метод, отбирающий ноды для индексации — мы скопируем код метода из родителя и добавим проверку типа материала:

  /**
   * {@inheritdoc}
   */
  public function updateIndex() {
    // Interpret the cron limit setting as the maximum number of nodes to index
    // per cron run.
    $limit = (int) $this->searchSettings->get('index.cron_limit');

    $query = $this->database->select('node', 'n', ['target' => 'replica']);
    $query->addField('n', 'nid');
    $query->leftJoin('search_dataset', 'sd', 'sd.sid = n.nid AND sd.type = :type', [':type' => $this->getPluginId()]);
    $query->addExpression('CASE MAX(sd.reindex) WHEN NULL THEN 0 ELSE 1 END', 'ex');
    $query->addExpression('MAX(sd.reindex)', 'ex2');
    $query->condition(
      $query->orConditionGroup()
        ->where('sd.sid IS NULL')
        ->condition('sd.reindex', 0, '<>')
    );
    // ПРОВЕРКА ТИПА:
    $query->condition('n.type', $this->restrictedTypes, 'NOT IN');
    $query->orderBy('ex', 'DESC')
      ->orderBy('ex2')
      ->orderBy('n.nid')
      ->groupBy('n.nid')
      ->range(0, $limit);

    $nids = $query->execute()->fetchCol();
    if (!$nids) {
      return;
    }

    $node_storage = $this->entityManager->getStorage('node');
    foreach ($node_storage->loadMultiple($nids) as $node) {
      $this->indexNode($node);
    }
  }

Во-вторых (для порядку), переопределим метод, который показывает в админке, сколько материалов проиндексировано и сколько осталось:

  /**
   * {@inheritdoc}
   */
  public function indexStatus() {
    $total = $this->database->select('node', 'n')
      ->fields('n', ['nid'])
      // ПРОВЕРКА ТИПА:
      ->condition('n.type', $this->restrictedTypes, 'NOT IN')
      ->countQuery()
      ->execute()
      ->fetchField();

    $query = $this->database->select('node', 'n');
    $query->distinct()
      ->fields('n', ['nid'])
      ->leftJoin('search_dataset', 'sd', 'sd.sid = n.nid AND sd.type = :type', [':type' => $this->getPluginId()]);
    // ПРОВЕРКА ТИПА:
    $query->condition('n.type', $this->restrictedTypes, 'NOT IN');
    $query->condition(
      $query->orConditionGroup()
        ->where('sd.sid IS NULL')
        ->condition('sd.reindex', 0, '<>')
    );
    $remaining = $query->countQuery()->execute()->fetchField();
    return ['remaining' => $remaining, 'total' => $total];
  }

Останется только почистить кэш, пройти в админку, добавить поисковую страницу с новым плагином и отключить старый.

Комментарии