1<?php
2
3namespace Drupal\Core\Path;
4
5use Drupal\Core\Database\Connection;
6use Drupal\Core\Database\Query\Condition;
7use Drupal\Core\Database\Query\SelectInterface;
8use Drupal\Core\Language\LanguageInterface;
9
10/**
11 * Provides the default path alias lookup operations.
12 *
13 * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
14 *   \Drupal\path_alias\AliasRepository instead.
15 *
16 * @see https://www.drupal.org/node/3092086
17 */
18class AliasRepository implements AliasRepositoryInterface {
19
20  /**
21   * The database connection.
22   *
23   * @var \Drupal\Core\Database\Connection
24   */
25  protected $connection;
26
27  /**
28   * Constructs an AliasRepository object.
29   *
30   * @param \Drupal\Core\Database\Connection $connection
31   *   A database connection for reading and writing path aliases.
32   */
33  public function __construct(Connection $connection) {
34    $this->connection = $connection;
35
36    // This is used as base class by the new class, so we do not trigger
37    // deprecation notices when that or any child class is instantiated.
38    $new_class = 'Drupal\path_alias\AliasRepository';
39    if (!is_a($this, $new_class) && class_exists($new_class)) {
40      @trigger_error('The \\' . __CLASS__ . ' class is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead, use \\' . $new_class . '. See https://drupal.org/node/3092086', E_USER_DEPRECATED);
41    }
42  }
43
44  /**
45   * {@inheritdoc}
46   */
47  public function preloadPathAlias($preloaded, $langcode) {
48    $select = $this->getBaseQuery()
49      ->fields('base_table', ['path', 'alias']);
50
51    if (!empty($preloaded)) {
52      $conditions = new Condition('OR');
53      foreach ($preloaded as $preloaded_item) {
54        $conditions->condition('base_table.path', $this->connection->escapeLike($preloaded_item), 'LIKE');
55      }
56      $select->condition($conditions);
57    }
58
59    $this->addLanguageFallback($select, $langcode);
60
61    // We order by ID ASC so that fetchAllKeyed() returns the most recently
62    // created alias for each source. Subsequent queries using fetchField() must
63    // use ID DESC to have the same effect.
64    $select->orderBy('base_table.id', 'ASC');
65
66    return $select->execute()->fetchAllKeyed();
67  }
68
69  /**
70   * {@inheritdoc}
71   */
72  public function lookupBySystemPath($path, $langcode) {
73    // See the queries above. Use LIKE for case-insensitive matching.
74    $select = $this->getBaseQuery()
75      ->fields('base_table', ['id', 'path', 'alias', 'langcode'])
76      ->condition('base_table.path', $this->connection->escapeLike($path), 'LIKE');
77
78    $this->addLanguageFallback($select, $langcode);
79
80    $select->orderBy('base_table.id', 'DESC');
81
82    return $select->execute()->fetchAssoc() ?: NULL;
83  }
84
85  /**
86   * {@inheritdoc}
87   */
88  public function lookupByAlias($alias, $langcode) {
89    // See the queries above. Use LIKE for case-insensitive matching.
90    $select = $this->getBaseQuery()
91      ->fields('base_table', ['id', 'path', 'alias', 'langcode'])
92      ->condition('base_table.alias', $this->connection->escapeLike($alias), 'LIKE');
93
94    $this->addLanguageFallback($select, $langcode);
95
96    $select->orderBy('base_table.id', 'DESC');
97
98    return $select->execute()->fetchAssoc() ?: NULL;
99  }
100
101  /**
102   * {@inheritdoc}
103   */
104  public function pathHasMatchingAlias($initial_substring) {
105    $query = $this->getBaseQuery();
106    $query->addExpression(1);
107
108    return (bool) $query
109      ->condition('base_table.path', $this->connection->escapeLike($initial_substring) . '%', 'LIKE')
110      ->range(0, 1)
111      ->execute()
112      ->fetchField();
113  }
114
115  /**
116   * Returns a SELECT query for the path_alias base table.
117   *
118   * @return \Drupal\Core\Database\Query\SelectInterface
119   *   A Select query object.
120   */
121  protected function getBaseQuery() {
122    $query = $this->connection->select('path_alias', 'base_table');
123    $query->condition('base_table.status', 1);
124
125    return $query;
126  }
127
128  /**
129   * Adds path alias language fallback conditions to a select query object.
130   *
131   * @param \Drupal\Core\Database\Query\SelectInterface $query
132   *   A Select query object.
133   * @param string $langcode
134   *   Language code to search the path with. If there's no path defined for
135   *   that language it will search paths without language.
136   */
137  protected function addLanguageFallback(SelectInterface $query, $langcode) {
138    // Always get the language-specific alias before the language-neutral one.
139    // For example 'de' is less than 'und' so the order needs to be ASC, while
140    // 'xx-lolspeak' is more than 'und' so the order needs to be DESC.
141    $langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED];
142    if ($langcode === LanguageInterface::LANGCODE_NOT_SPECIFIED) {
143      array_pop($langcode_list);
144    }
145    elseif ($langcode > LanguageInterface::LANGCODE_NOT_SPECIFIED) {
146      $query->orderBy('base_table.langcode', 'DESC');
147    }
148    else {
149      $query->orderBy('base_table.langcode', 'ASC');
150    }
151    $query->condition('base_table.langcode', $langcode_list, 'IN');
152  }
153
154}
155