api-8.x-1.x-dev/src/Controller/SearchController.php

src/Controller/SearchController.php
<?php

namespace Drupal\api\Controller;

use Drupal\Component\Utility\Xss;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Link;
use Drupal\Core\Path\PathValidatorInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\api\Entity\Branch;
use Drupal\api\Entity\DocBlock;
use Drupal\api\Form\SearchForm;
use Drupal\api\Formatter;
use Drupal\api\Traits\RouteElementsTrait;
use Drupal\api\Utilities;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Search pages.
 *
 * @package Drupal\api\Controller
 */
class SearchController extends ControllerBase {

  use RouteElementsTrait;

  /**
   * Utilities service.
   *
   * @var \Drupal\api\Utilities
   */
  protected $utilities;

  /**
   * The path validator.
   *
   * @var \Drupal\Core\Path\PathValidatorInterface
   */
  protected $pathValidator;

  /**
   * Construct method.
   *
   * @param \Drupal\api\Utilities $utilities
   *   Utilities service.
   * @param \Drupal\Core\Path\PathValidatorInterface $path_validator
   *   The path validator.
   */
  public function __construct(Utilities $utilities, PathValidatorInterface $path_validator) {
    $this->utilities = $utilities;
    $this->pathValidator = $path_validator;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('api.utilities'),
      $container->get('path.validator')
    );
  }

  /**
   * If this is actually a not found exception, get the term from the URI.
   *
   * @param string $term
   *   Original term passed to the route, most likely empty.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   Request object.
   *
   * @return string
   *   Original term or URI.
   */
  protected function extractTermIfNotFoundRequest($term, Request $request) {
    if (empty($term) && !empty($request->attributes->get('exception'))) {
      if ($request->attributes->get('exception') instanceof NotFoundHttpException) {
        // Let the search decide the destination, so remove the param.
        $request->query->remove('destination');
        // Term will be the URI now.
        $term = trim($request->getRequestUri(), '/' . base_path());
      }
    }

    return $term;
  }

  /**
   * Global search route.
   *
   * @param string $term
   *   Search term.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   Request object.
   *
   * @return \Symfony\Component\HttpFoundation\Response
   *   Response object.
   */
  public function globalSearchRedirect($term, Request $request) {
    // In case this route is hooked up to the 404 handler.
    $term = $this->extractTermIfNotFoundRequest($term, $request);
    $term = Xss::filter($term);
    if (empty($term)) {
      return $this->redirect('api.search.default');
    }

    // Try exact match.
    $matches = DocBlock::searchByTitle($term, NULL, 1, TRUE);
    if (empty($matches)) {
      // Try partial match.
      $matches = DocBlock::searchByTitle($term, NULL, 1);
    }

    if (!empty($matches)) {
      $match_id = array_shift($matches);
      $match = DocBlock::load($match_id);

      return $this->redirect('api.search.project.branch.term', [
        'branch' => $match->getBranch()->getSlug(),
        'project' => $match->getBranch()->getProject()->getSlug(),
        'term' => $term,
      ]);
    }

    // If it is a path, it is very likely that the element no longer exist on
    // the supported branch or that it moved location. We cannot do much other
    // than offering guidance in the message.
    if (str_contains($term, '/')) {
      $this->messenger()->addMessage($this->t('Sorry, the path %term cannot be matched against any page in this branch. Try searching by one of the components of the path.', [
        '%term' => $term,
      ]));
    }
    // If none found (and it is not a path) then redirect to branch search page.
    else {
      $this->messenger()->addMessage($this->t('Sorry, %term cannot be found.', [
        '%term' => $term,
      ]));
    }
    return $this->defaultsRedirect($request);
  }

  /**
   * Endpoint with no project or branch, so just redirect to defaults.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   Request object.
   *
   * @return \Symfony\Component\HttpFoundation\RedirectResponse
   *   Redirect to the right page.
   */
  public function defaultsRedirect(Request $request) {
    $branch = $this->utilities->getDefaultBranchProject();
    if (!$branch) {
      $this->messenger()->addMessage($this->t('Default project and branch is not set.'));
      return $this->redirect('api.projects');
    }
    $project = $branch->getProject();
    return $this->redirect('api.search.project.branch', [
      'branch' => $branch->getSlug(),
      'project' => $project->getSlug(),
    ]);
  }

  /**
   * Endpoint with branch and no project, so just redirect to the right route.
   *
   * @param string $branch
   *   Branch slug.
   * @param string $term
   *   Search term.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   Request object.
   *
   * @return \Symfony\Component\HttpFoundation\RedirectResponse
   *   Redirect to the right page.
   */
  public function branchSearchRedirect($branch, $term, Request $request) {
    $branch_ids = Branch::findBySlug($branch);
    if ($branch_ids) {
      $branch_id = array_shift($branch_ids);
      $branch = Branch::load($branch_id);
      $project = $branch->getProject();
      $term = Xss::filter($term);

      return $this->redirect('api.search.project.branch.term', [
        'branch' => $branch->getSlug(),
        'project' => $project->getSlug(),
        'term' => $term,
      ]);
    }

    throw new NotFoundHttpException();
  }

  /**
   * Redirects to either the module-defined search or the global search.
   *
   * @param string $term
   *   Term to search.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   Request object.
   *
   * @return \Symfony\Component\HttpFoundation\RedirectResponse
   *   Redirect to the right place.
   */
  public function moduleSearchRedirect($term, Request $request) {
    $term = Xss::filter($term);

    // Check if "/search/api" is a valid path (defined by sub-modules).
    if ($this->pathValidator->isValid('/search/api')) {
      $url = 'internal:/search/api?keys=' . $term;
      return new RedirectResponse(Url::fromUri($url)->toString());
    }

    // Otherwise redirect to the global redirect.
    return $this->redirect('api.search.global', [
      'term' => $term,
    ]);
  }

  /**
   * Search route given a project and branch that displays the search form.
   *
   * @param string $project
   *   Project slug.
   * @param string $branch
   *   Branch slug.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   Request object.
   *
   * @return array
   *   Render array.
   */
  public function searchFormPage($project, $branch, Request $request) {
    $project = $this->getProject($project, TRUE);
    $branch = $this->getBranch($branch, $project, TRUE);

    return [
      '#title' => $this->t('@project @branch', [
        '@project' => $project->getTitle(),
        '@branch' => $branch->getTitle(),
      ]),
      'form' => $this->formBuilder()->getForm(SearchForm::class, $branch),
    ];
  }

  /**
   * Search route given a project and branch that displays the search form.
   *
   * @param string $project
   *   Project slug.
   * @param string $branch
   *   Branch slug.
   * @param string $term
   *   Search term.
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   Request object.
   *
   * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
   *   Render array.
   */
  public function searchListingPage($project, $branch, $term, Request $request) {
    $project = $this->getProject($project, TRUE);
    $branch = $this->getBranch($branch, $project, TRUE);
    $term = Xss::filter($term);
    $exact_matches = NULL;

    // Try exact match and redirect if only one result.
    $matches = DocBlock::searchByTitle($term, $branch, 10, TRUE);
    if ($matches) {
      // Check for case sensitivity.
      $matches = DocBlock::loadMultiple($matches);
      // If we only have one match go directly there.
      if (count($matches) === 1) {
        foreach ($matches as $match) {
          $url = Formatter::objectUrl($match);
          if ($url) {
            return new RedirectResponse($url->toString());
          }
        }
      }
      // Multiple matches were found, build a table with the direct matches.
      else {
        $rows = [];
        $i = 0;
        foreach ($matches as $match) {
          /** @var \Drupal\api\Entity\DocBlock $match */
          $url = Formatter::objectUrl($match);
          if ($url) {
            // Nice looking file render.
            $file_docblock_id = DocBlock::findFileByFileName($match->getFileName(), $branch);
            $link_to_file = NULL;
            if ($file_docblock_id) {
              $file_entity = DocBlock::load($file_docblock_id);
              // phpcs:ignore
              $link_to_file = new TranslatableMarkup(Formatter::linkFile($file_entity));
            }

            $rows[] = [
              'data' => [
                ['data' => Link::fromTextAndUrl($match->getTitle(), $url)],
                ['data' => $match->getObjectType()],
                ['data' => $link_to_file ?? $match->getFileName()],
                // phpcs:ignore
                ['data' => new TranslatableMarkup($match->getSummary() ?? '')],
              ],
              'class' => ($i++ % 2 == 0) ? 'odd' : 'even',
            ];
          }
        }

        // Table matching the style of the view with all results.
        $exact_matches = [
          '#theme' => 'table',
          '#caption' => $this->t('These are exact matches according to your search'),
          '#prefix' => $this->t('<h3>Exact matches</h3>'),
          '#suffix' => '<hr>' . $this->t('<h3>All search results</h3>'),
          '#header' => [
            ['data' => $this->t('Title')],
            ['data' => $this->t('Object type')],
            ['data' => $this->t('File name')],
            ['data' => $this->t('Summary')],
          ],
          '#rows' => $rows,
        ];
      }
    }

    // If we get here, there were multiple or zero matches.
    return [
      '#title' => $this->t('Search for <em>@term</em>', [
        '@term' => $term,
      ]),
      'search_links' => Formatter::searchLinks($term, $branch),
      'exact_matches' => $exact_matches,
      'view' => views_embed_view('api_search', 'block_search_results', $branch->id(), $term),
    ];
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc