2009-05-30 - Cross Link Application with symfony

One of the main complain about symfony is that sharing a route between applications is not an easy task, and there is not a clean way to do that.

The current solutions are:

Now, let's introduce another way to do cross link application in symfony.

The idea is to include applications' routes in the current application but we also need to keep the right level of separation between the different applications.

The swToolboxPlugin implements a natural way to share links between application, for instance you can call from the frontend a backend route by doing:

<?php link_to('@backend.edit_blog_post?id='.$post->getId()) ?>

Installation

 

all:
  swToolbox:
    routes_register_cross_applications: on

    swToolboxCrossApplicationRouting:
      backend:
        enabled: on
        load: [frontend]
        host:
          dev: local.host/frontend_dev.php
          prod: local.host

      frontend:
        enabled: on
        load: [backend]
        host:
          dev: backend.local.host/backend_dev.php
          prod: backend.local.host

The few config lines specify to the plugin to load the backend app's route when using the frontend's app and "vice-versa".

Usage

From one application you can do:

[php]
<?php link_to('@backend.edit_blog_post?id='.$post->getId()) ?>

No helper or modification are required in your code, except adding more links ;)

How does that work ?

The plugin register a specific routing handler, which load routes from another application. All external routes are encapsulated into a specific proxy route. The proxy route just adds more requirements to the loaded route to make sure they cannot match the routes from the current application. The specific routing handler also renames route to 'APP_NAME.ROUTE_NAME'.

That's all ;)

Limitations

Extra hack !

In some cases you might not want to edit a link, such as APP_NAME.ROUTE_NAME, but you want to keep the ROUTE_NAME only. This can be done but with a little hack into the sfFrontWebController which is in charge of generating the url. Create your own sfFrontWebController and extend the genUrl method.

<?php
class yourFrontWebController extends sfFrontWebController
{
  /**
   *
   * @see sfWebController#genUrl()
   */
  public function genUrl($parameters = array(), $absolute = false)
  {

    // absolute URL or symfony URL?
    if (is_string($parameters) && preg_match('#^[a-z][a-z0-9\+.\-]*\://#i', $parameters))
    {
      return $parameters;
    }

    // relative URL?
    if (is_string($parameters) && 0 === strpos($parameters, '/'))
    {
      return $parameters;
    }

    if (is_string($parameters) && $parameters == '#')
    {
      return $parameters;
    }

    $route = '';
    $fragment = '';

    if (is_string($parameters))
    {
      // strip fragment
      if (false !== ($pos = strpos($parameters, '#')))
      {
        $fragment = substr($parameters, $pos + 1);
        $parameters = substr($parameters, 0, $pos);
      }

      list($route, $parameters) = $this->convertUrlStringToParameters($parameters);
    }
    else if (is_array($parameters))
    {
      if (isset($parameters['sf_route']))
      {
        $route = $parameters['sf_route'];
        unset($parameters['sf_route']);
      }
    }

    // ------ START OF HACK

    // Custom method to avoid the need of modifying the route name in the template
    //   this is usefull if the template is shared across multiple template
    //   the first route found will be used
    if(!$this->context->getRouting()->hasRouteName($route))
    {
      $sw_cross_link_config = sfConfig::get('app_swToolbox_swToolboxCrossApplicationRouting', array());
      $sw_cross_current_app = $this->context->getConfiguration()->getApplication();

      if(array_key_exists($sw_cross_current_app, $sw_cross_link_config))
      {
        foreach($sw_cross_link_config[$sw_cross_current_app]['load'] as $app_to_load)
        {
          $app_route = $app_to_load.'.'.$route;
          if($this->context->getRouting()->hasRouteName($app_route))
          {
            $route = $app_route;
            break;
          }
        }
      }
    }

    // ------- END OF HACK


    // routing to generate path
    $url = $this->context->getRouting()->generate($route, $parameters, $absolute);

    if ($fragment)
    {
      $url .= '#'.$fragment;
    }

    return $url;
  }
}

 

all:
  controller:
    class: mgWebController

Now if the routing class cannot find the route in the current application, it will look to the external routes loaded. If the route exists, boom you will get the route ;)

Comments

comments powered by Disqus