It is always almost the same story: “As a customer I want to import data from the the superseded system in to the newly build environment”.

So whats the deal? Building the new system based on the database structure to make the import as easy as possible? No, way to many drawbacks and since we have our lessons learned we know that flexibility matters and we'd rather go for a better fitting database design.

Web-services for the reScue

As for the flexibility decoupling is the magic word. A web-service might be the right answer to this question. Why? Because there are a huge number of standards (HTTP, RSS, ATOM, XML-RPC, AJAX, …) supporting this approach, they are locally independend, and they really easy to implement. At least the old system had a RSS-feed in place. So I started investigating if there is a way in Symfony2 to call recieve data from a feed.

Do you know app/console?

Seems at least some people had the same idea as I had, retrieving data from a feed via HTTP GET. So Symfony2 comes up with a nice litte wrapper – the console. Unfortunately the documentation on the website only gives a very brief introduction to it.

Located in the app directory of your project it is ready to be called as soon your Symfony2 installation is ready to be used. Executing it on the command line without an argument it exposes its help page.

$ php app/console
Symfony version 2.0.0-DEV - app

Usage:
  [options] command [arguments]

Options:
  --help           -h Display this help message.
  --quiet          -q Do not output any message.
  --verbose        -v Increase verbosity of messages.
  --version        -V Display this program version.
  --ansi           -a Force ANSI output.
  --no-interaction -n Do not ask any interactive question.
  --shell          -s Launch the shell.

Available commands:
  help          Displays help for a command (?)
  list          Lists commands
assets
  :install
init
  :bundle
router
  :debug        Displays current routes for an application
  :dump-apache  Dumps all routes as Apache rewrite rules

The console provides you with almost everything necessary to run your bundle from the command line. It is even capable of creating a base setup of a new bundle using the init:bundle command. For example:

$>php app/console init:bundle Application/MyBundle

Making you bundle accessible from the cli

Since the search in the online documentation directly passed me to the Doctrine integration of Symfony2 using the console to do some magic handling the cache or setting up the Doctrine environment I found this as a nice source of information how to do this. Also my college Jordi was very helpful getting a clue of all this.

Preparing your Bundle

Basically you just have to create a directory in your top level of your Bundle name Command and place every class file in to be available from the command line. Surely the filename and the name of the containing class have to end with (e.g. ‘ImportCommand'). The nice thing with his is that Symfony2 finds your class on its own. So no further configuration has to be done.

Adding functionality

Before we could start implementing what your cli representation of your Bundle shall do we need to set some basics first.

For starters your command class has to extends _ SymfonyBundleFrameworkBundleCommandCommand _to integrate well into the Symfony2 environment.

Doing this you enables you to override the execute() and configure() functions. The names of he methods will let your guess their function.

Configuration

I earlier quoted that there is no further configuration is to be done. This is true in case of the registration of your command in Symfony2. Surely there is some configuration to be done in terms of name, arguments, description, and perhaps a little help text. The extended class (SymfonyBundleFrameworkBundleCommandCommand) provides you with a set of setter methods to add the mentioned and more information to the configuration of your command. Since Symfony2 is still under development please checkout the API-documentation for a conclusive list of those setters.The Name is used as the command in the console arguments to address your implementation. You should take care that this is all unique. One way to assure this is to add a prefix divided by colon from the real name (e.g ‘myProjectName:importer').

The next specialty is the arguments list. This is defined by the setDefinition() method expecting a list of InputAgrument instances. I put together an example to make this more clear to you.

…

protected function configure()
{
    $this
        ->setName('myProjectName:importer')
        ->setDescription('Import data from a feed.')
        ->setDefinition(array(
            new InputArgument(
                'service',
                InputArgument::REQUIRED,
                'The name of the service to be used to import the retrieved data.'
            ),
            new InputArgument('feed', InputArgument::REQUIRED, 'The feed to be read.'),
        ));
}

…

Once you are done you will find your implementation in the help text of app/console.

$ php app/console
Symfony version 2.0.0-DEV - app

Usage:
  [options] command [arguments]

Options:
  --help           -h Display this help message.
  --quiet          -q Do not output any message.
  --verbose        -v Increase verbosity of messages.
  --version        -V Display this program version.
  --ansi           -a Force ANSI output.
  --no-interaction -n Do not ask any interactive question.
  --shell          -s Launch the shell.

Available commands:
  help          Displays help for a command (?)
  list          Lists commands
assets
  :install
init
  :bundle
 **myProjectName** 
 **  :importer     Import data from a feed.** 
router
  :debug        Displays current routes for an application
  :dump-apache  Dumps all routes as Apache rewrite rules

Execution

As we now have configured our command Bundle we need to tell it what to do in case it gets called. For this purpose the method execute() was introduced. The definition of this method tells us to define two arguments:

  • an implementation of the InputInterface
  • an implementation of the OutputInterface

But don't worry as usual in Symfony2 you don't have to care about their origin just make sure they are available in your implementation of execute(). You will need them that's for sure. The input object grants you access via a number of getter methods (like getArgument($name)) to it's attributes. The output object enables you to output information e.g to the console or a stream, setting the verbosity level, and so on.All you need to do is implementing your own logic doing whatever you want after your command object was executed. To execute your implementation from the command line just type

  $php app/console myProjectName:importer RSSfeed http://feeds.liip.ch/liip_news_de

to per example read the Liip feed into your RSSfeed interpreter.

Conclusion

Once you know where to look it is a very easy process getting your Bundle accessible from the command line. The next step is running it as a cronjob to get feed updates in to my data storage.