3 namespace Drupal\pathauto;
5 use Drupal\Component\Utility\Unicode;
6 use Drupal\Core\Config\ConfigFactoryInterface;
7 use Drupal\Core\Extension\ModuleHandlerInterface;
8 use Drupal\Core\Language\LanguageInterface;
9 use Drupal\Core\Path\AliasManagerInterface;
10 use Drupal\Core\Routing\RouteProviderInterface;
13 * Provides a utility for creating a unique path alias.
15 class AliasUniquifier implements AliasUniquifierInterface {
20 * @var \Drupal\Core\Config\ConfigFactoryInterface
22 protected $configFactory;
25 * The alias storage helper.
27 * @var \Drupal\pathauto\AliasStorageHelperInterface
29 protected $aliasStorageHelper;
34 * @var \Drupal\Core\Extension\ModuleHandlerInterface
36 protected $moduleHandler;
39 * The route provider service.
41 * @var \Drupal\Core\Routing\RouteProviderInterface
43 protected $routeProvider;
48 * @var \Drupal\Core\Path\AliasManagerInterface
50 protected $aliasManager;
53 * Creates a new AliasUniquifier.
55 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
57 * @param \Drupal\pathauto\AliasStorageHelperInterface $alias_storage_helper
58 * The alias storage helper.
59 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
61 * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
62 * The route provider service.
63 * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
66 public function __construct(ConfigFactoryInterface $config_factory, AliasStorageHelperInterface $alias_storage_helper, ModuleHandlerInterface $module_handler, RouteProviderInterface $route_provider, AliasManagerInterface $alias_manager) {
67 $this->configFactory = $config_factory;
68 $this->aliasStorageHelper = $alias_storage_helper;
69 $this->moduleHandler = $module_handler;
70 $this->routeProvider = $route_provider;
71 $this->aliasManager = $alias_manager;
77 public function uniquify(&$alias, $source, $langcode) {
78 $config = $this->configFactory->get('pathauto.settings');
80 if (!$this->isReserved($alias, $source, $langcode)) {
84 // If the alias already exists, generate a new, hopefully unique, variant.
85 $maxlength = min($config->get('max_length'), $this->aliasStorageHelper->getAliasSchemaMaxlength());
86 $separator = $config->get('separator');
87 $original_alias = $alias;
91 // Append an incrementing numeric suffix until we find a unique alias.
92 $unique_suffix = $separator . $i;
93 $alias = Unicode::truncate($original_alias, $maxlength - Unicode::strlen($unique_suffix), TRUE) . $unique_suffix;
95 } while ($this->isReserved($alias, $source, $langcode));
101 public function isReserved($alias, $source, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED) {
102 // Check if this alias already exists.
103 if ($existing_source = $this->aliasManager->getPathByAlias($alias, $langcode)) {
104 if ($existing_source != $alias) {
105 // If it is an alias for the provided source, it is allowed to keep using
106 // it. If not, then it is reserved.
107 return $existing_source != $source;
112 // Then check if there is a route with the same path.
113 if ($this->isRoute($alias)) {
116 // Finally check if any other modules have reserved the alias.
122 $implementations = $this->moduleHandler->getImplementations('pathauto_is_alias_reserved');
123 foreach ($implementations as $module) {
125 $result = $this->moduleHandler->invoke($module, 'pathauto_is_alias_reserved', $args);
127 if (!empty($result)) {
128 // As soon as the first module says that an alias is in fact reserved,
129 // then there is no point in checking the rest of the modules.
138 * Verify if the given path is a valid route.
140 * @param string $path
141 * A string containing a relative path.
144 * TRUE if the path already exists.
146 * @throws \InvalidArgumentException
148 public function isRoute($path) {
149 if (is_file(DRUPAL_ROOT . '/' . $path) || is_dir(DRUPAL_ROOT . '/' . $path)) {
150 // Do not allow existing files or directories to get assigned an automatic
151 // alias. Note that we do not need to use is_link() to check for symbolic
152 // links since this returns TRUE for either is_file() or is_dir() already.
156 $routes = $this->routeProvider->getRoutesByPattern($path);
158 // Only return true for an exact match, ignore placeholders.
159 foreach ($routes as $route) {
160 if ($route->getPath() == $path) {