Posts Tagged ‘context’

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