0
votes

I want to use the twig function path('route_name') and the router generator's generateUrl method, to render relative links in my symfony project which is multidomain.

The solution sounds simple, while having the routing.yml configered properly and simply calling these methods. But it doesnt work. I would appreciate anyone having the same issue or knowing what the issue could be.

When dumping the $this->container->get('router') i can see the Request host being the one i request: local.domain.com

protected 'generator' => 
    object(appDevUrlGenerator)[2301]
      protected 'routes' => null
      protected 'context' => 
        object(Symfony\Component\Routing\RequestContext)[247]
          private 'baseUrl' => string '/app_dev.php' (length=12)
          private 'pathInfo' => string '/my/link/path' (length=34)
          private 'method' => string 'GET' (length=3)
          private 'host' => string 'local.domain.com' (length=13)

But calling the method as in symfony official example here: http://symfony.com/doc/current/routing.html#generating-urls

i get the absolute url or rather the url with the default domain written in routing.yml

defaults: { page_domain: domain.com }

I'm not quite sure if this is the problem, since the request carries the proper HOST.

Why does the function not generate a relative URL? If you need any other information for debugging, feel free to ask. I'll post everything you need.

Best Regards, MiKE

2
please provide full dump of RequestContext (including schema) and also full route declaration from routing.yml. There is no such option page_domain, as far as I know - Dmitry Malyshenko
Digging further i found out this may be a problem from me wanting to have multi domain host matching. And this is currently not so supported as i thought. github.com/symfony/symfony/issues/6857 Any thoughts on this? - MiKE

2 Answers

2
votes

This is because you defined a route or route resource to be host sensitive by making whole or part of domain as a routing parameter - a common solution in SaaS architectures. Symfony always generates a full URL if the current host is different from the one which is provided or default when none is given. Your routing might be looking like this:

#routing.yml
app:
    resource: "@AppBundle/Controller/"
    type:     annotation
    prefix:   /
    host:     "{page_domain}"
    requirements:
        page_domain: ".+"
    defaults:
        page_domain: "domain.com"

When the framework is generating the URL it needs to know for which domain to generate it - if you do not provide it explicitly it will fall back to a default setting. You can easily imagine that while most links generated within client1.domain.com should point to client1.domain.com as well, it should still technically be possible to generate a cross-domain link to client2.domain.com from within client1.domain.com. You need to realize that in your case domain is the same type of route parameter as any other. You simply have to provide current domain during URL generation.

In Twig you can do it this way:

{{ path('your_route_name', {'page_domain': app.request.attributes.get('page_domain')}) }}

In controller it looks like this:

    $url = $this->generateUrl(
        'your_route_name',
        ['page_domain' => $this->get('request_stack')->getCurrentRequest()->attributes->get('page_domain')]
    );
0
votes

As far as I've seen, this is a "normal" issue if your routing handle the host as a parameter. In this case, if you are using another host than the default one, the default Twig function path() will generate schema-related URI. For example, instead of having /homepage, it will give //www.example.com/homepage.

The workaround for me was to override the default behavior of Twig default function. The work is pretty simple in that case.

Let says we have this file routing.yml:

administration:
    resource: "@AppBundle/Controller/"
    type:     annotation
    host: "{_host}"
    defaults:
        _host: "%app.host.default%"
    requirements:
        _host: "%app.host.regex%"

It means that our parameters for the host is name _host.

Add this code in file AppBundle/Twig/RoutingExtension.php (or whatever your bundle is):

namespace AppBundle\Twig;

use Symfony\Bridge\Twig\Extension\RoutingExtension as BaseRoutingExtension;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

/**
 * Class RoutingExtension
 * Overrides the {@see BaseRoutingExtension::getPath()} method to fix issue on Twig's function path() returning
 * schema-relative URI instead of path-relative.
 *
 * @package AppBundle\Twig
 */
class RoutingExtension extends BaseRoutingExtension
{
    /**
     * @var RequestStack $requestStack
     */
    private $requestStack;

    /**
     * @var string $host
     */
    private $host;

    /**
     * @inheritdoc
     */
    public function __construct(UrlGeneratorInterface $_generator, RequestStack $_requestStack)
    {
        parent::__construct($_generator);
        $this->requestStack = $_requestStack;
    }


    /**
     * @inheritdoc
     */
    public function getPath($_name, $_parameters = [], $_relative = false)
    {
        // fastening the _host value, as cannot be loaded in constructor
        if (!$this->host) {
            $this->host = $this
                ->requestStack
                ->getCurrentRequest()
                ->attributes
                ->get('_host')
            ;
        }

        // merges the parameters, keep "_host" key priority from the given $_parameters
        $parameters = array_merge(
            $_parameters,
            [
                '_host' => $this->host,
            ]
        );

        // generate the route as usual
        return parent::getPath($_name, $parameters, $_relative);
    }
}

Be careful on the method getPath, replace _host into whatever your key is in routing.yml.

Now, all we have to do is to declare this as the service to use instead of the native one. Go to your file services.yml:

services:
  twig.extension.routing:
    class: AppBundle\Twig\RoutingExtension
    tags:
      - { name: twig.extension }
    arguments:
      - "@router"
      - "@request_stack"

And that's all! You don't have to edit any of your Twig: this changes will be used by your Twig files. Clearly note the key inside services: it must be twig.extension.routing! If you change this, all of the code won't work at all, and Twig will continue to generate those schema-relative we tried to solve.