Disclaimer
This answer provides a solution to the question of routing requests to subdomains to a specific RequestHandler. However, is not a good practice, since it introduces difficulties like the overhead in routes configuration and maintaining two routes configurations. In addition, it does not solve the subdomain-specific path routing (e.g. http://journal.example.com/new or http://journal.example.com/list).
Using the same application across various subdomains can mean challenges in session handling and so forth. Using the same domain for one application makes these issues obsolete.
Possible solution
Since routes are not aware of the host, a plain solution in the routes configuration is not possible. It can be achieved by using a routing middleware.
Subdomain routes configuration
Define routes in config/autoload/subdomain-routes.global.php
<?php
declare(strict_types=1);
return [
'subdomain_routes' => [
'mezzio-subdomains.localhost' => \App\Handler\HomePageHandler::class,
'account.mezzio-subdomains.localhost' => \Account\Handler\HomepageHandler::class,
'journal.mezzio-subdomains.localhost' => \Journal\Handler\HomepageHandler::class,
],
];
Routing middleware
The routing middleware uses a SuperFactory src/App/Service/SuperFactory.php
to retrieve the services from the container.
<?php
declare(strict_types=1);
namespace App\Service;
use Laminas\ServiceManager\Exception\ServiceNotFoundException;
use Psr\Container\ContainerInterface;
use Psr\Http\Server\RequestHandlerInterface;
class SuperFactory
{
private ContainerInterface $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function create(string $serviceClass) : RequestHandlerInterface
{
if ($this->container->has($serviceClass)) {
return $this->container->get($serviceClass);
}
throw new ServiceNotFoundException(sprintf(
'Unable to resolve service "%s" to a factory; are you certain you provided it during configuration?',
$serviceClass
));
}
}
The routing middleware src/App/Middleware/SubdomainRouter.php
returns the response of the RequestHandler, if a matching subdomain is found.
<?php
declare(strict_types=1);
namespace App\Middleware;
use App\Service\SuperFactory;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class SubdomainRouter implements MiddlewareInterface
{
private array $subdomainRoutes;
private SuperFactory $superFactory;
public function __construct(array $subdomainRoutes, SuperFactory $superFactory)
{
$this->subdomainRoutes = $subdomainRoutes;
$this->superFactory = $superFactory;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
{
$host = $request->getUri()->getHost();
if (array_key_exists($host, $this->subdomainRoutes)) {
$handler = $this->superFactory->create($this->subdomainRoutes[$host]);
return $handler->handle($request);
}
return $handler->handle($request);
}
}
Pipeline
The routing middleware must be placed in the pipeline before Mezzio\Router\Middleware\RouteMiddleware
.
// [..]
// Add this line
$app->pipe(App\Middleware\SubdomainRouter::class);
// The following route is already defined in the skeleton
$app->pipe(RouteMiddleware::class);
// [..]
Finish
Now requests to http://mezzio-subdomains.localhost/, http://account.mezzio-subdomains.localhost/ and http://journal.mezzio-subdomains.localhost/ are routed as configured.