/**
* Internal method for creating global or custom URL alias (these are handled in the same way).
*
* @throws \eZ\Publish\Core\Base\Exceptions\ForbiddenException if the path already exists for the given language
*
* @param string $action
* @param string $path
* @param bool $forward
* @param string|null $languageCode
* @param bool $alwaysAvailable
*
* @return \eZ\Publish\SPI\Persistence\Content\UrlAlias
*/
protected function createUrlAlias($action, $path, $forward, $languageCode, $alwaysAvailable)
{
$pathElements = explode('/', $path);
$topElement = array_pop($pathElements);
$languageId = $this->languageHandler->loadByLanguageCode($languageCode)->id;
$parentId = 0;
// Handle all path elements except topmost one
$isPathNew = false;
foreach ($pathElements as $level => $pathElement) {
$pathElement = $this->slugConverter->convert($pathElement, 'noname' . ($level + 1));
$pathElementMD5 = $this->getHash($pathElement);
if (!$isPathNew) {
$row = $this->gateway->loadRow($parentId, $pathElementMD5);
if (empty($row)) {
$isPathNew = true;
} else {
$parentId = $row['link'];
}
}
if ($isPathNew) {
$parentId = $this->insertNopEntry($parentId, $pathElement, $pathElementMD5);
}
}
// Handle topmost path element
$topElement = $this->slugConverter->convert($topElement, 'noname' . (count($pathElements) + 1));
// If last (next to topmost) entry parent is special root entry we handle topmost entry as first level entry
// That is why we need to reset $parentId to 0
if ($parentId != 0 && $this->gateway->isRootEntry($parentId)) {
$parentId = 0;
}
$topElementMD5 = $this->getHash($topElement);
// Set common values for two cases below
$data = array('action' => $action, 'is_alias' => 1, 'alias_redirects' => $forward ? 1 : 0, 'parent' => $parentId, 'text' => $topElement, 'text_md5' => $topElementMD5, 'is_original' => 1);
// Try to load topmost element
if (!$isPathNew) {
$row = $this->gateway->loadRow($parentId, $topElementMD5);
}
// If nothing was returned perform insert
if ($isPathNew || empty($row)) {
$data['lang_mask'] = $languageId | (int) $alwaysAvailable;
$id = $this->gateway->insertRow($data);
} elseif ($row['action'] == 'nop:' || $row['is_original'] == 0) {
// Row exists, check if it is reusable. There are 2 cases when this is possible:
// 1. NOP entry
// 2. history entry
$data['lang_mask'] = $languageId | (int) $alwaysAvailable;
// If history is reused move link to id
$data['link'] = $id = $row['id'];
$this->gateway->updateRow($parentId, $topElementMD5, $data);
} else {
throw new ForbiddenException("Path '%path%' already exists for the given language", ['%path%' => $path]);
}
$data['raw_path_data'] = $this->gateway->loadPathData($id);
return $this->mapper->extractUrlAliasFromData($data);
}