Fork me on GitHub
 

Introduction

KnpRadBundle is the central and single custom piece of the Symfony2 RAD edition. It adds a custom autoloadable kernel and application bundle bundle knowledge to Symfony2, including other neat features which you can read about below.

The KnpRadBundle gives life to the RAD distribution of Symfony2 and has a project generator specifically to build itself. The RAD edition has a somewhat different project structure and application management flow. But you can also use the KnpRadBundle just like any other bundle inside a Symfony2 project, enabling only features that you like.

On the first read, you might think that we're breaking conventions here or trying to produce a new main distribution of Symfony2. It's really not the case. We're just trying to show how easy it is to configure Symfony2 for your needs and create custom distribution with only one bundle. We're solving our own development flow problems here and we hope that many developers out there will share our opinions on development process.

Have fun!

Installation

  1. Create project folder and move into it
  2. Create composer.json file:

    {
        "require": {
            "knplabs/rad-bundle": "dev-master"
        }
    }
  3. Run wget http://getcomposer.org/composer.phar && php composer.phar install
  4. Generate project with generator: php vendor/bin/generate-rad-project NAME_OF_PROJECT
  5. Generate configs with php bin/configs

After you'll do this steps, you'll have empty project, generated for you with really useful defaults, like default view, based on HTML5 Boilerplate 3.0 and Twitter Bootstrap 2.0 forms layout. Then you'll just need to create action:

./console rad:generate:controller Default:index

And you'll have your first RAD action up and ready :-)

All your configuration in one place

  • config
    • kernel.yml
    • bundles
      • framework.yml
      • monolog.yml
      • doctrine.yml
      • fos_user.yml
      • ...
    • routing
      • all.yml
      • dev.yml
      • test.yml
      • prod.yml

In the RAD edition, there's no kernel class. Instead, all your kernel or project configuraion lives inside the config/kernel.yml file (read about it here). Generally, it holds environment-dependant parameters, a list of your project's bundles, and project name.

Instead of a single config.yml file, every bundle has its own configuration file (e.g. framework.yml for the FrameworkBundle or even fos_user.yml for FOSUserBundle). These all live inside the config/bundles folder.

Finally, the routing subdirectory works pretty much like the normal app/config/routing.yml and app/config/routing_dev.yml files in the Symfony2 Standard Edition. The all.yml file is loaded in every environment and you'll use it to import routes from any 3rd party bundles. But don't put your routes here! Those should live inside your application bundle's routing.yml file, which is nicely autoloaded for you.

Customizing your app for each environment is now super easy! The kernel and bundle configuration files consists of environment sections.

all:  ~
dev:  ~
test: ~
prod: ~

The most significant is the all environment section. Any configuration under this key is loaded first any every environment.

Other sections should be adjusted for your specific environments: dev, test, prod.

This concept is proven, easy on the eyes, and does not force you to browse more files in order to lookup the specific configuration values used.

Kernel configuration file

In RAD edition, there's not custom project kernel class. Mainly because you ain't gonna need it. In your front controllers (including console) you'll just initialize RadAppKernel:

<?php
use Symfony\Component\HttpFoundation\Request;
use Knp\Bundle\RadBundle\HttpKernel\RadKernel;

$loader =require(__DIR__.'/../vendor/.composer/autoload.php');
$kernel = RadKernel::createAppKernel($loader, 'prod', false);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();

app/kernel.yml is the main configuration file for your application. It defines all the bundles that your project requires, environment-dependant parameters and a project namespace.

# app/kernel.yml
project: KnpRad
all:
    bundles:
        - Symfony\Bundle\FrameworkBundle\FrameworkBundle
        - Symfony\Bundle\SecurityBundle\SecurityBundle
        - Symfony\Bundle\TwigBundle\TwigBundle
        - Symfony\Bundle\MonologBundle\MonologBundle
        - Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle
        - Symfony\Bundle\AsseticBundle\AsseticBundle
        - Symfony\Bundle\DoctrineBundle\DoctrineBundle
        - Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle

    parameters:
        locale:  en
        secret:  ThisTokenIsNotSoSecretChangeIt
        ...
dev:
    bundles:
        - Symfony\Bundle\WebProfilerBundle\WebProfilerBundle
test:
    bundles:
        - Symfony\Bundle\WebProfilerBundle\WebProfilerBundle

As with any other config in the RAD edition, this file is split by environment and has 3 main configuration options:

  1. project defines your project name (namespace). This could be either a simple project name (KnpRad) or fully qualified namespace (Knp\Rad). This option tells RadKernel where to find your application bundle. In the case of KnpRad, your application code should live inside the src/KnpRad folder and have the KnpRad\ namespace. In the case of Knp\Rad, the folder would be src/Knp/Rad and the namespace would be Knp\Rad\.
  2. parameters defines environment-sensible project parameters, like database connection or mailer configuration. It's like the good old parameters.ini.
  3. bundles defines what bundle classes RadKernel should activate for each environment. Every environment could have own set of bundles, but all environment will have a common set of bundles, defined in the all environment section.

After loading app/kernel.yml, RadKernel will look for a app/kernel.custom.yml file. You can use this file to redefine any parameters that are specific to your machine. This is quite literally the equivalent of parameters.ini and the file should be ignored in your version control system. app/kernel.yml:

# app/kernel.custom.yml
all:
    parameters:
        locale: ru

Bundle configuration files

  • app/config
    • app.yml
    • assetic.yml
    • doctrine.yml
    • framework.yml
    • knp_rad.yml
    • monolog.yml
    • security.yml
    • swiftmailer.yml
    • twig.yml
    • web_profiler.yml

The app/config directory is concerned with configuring all bundles used by the project. You can check the configuration files which come with the RAD distribution: most contain very detailed comments which can guide you through your more bizzare cases.

Secondly, there is one special configuration file - app.yml. This is your project based (application bundle) configuration. Anything here becomes a parameter, prefixed with app.. For example, the following code would result in a app.foo parameter:

# app/config/app.yml
all:
    foo: bar

Every bundle configuration file consists of environment sections, the same as the kernel configuration.

In addition to that, the KnpRadBundle will generate configuration files with sensible defaults for you after calling the bin/vendors or bin/configs commands. This works for the most-used bundles out of the box. In other words, after installing a new bundle and running bin/vendors, you'll automatically have a new configuration file for that bundle. If it's a well-supported bundle, you'll aready have some sensible default configuration. Woh!

Application (project) bundle

  • src/KnpRad
    • Controller
      • MainController.php
    • Tests
      • SomeModelTest.php
    • Resources
      • config
        • routing.yml
        • services.yml
      • translations
        • messages.fr.yml
      • public
        • js/jquery.js
        • css/style.scss
      • views
        • Main
          • index.html.twig
        • layout.html.twig

Instead of having many bundles that house your application code, RAD creates a single, "application" bundle for your code. This bundle is special compared to 3rd party bundles. And you could have only one per project.

  1. Controller holds all your controllers. Refer to them via the shorter App:CONTROLLER:ACTION syntax.
  2. Tests holds all your tests. The inner folder hierarchy should follow your project's one.
  3. Resources holds your resources

Base Controller

RAD edition comes with extended base controller, which provides more controller shortcuts for your RAD development:

Application configuration

  • src/KnpRad/Resources/config
    • routing.yml
    • services.yml

The Application config folder holds all your application configuration, including routing and service/DIC configuration. And these files will be autoloaded!

Yup, no need to include routing.yml inside config/routing/all.yml or create an extension class just to load services.yml - this will be done for you, automatically, whenever you create files named services.yml or routing.yml inside the application config folder.

Views mapped to controller actions

In the application bundle, your views get mapped one-to-one to specific controller actions. It means, that you don't need to write:

return $this->render('App:Overview:index.html.twig', array(
    'name' => $name
));

anymore. Just returning the context array return array('name' => $name); is enough for RadKernel to understand what view you're referring to. It's like the famous SensioFrameworkExtraBundle, but without annotations :-)

In order to help you feel the connection between the view and action even more, we've made the Action suffix of optional. As a matter of fact, rad:generate:controller command will generate actions without the suffix for you.

/**
 * hello action of Default controller.
 *
 * renders App:Default:hello.html.twig
 */
public function hello($name)
{
    return array('name' => $name);
}

Assetic pipeline

Scripts and styles management was always a pain in almost any framework. And it got even worse with increasing usage of 3rd-party javascript or stylesheet libraries and even their preprocessors.

Thanks amazing community generally and Kriss Wallsmith particulary, in Symfony2 we have Assetic - amazing piece of code, that manages all those preprocessor and management stuff. It takes view-centric approach, where assets become part of the view and template becomes responsible for assets management and dependencies solving.

While this view-centric approach works for old school backend-driven applications, it could fail shortly for applications, where main part is the frontend:

{% javascripts
    "bundles/someapp/js/lib/jasmine-1.1.0/jasmine.js"
    "bundles/someapp/js/lib/jasmine-1.1.0/jasmine-html.js"
    
    "bundles/someapp/js/utils.js"
    "bundles/someapp/js/someapp-date.js"
    "bundles/someapp/js/period.js"
    "bundles/someapp/js/period-pair.js"
    
    "bundles/someapp/js/model/model.js"
    "bundles/someapp/js/model/site.js"
    
    "bundles/someapp/js/chart/raphael-min.js"
    "bundles/someapp/js/chart/chart.js"
    "bundles/someapp/js/chart/bar-chart.js"
    "bundles/someapp/js/chart/line-chart.js"
    
    "bundles/someapp/js/spec/*.js"
    "bundles/someapp/js/spec/model/*.js"
    "bundles/someapp/js/spec/chart/*.js"
    output="js/tests/jasmine.js" %}
    <script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}

It's not so readable anymore, right? It become a mess. Because we're trying to solve dependencies from the wrong place. It's like trying to drive a car from airplane cockpit. But what if i want to build clean Rich Internet Application and do it in Symfony2?

Assets Pipeline

Taking seriously, Assets Pipeline is one of the key features of Rails 3.1. But we liked it so much, that we ported it to our KnpRadBundle.

What is Assets Pipeline actually? It's the modern assets management system, built on top of 3 important concepts:

  1. Relative loadpath. Every asset in pipeline has relative loadpaths registered. For example, our RAD edition comes out of the box with src/Your/AppBundle/public, vendor/assets and AnyBundle/Resources/public support. All those folders have js and css subfolders (or could have). So every time, you're requiring subdir/script (note no extension) script (with javascripts tag), pipeline locator will search for it in:
    • src/YourAppBundle/public/js/subdir/script.js
    • src/YourAppBundle/public/js/subdir/script.coffee
    • src/YourAppBundle/public/js/subdir/script.anyOtherFilter
    • vendor/assets/js/subdir/script.js
    • vendor/assets/js/subdir/script.coffee
    • vendor/assets/js/subdir/script.anyOtherFilter
    • AnyBundle/Resources/public/js/subdir/script.js
    • AnyBundle/Resources/public/js/subdir/script.coffee
    • AnyBundle/Resources/public/js/subdir/script.anyOtherFilter
    Yep, that's right, now some bundle could come with lib/jquery.js out of the box. And you could require it in your app as easy as lib/jquery. And when you'll want to update jquery - just put new version in your vendor/assets/js/lib folder - it will be used automatically.
  2. Automatic filter applying. Have you noticed, that pipeline locator searches for *.coffee, *.anyOtherFilter (including *.sass) too? If locator finds that your lib/jquery is vendor/assets/js/lib/jquery.coffee - it will not only load it for you, it will also implicitly ensure that this asset will be CoffeeScript preprocessed. How awesome is that?
  3. Asset dependencies directives. Now the most interesting part. Most of your scripts doesn't come along. They come grouped by nature (jasmine spec with backbone model). And we strongly believe, that assets should manage those dependencies on their own (well, actually Rails community believe, but we totally agree with them). So, pipeline locator supports 5 types of inline directives:
    • require lib/jquery requires another asset from pipeline
    • require_directory lib requires whole directory from pipeline (1 level)
    • require_tree lib recursively requires directory tree from pipeline
    • require_self puts current asset in the place between two other assets in requirement tree
    Yep, you hear us right. You can specify asset dependencies in assets themselves! The one requirement though, your asset should stay valid with those assets, so you need to put them in comment blocks. #= require..., //= require..., *= require... are all supported.

That's basically all main parts to understand. But how to use this shiny pipeline locator in your RAD project? With special pipe (|) prefix:

{% javascripts "|script" %}
    <script src="{{ asset_url }}"></script>
{% endjavascripts %}

It will force Assetic to use pipeline locator to find this asset and to evaluate its require directives (if it has some). Lets pretend, that we have src/Your/Project/public/js/script.coffee, that contains:

#= require jquery/date-plugin
#= require jquery/month-plugin

Both those plugins are in vendor/assets/js/jquery folder and have //require jquery at the begining. In this case, you'll finish with assets list like:

vendor/assets/js/jquery.js
vendor/assets/js/jquery/date-plugin.js
vendor/assets/js/jquery/month-plugin.js
src/Your/Project/js/script.coffee

And script.coffee will be automatically preprocessed by CoffeeScript!

Assetic Pipeline is almost exact copy of Rails Assets Pipeline. You could read more about it here or even watch DHH presentation from YouTube.

Code generators

RAD edition comes with some handy code generators out of the box.

Better project structure

As there's no app folder anymore, no need to hide cache or logs folders inside subfolders. As a matter of fact.

What it means for you? It means, that those folders now are much easier to create or deploy or navigate or event ignore with beloved VCS - they are in the project root now.