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
Pingback:Lier des applications entre elles « Devblog IntraViva
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);
}
}
I have added symfony 1.2 support.
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.
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.
Please try set sf_no_script_name both of your application to on (for production).
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.
Sorry, the hyperlink is like this:
link_to(‘Login’, ‘backend|login/index’)
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.
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.
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.
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.
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>
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!
n/m, i got it fixed… not sure exactly how, but am making a backup! 🙂
thanks for the plugin.
Hello,
Who can help me to install this plugin?
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.
Pingback:Robert Biro
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.