Using a module from another project

The MVC paradigm, adopted in symfony from top to bottom, brings highly decoupled components meant to be used independently.

In a symfony application, modules are flexible and allow complex manipulations like :

  • forwarding to another module/action and delegate request processing
  • rendering a module/action in isolation (like an email generation module)

Unfortunately, this agility is only available inside a project context. You cannot use a module from another project.

Here is a trick to ‘load’ components (modules, libs, validators or whatever) from an external project.

Project configurations and contexts

The Application configuration object is defined early in the process chain. sfContext loads it and creates a sfContext instance for the current application.

Since symfony 1.1, sfContext is not a singleton anymore. It is now possible to have several context instances.

You can create a configuration object for the external project/application, add a sfContext instance to the stack, and switch between them using the sfContext::switchTo() method.

sfContext::createInstance($configuration)
sfContext::switchTo('another_app');

ExternalProjectFilter

Let’s create a filter that will handle all this job early in the process chain.

In /apps/yourapp/config/filters.yml:

rendering: ~
security:  ~

external:
  class: ExternalProjectFilter
  param:
    sf_root_dir: /var/www/external_project_dir
    sf_app: external_app_name
    sf_env: dev
    sf_debug: true

cache:     ~
common:    ~
execution: ~

Now let’s write the ExternalProjectFilter class in /lib/ExternalProjectFilter.class.php

class ExternalProjectFilter extends sfFilter
{
  public function execute($filterChain)
  {
    if ($this->isFirstCall())
    {
      $current_app = sfConfig::get('sf_app');

      $configuration = ProjectConfiguration::getApplicationConfiguration(
        $this->getParameter('sf_app'),
        $this->getParameter('sf_env'),
        $this->getParameter('sf_debug'),
        $this->getParameter('sf_root_dir')
      );

      // Add an instance of the external project on the current context
      sfContext::createInstance($configuration);

      // Add the external project's database connection settings
      Propel::setConfiguration(sfPropelDatabase::getConfiguration());
      Propel::initialize();

      // Autoload external project files
      $autoload = sfSimpleAutoload::getInstance();
      $autoload->addDirectory( $this->getParameter('sf_root_dir').'/lib');
      $autoload->register();

      // Switch back to the current context
      sfContext::switchTo($current_app);
    }

    $filterChain->execute();
  }
}

Example

In /apps/yourapp/modules/article/template/indexSuccess.php

...
sfContext::switchTo('external_app_name');
echo sfContext::getInstance()->getController()->getPresentationFor('customer', 'index');
sfContext::switchTo('your_app_name');
...

Limitations

Projects cannot share the same default database connection name ‘propel’. The trick is to explicitly name the database in your schema/databases.yml :

schema.xml

<?xml version="1.0" encoding="UTF-8"?>
<database name="project" package="lib.model" defaultIdMethod="native" noXsd="true">
  <table name="article">

databases.yml

all:
  project:
    class:          sfPropelDatabase
    param:
      dsn:          mysql://user:pass@host/db
      encoding:     utf8
Advertisements

  1. why not sharing this module in a plugin?

  2. nicolas.martin

    There is still some coupling between two projects. When I began to work on the multiple sfContext instances solution, I thought each instances would be clearly independant and would embed all what a project need to run, but it is not the case.
    As you can see, I have to manually add autoloading, propel connections, and perhaps I’m missing stuff like logging, internationalization or whatever.
    I think by now, this solution is too much tricky to stand as a real compatible plugin.

  3. Łukasz Wojciechowski

    Thanks Nicolas

    This was very helpful to me.

    In my case I had two applications in one project and I wanted to use in project 2 some module action of project 1.

    My apps are set to use different session cookie names and that was a problem because sfSessionStorage was initialized twice – one time for each app. After that session_regenerate_id() brokes my session of project 2.

    To make things wokring again I added ‘no_session’ environment to project 1 and I disabled session by setting storage factory to a class which implements sfSessionStorage methods as empty methods.

    Then I used ‘no_session” environment when creating Context for project 1 in project 2.

    Best regards

  1. 1 rpsblog.com » A week of symfony #93 (6-&gt;12 october 2008)

    […] Using a module from another project […]




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: