Hyperlink Between Application in Symfony 1.1


Symfony 1.1 introduce a new class named sfApplicationConfiguration which replace flat application front controller script into object oriented way. One of the benefit of this class is used to hyperlink between application. Now hyperlink between aplication using symfony internal routing name is possible. This is hard to achieve in previous symfony release–symfony 1.0–hyperlink must be in absolute form.
This feature is not available out of the box, so we must do a little hack to use it. The idea is, switch to the desired application to generate the route.

Code:

<?php

class sfCrossAppController extends sfFrontWebController
{
  const
    CROSS_APP_URL_SPLITER       = '|';

  public function genUrl($parameters = array(), $absolute = false)
  {
    if (!is_array($parameters) && false !== strpos($parameters, self::CROSS_APP_URL_SPLITER))
    {
      list($app, $parameters) = explode(self::CROSS_APP_URL_SPLITER, $parameters, 2);
      if ($app !== ($oldApp = sfConfig::get('sf_app')))
      {
        $environment    = sfConfig::get('sf_environment');
        $no_script_name = sfConfig::get('sf_no_script_name');

        // application paths
        $app_paths      = sfConfig::get('app_sf_cross_app_controller_plugin_app_paths', array());
        $old_app_path   = isset($app_paths[$oldApp]) ? $app_paths[$oldApp] : '';
        $app_path       = isset($app_paths[$app]) ? $app_paths[$app] : '';
        $message        = null;

        // save all configs, switchTo() clear it
        $configs = sfConfig::getAll();

        // switch to desired application
        sfContext::switchTo($app);
        try
        {
          $context    = sfContext::getInstance();
          $controller = $context->getController();
          $request    = $context->getRequest();

          $parameters = $controller->genUrl($parameters, true);

          $replace    = '';
          $with       = '';
          if (!$no_script_name)
          {
            $replace .= $oldApp.'_'.$environment.'.php';
            $with    .= $app.'_'.$environment.'.php';
          }

          // replace script name, url generation always using current script name
          $replace    = $request->getHost().$old_app_path.(substr($old_app_path, -1) !== '/' ? '/' : '').$replace;
          $with       = $request->getHost().$app_path.(substr($app_path, -1) !== '/' ? '/' : '').$with;
          $parameters = str_replace($replace, $with, $parameters);
        }
        catch (sfException $e)
        {
          $message = $e->getMessage();
        }

        // switch back to original application
        sfContext::switchTo($oldApp);
        sfConfig::add($configs);

        // log any error message
        if (!is_null($message) && sfConfig::get('sf_logging_enabled'))
        {
          sfContext::getInstance()->getEventDispatcher()->notify(new sfEvent($this, 'application.log', array($message, 'priority' => sfLogger::ERR)));
        }
      }
    }

    return parent::genUrl($parameters, $absolute);
  }
}

To configure this plugin, we need to changes the controller class to sfCrossAppController in all our applications factories.yml.

all:
  controller:
    class: sfCrossAppController

Next, we may configure the path of applications, we can put in app.yml in the config directory of the project (project/config/app.yml).

all:
  sf_cross_app_controller_plugin:
    app_paths:
      frontend: /
      backend:  /admin/
      other:    /other/

This settings is actually needed to generate url for production when sf_no_script_name is set to on.
We define a cross application routing with this pattern app|routing, for example frontend|@homepage will generate the homepage routing of frontend application.
Now all builtin symfony helper which accept internal route (@route or module/action) can be prefixed with app and separated by | to point to the route of app application.

Download here:
Symfony 1.1: sfCrossAppControllerPlugin.zip
Symfony 1.2: sfCrossAppControllerPlugin.zip

Advertisements

, ,

  1. #1 by Gerald Hanks on November 12, 2008 - 2:30 pm

    I was not able to get the plugin to work under symfony 1.2 without making a couple of changes:

    getController();
    $request = $context->getRequest();

    $parameters = $controller->genUrl($parameters, true);

    $replace = ”;
    $with = ”;
    if (!$no_script_name)
    {
    $replace .= $oldApp.’_’.$environment.’.php’;
    $with .= $app.’_’.$environment.’.php’;
    }

    // replace script name, url generation always using current script name
    $replace = $request->getHost().$old_app_path.(substr($old_app_path, -1) !== ‘/’ ? ‘/’ : ”).$replace;
    $with = $request->getHost().$app_path.(substr($app_path, -1) !== ‘/’ ? ‘/’ : ”).$with;
    $parameters = str_replace($replace, $with, $parameters);
    }
    catch (sfException $e)
    {
    $message = $e->getMessage();
    }

    // switch back to original application
    sfContext::switchTo($oldApp);
    sfConfig::add($configs);

    // log any error message
    if (!is_null($message) && sfConfig::get(‘sf_logging_enabled’))
    {
    sfContext::getInstance()->getEventDispatcher()->notify(new sfEvent($this, ‘application.log’, array($message, ‘priority’ => sfLogger::ERR)));
    }
    }
    }

    return parent::genUrl($parameters, $absolute);
    }
    }

  2. #2 by tohenk on November 13, 2008 - 9:13 am

    I have added symfony 1.2 support.

  3. #3 by Dan on November 18, 2008 - 5:32 pm

    Could you please give a specific example of the changes needed in the routing.yml files for routing to work properly? I am struggling to link between apps in a prod environment. Dev is okay.

    I am using Symfony 1.1.

    Thank you.

  4. #4 by dan on November 20, 2008 - 3:44 am

    i cannot get the 1.1 version of this to work in production. It only works when in dev. For example, if i use with frontend_dev & backend_dev then it works but when using the non dev environment it fails to generate the url properly & keeps putting the front controller name in to the url causing a 404 page not found.

    Any help would be appreciated.

    Thank you.

  5. #5 by tohenk on November 20, 2008 - 11:58 am

    Please try set sf_no_script_name both of your application to on (for production).

  6. #6 by Dan on November 20, 2008 - 4:37 pm

    Thanks for the response Tohenk but it didn’t work. When I click a hyperlink set up as follows in the code:

    I get redirected to this page:

    http://localhost/index.php/login

    I have clearly specified the backend application in the link_to function so why is it simply going to the index.php (frontend) app?

    Thanks for your time.

  7. #7 by Dan on November 20, 2008 - 4:38 pm

    Sorry, the hyperlink is like this:

    link_to(‘Login’, ‘backend|login/index’)

  8. #8 by tohenk on November 20, 2008 - 5:02 pm

    Do your backend application placed in a sub folder, e.g. http://localhost/admin/? Please confirm that your application folder match the sf_cross_app_controller_plugin_app_paths settings. Please notice, to use project/config/app.yml, not the application one.

  9. #9 by Dan on November 20, 2008 - 5:52 pm

    My app.yml in project/config folder is:

    all:
    sf_cross_app_controller_plugin:
    app_paths:
    frontend: /
    backend: /backend/

    Each app is in a separate folder under the project level: ‘frontend’ and ‘backend’.

    I have set my frontend app to ‘no_script_name: on’ and the backend to ‘no_script_name: off’, which is how it will be when published to the Web.

    So, now when I click my link below:

    link_to(’Login’, ‘backend|login/index’)

    The url becomes:

    http://localhost/backend/index.php/login

    which results in a 404. The url should be:

    http://localhost/backend.php/login

    Any ideas?

    Thank you.

  10. #10 by tohenk on November 20, 2008 - 6:13 pm

    It seems both of your front controller script placed in the same folder.

    To be worked in production:
    – Move the backend front controller script to a subfolder: backend, so your backend application should be accessed via http://localhost/backend/backend.php.
    – Edit your backend.php script to match the new settings, it should looks like:


    require_once(dirname(__FILE__).'/../../config/ProjectConfiguration.class.php');

    – By moving your backend front controller script into subfolder, you can even rename the backend script controller name as index.php and copy the .htaccess file to make the script name invisible.

  11. #11 by Dan on November 20, 2008 - 6:32 pm

    Thanks Tohenk. It seems I am confused about how apps should be arranged in prod!

    I have done as you suggested but the stylesheets etc are not read in now. Does the backend app require the css, js, images folders copied in to the backend folder or can they be accessed by the backend sub app from where they are?

    Thanks.

  12. #12 by tohenk on November 21, 2008 - 10:34 am

    Yes, all assets must be made available to the backend. You can do it in severals ways.
    – symlink it from frontend.
    – use your .htaccess file to map everything needed on backend from frontend, here is a sample:

    <IfModule mod_rewrite.c>
    RewriteEngine On

    # redirect /css, /js, /images, and symfony default to root
    RewriteRule ^css/(.*)$ /css/$1
    RewriteRule ^images/(.*)$ /images/$1
    RewriteRule ^js/(.*)$ /js/$1
    RewriteRule ^sf/(.*)$ /sf/$1
    # add your own mapping here

    # we skip all files with .something
    RewriteCond %{REQUEST_URI} \..+$
    RewriteCond %{REQUEST_URI} !\.html$
    RewriteRule .* - [L]
    RewriteCond %{REQUEST_FILENAME} !-f

    # we check if the .html version is here (caching)
    RewriteRule ^$ index.html [QSA]
    RewriteRule ^([^.]+)$ $1.html [QSA]
    RewriteCond %{REQUEST_FILENAME} !-f

    # no, so we redirect to our front web controller
    # match your backend front controller (change /admin/index.php to your own)
    RewriteRule ^(.*)$ /admin/index.php [QSA,L]

    </IfModule>

  13. #13 by Jeremy on May 2, 2009 - 4:16 am

    Hmm… I cannot get this to work with Symfony 1.2

    I have done a little digging, and it appears as if the sfConfig can access anything if there is a pipe in the $route. I narrowed it down to that by doing a simple “echo sfConfig::get(‘sf_app’); exit();” in before the line:

    if ($route && false !== strpos($route, self::CROSS_APP_URL_SPLITER))

    and it works. But if I move it just after that line, it returns an empty string! Very strange.

    What am I doing wrong? Any input would be highly appreciated!

  14. #14 by Jeremy on May 6, 2009 - 4:31 am

    n/m, i got it fixed… not sure exactly how, but am making a backup! 🙂

    thanks for the plugin.

  15. #15 by Amenophis on October 6, 2009 - 4:50 am

    Hello,
    Who can help me to install this plugin?

    • #16 by tohenk on October 10, 2009 - 8:46 am

      Download the plugin suitable for your symfony version, if you’re using symfony 1.3, the 1.2 version can be used.
      For installation, just extract the archive under [PROJECT]/plugins directory. Don’t forget to enable the plugin in your project configuration ([PROJECT]/config/ProjectConfiguration.class.php) for symfony 1.2 and later.
      Follow the instruction above for configuring the plugin.

  16. #17 by lee men denim shorts on September 16, 2013 - 10:53 am

    Excellent post. I was checking continuously this blog and I am impressed!

    Extremelyy helpful information particularly the remaining phase :
    ) I care for such information a lot. I used to be looking for this particular
    information for a vry lengthy time. Thanks and best of luck.

  1. Lier des applications entre elles « Devblog IntraViva
  2. Robert Biro

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: