Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Routing] Data Object Routes #175

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion config/services/adapter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ services:
tags:
- { name: i18n.adapter.path.generator, alias: document }

I18nBundle\Adapter\PathGenerator\DataObject:
parent: I18nBundle\Adapter\PathGenerator\AbstractPathGenerator
arguments:
- '@router'
- '@event_dispatcher'
tags:
- { name: i18n.adapter.path.generator, alias: data_object }


#
# Adapter: ReDirector
Expand All @@ -63,4 +71,4 @@ services:
I18nBundle\Adapter\Redirector\FallbackRedirector:
parent: I18nBundle\Adapter\Redirector\AbstractRedirector
tags:
- { name: i18n.adapter.redirector, alias: fallback, priority: 100 }
- { name: i18n.adapter.redirector, alias: fallback, priority: 100 }
63 changes: 63 additions & 0 deletions src/Adapter/PathGenerator/DataObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

/**
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - DACHCOM Commercial License (DCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) DACHCOM.DIGITAL AG (https://www.dachcom-digital.com)
* @license GPLv3 and DCL
*/

namespace I18nBundle\Adapter\PathGenerator;

use I18nBundle\Context\I18nContextInterface;
use I18nBundle\I18nEvents;
use I18nBundle\Model\RouteItem\AlternateRouteItemInterface;
use I18nBundle\Model\RouteItem\RouteItemInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

class DataObject extends DynamicRoute
{
public function __construct(
protected RouterInterface $router,
protected EventDispatcherInterface $eventDispatcher
) {}

public function getUrls(I18nContextInterface $i18nContext, bool $onlyShowRootLanguages = false): array
{
return $this->buildAlternateRoutesStack(
$i18nContext,
RouteItemInterface::DATA_OBJECT_ROUTE,
I18nEvents::PATH_ALTERNATE_DATA_OBJECT_ROUTE
);
}

/**
* @throws \Exception
*/
protected function generateLink(AlternateRouteItemInterface $routeItem): string
{
$routeParameters = $this->alternateRouteItemTransformer->reverseTransformToArray($routeItem, [
'type' => RouteItemInterface::DATA_OBJECT_ROUTE,
]);

if ($routeItem->getEntity() === null || $routeItem->getUrlSlug() === null) {
throw new \RuntimeException(
'Cannot create Data Object route URL. Object and/or UrlSlug is missing!'
);
}

return $this->router->generate(
$routeItem->getRouteName(),
$routeParameters,
UrlGeneratorInterface::ABSOLUTE_URL
);
}
}
4 changes: 4 additions & 0 deletions src/Builder/RouteItemBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ public function buildRouteItemByRequest(Request $baseRequest, ?Document $baseDoc
$routeItem = $this->routeItemFactory->create(RouteItemInterface::STATIC_ROUTE, false);
} elseif (str_starts_with($currentRouteName, 'document_')) {
$routeItem = $this->routeItemFactory->create(RouteItemInterface::DOCUMENT_ROUTE, false);
} elseif (str_starts_with($currentRouteName, 'data_object_')) {
$routeItem = $this->routeItemFactory->create(RouteItemInterface::DATA_OBJECT_ROUTE, true);
$routeItem->setEntity($baseRequest->attributes->get('object'));
$routeItem->setUrlSlug($baseRequest->attributes->get('urlSlug'));
} elseif ($baseRequest->attributes->has(Definitions::ATTRIBUTE_I18N_ROUTE_IDENTIFIER)) {
$routeItem = $this->routeItemFactory->create(RouteItemInterface::SYMFONY_ROUTE, false);
}
Expand Down
1 change: 1 addition & 0 deletions src/I18nEvents.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ final class I18nEvents
public const CONTEXT_SWITCH = 'i18n.switch';
public const PATH_ALTERNATE_STATIC_ROUTE = 'i18n.path.static_route.alternate';
public const PATH_ALTERNATE_SYMFONY_ROUTE = 'i18n.path.symfony_route.alternate';
public const PATH_ALTERNATE_DATA_OBJECT_ROUTE = 'i18n.path.data_object_route.alternate';
public const PREVIEW_CONFIG_GENERATION = 'i18n.preview.config_generation';
public const PREVIEW_URL_GENERATION = 'i18n.preview.url_generation';
}
13 changes: 13 additions & 0 deletions src/Model/RouteItem/BaseRouteItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace I18nBundle\Model\RouteItem;

use Pimcore\Model\DataObject\Data\UrlSlug;
use Pimcore\Model\Element\ElementInterface;
use Symfony\Component\HttpFoundation\ParameterBag;

Expand All @@ -25,13 +26,15 @@ abstract class BaseRouteItem
protected ParameterBag $routeParameters;
protected ParameterBag $routeAttributes;
protected ParameterBag $routeContext;
protected ?UrlSlug $urlSlug = null;

public function __construct(string $type, bool $headless)
{
if (!in_array($type, [
RouteItemInterface::DOCUMENT_ROUTE,
RouteItemInterface::SYMFONY_ROUTE,
RouteItemInterface::STATIC_ROUTE,
RouteItemInterface::DATA_OBJECT_ROUTE,
], true)) {
throw new \Exception(sprintf('Invalid RouteItem type "%s"', $type));
}
Expand Down Expand Up @@ -127,4 +130,14 @@ public function setRouteName(?string $routeName): void
{
$this->routeName = $routeName;
}

public function getUrlSlug(): ?UrlSlug
{
return $this->urlSlug;
}

public function setUrlSlug(?UrlSlug $urlSlug): void
{
$this->urlSlug = $urlSlug;
}
}
5 changes: 5 additions & 0 deletions src/Model/RouteItem/BaseRouteItemInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace I18nBundle\Model\RouteItem;

use Pimcore\Model\DataObject\Data\UrlSlug;
use Pimcore\Model\Element\ElementInterface;
use Symfony\Component\HttpFoundation\ParameterBag;

Expand Down Expand Up @@ -51,4 +52,8 @@ public function hasRouteName(): bool;
public function getRouteName(): ?string;

public function setRouteName(?string $routeName): void;

public function getUrlSlug(): ?UrlSlug;

public function setUrlSlug(?UrlSlug $urlSlug): void;
}
1 change: 1 addition & 0 deletions src/Model/RouteItem/RouteItemInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ interface RouteItemInterface extends BaseRouteItemInterface
public const STATIC_ROUTE = 'static_route';
public const SYMFONY_ROUTE = 'symfony';
public const DOCUMENT_ROUTE = 'document';
public const DATA_OBJECT_ROUTE = 'data_object';
}
76 changes: 64 additions & 12 deletions src/Modifier/RouteModifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use I18nBundle\LinkGenerator\I18nLinkGeneratorInterface;
use I18nBundle\Manager\I18nContextManager;
use I18nBundle\Model\RouteItem\RouteItemInterface;
use I18nBundle\Model\SiteRequestContext;
use I18nBundle\Model\ZoneInterface;
use I18nBundle\Model\ZoneSiteInterface;
use I18nBundle\Tool\System;
Expand Down Expand Up @@ -192,23 +193,53 @@ public function buildDocumentPath(I18nContextInterface $i18nContext, int $refere
return $documentPath;
}

$scheme = $zoneSite->getSiteRequestContext()->getScheme();
$host = $zoneSite->getSiteRequestContext()->getHost();
$httpPort = $zoneSite->getSiteRequestContext()->getHttpPort();
$httpsPort = $zoneSite->getSiteRequestContext()->getHttpsPort();
return $this->generateAbsoluteUrl($zoneSite->getSiteRequestContext(), $documentPath);
}

$port = '';
if ($scheme === 'http' && $httpPort !== 80) {
$port = ':' . $httpPort;
} elseif ($scheme === 'https' && $httpsPort !== 443) {
$port = ':' . $httpsPort;
/**
* @throws \Exception
*/
public function buildDataObjectPath(I18nContextInterface $i18nContext, int $referenceType): string
{
$routeItem = $i18nContext->getRouteItem();
$attributes = $routeItem->isHeadless()
? $routeItem->getRouteParameters()
: $routeItem->getRouteAttributes();
$object = $routeItem->getEntity();
$urlSlug = $routeItem->getUrlSlug();

if (!$object instanceof Concrete) {
throw new \RuntimeException(
sprintf(
'Cannot build data object path. Entity must be an instance of "%s", "%s" given!',
Concrete::class,
get_class($object)
)
);
}

if (!empty($documentPath)) {
$documentPath = '/' . ltrim($documentPath, '/');
$slugs = $object->get($urlSlug->getFieldname(), $attributes['_locale']);

if (empty($slugs)) {
throw new \RuntimeException(
sprintf(
'No slugs found in object field "%s" for locale "%s"',
$urlSlug->getFieldname(),
$attributes['_locale']
)
);
}

return sprintf('%s://%s%s%s', $scheme, $host, $port, $documentPath);
$dataObjectPath = $slugs[0]->getSlug();

if ($referenceType !== UrlGeneratorInterface::ABSOLUTE_URL) {
return $dataObjectPath;
}

return $this->generateAbsoluteUrl(
$i18nContext->getCurrentZoneSite()->getSiteRequestContext(),
$dataObjectPath
);
}

protected function translateDynamicRouteKey(ZoneInterface $zone, RouteItemInterface $routeItem, string $key, string $locale): string
Expand Down Expand Up @@ -267,4 +298,25 @@ protected function validateRouteParameters(string $name, string $routeType, arra

return $parameters;
}

private function generateAbsoluteUrl(SiteRequestContext $siteRequestContext, string $path): string
{
$scheme = $siteRequestContext->getScheme();
$host = $siteRequestContext->getHost();
$httpPort = $siteRequestContext->getHttpPort();
$httpsPort = $siteRequestContext->getHttpsPort();

$port = '';
if ($scheme === 'http' && $httpPort !== 80) {
$port = ':' . $httpPort;
} elseif ($scheme === 'https' && $httpsPort !== 443) {
$port = ':' . $httpsPort;
}

if (!empty($path)) {
$path = '/' . ltrim($path, '/');
}

return sprintf('%s://%s%s%s', $scheme, $host, $port, $path);
}
}
9 changes: 9 additions & 0 deletions src/Routing/I18nRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ public function generate($name, $parameters = [], $referenceType = self::ABSOLUT
return $this->generateDocumentRoute($i18nContext, $referenceType);
}

if ($i18nContext->getRouteItem()->getType() === RouteItemInterface::DATA_OBJECT_ROUTE) {
return $this->generateDataObjectRoute($i18nContext, $referenceType);
}

throw new RouteNotFoundException(sprintf('None of the chained routers were able to generate i18n route: %s', $name));
}

Expand Down Expand Up @@ -141,6 +145,11 @@ protected function generateDocumentRoute(I18nContextInterface $i18nContext, int
return $this->routeModifier->buildDocumentPath($i18nContext, $referenceType);
}

protected function generateDataObjectRoute(I18nContextInterface $i18nContext, int $referenceType): string
{
return $this->routeModifier->buildDataObjectPath($i18nContext, $referenceType);
}

protected function generateContextAwarePath(I18nContextInterface $i18nContext, RouteItemInterface $routeItem, int $referenceType): string
{
$this->buildRouteContext($i18nContext, $referenceType);
Expand Down
10 changes: 9 additions & 1 deletion src/Transformer/AlternateRouteItemTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ public function transform(RouteItemInterface $routeItem, array $context = []): A
$alternateRouteItem->getRouteParametersBag()->set('_locale', $zoneSite->getLocale());
$alternateRouteItem->getRouteContextBag()->set('site', $zoneSite->getPimcoreSite());

// Data object routes always have a route name, entity and url slug assigned
if ($routeItem->getType() === RouteItemInterface::DATA_OBJECT_ROUTE) {
$alternateRouteItem->setRouteName($routeItem->getRouteName());
$alternateRouteItem->setEntity($routeItem->getEntity());
$alternateRouteItem->setUrlSlug($routeItem->getUrlSlug());
}

return $alternateRouteItem;
}

Expand Down Expand Up @@ -66,7 +73,8 @@ public function reverseTransformToArray(mixed $transformedRouteItem, array $cont
'routeName' => $transformedRouteItem->getRouteName(),
'routeParameters' => $transformedRouteItem->getRouteParameters(),
'routeAttributes' => $transformedRouteItem->getRouteAttributes(),
'context' => $transformedRouteItem->getRouteContext()
'context' => $transformedRouteItem->getRouteContext(),
'urlSlug' => $transformedRouteItem->getUrlSlug(),
]
];
}
Expand Down
Loading