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!
composer.json file:{
"require": {
"knplabs/rad-bundle": "dev-master"
}
}
wget http://getcomposer.org/composer.phar && php composer.phar installphp vendor/bin/generate-rad-project NAME_OF_PROJECTphp bin/configsAfter 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 :-)
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.
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:
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\.
parameters defines environment-sensible project parameters, like
database connection or mailer configuration. It's like the good old parameters.ini.
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
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!
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.
Controller holds all your controllers. Refer to them via the shorter App:CONTROLLER:ACTION syntax.Tests holds all your tests. The inner folder hierarchy should follow your project's one.Resources holds your resourcesRAD edition comes with extended base controller, which provides more controller shortcuts for your RAD development:
getValidator()validate($object, $groups = null)getSession()getSecurityContext()isGranted($attributes, $object = null)createAccessDeniedException($message = 'Access Denied', \Exception $previous = null)isGrantedOr403($attributes, $object = null, $message = 'Access Denied')getEntityManager($name = null)getEntityRepository($repositoryName, $managerName = null)getDocumentManager($name = null)getDocumentRepository($repositoryName, $managerName = null)renderJson(array $hash, Response $response = null)findEntity($class, $id, $managerName = null)findEntityBy($class, array $criteria, $managerName = null)findEntityOr404($class, $id, $managerName = null)findEntityByOr404($class, array $criteria, $managerName = null)findDocument($class, $id, $managerName = null)findDocumentBy($class, array $criteria, $managerName = null)findDocumentOr404($class, $id, $managerName = null)findDocumentByOr404($class, array $criteria, $managerName = null)
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.
config/routing.yml - defines bundle routes. Will be autoloaded by RadKernel for you.config/services.yml - defines bundle services. Will be autoloaded by RadKernel for you.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);
}
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?
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:
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.jssrc/YourAppBundle/public/js/subdir/script.coffeesrc/YourAppBundle/public/js/subdir/script.anyOtherFiltervendor/assets/js/subdir/script.jsvendor/assets/js/subdir/script.coffeevendor/assets/js/subdir/script.anyOtherFilterAnyBundle/Resources/public/js/subdir/script.jsAnyBundle/Resources/public/js/subdir/script.coffeeAnyBundle/Resources/public/js/subdir/script.anyOtherFilterlib/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.
*.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?
require lib/jquery requires another asset from pipelinerequire_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#= 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.
RAD edition comes with some handy code generators out of the box.
rad:generate:controller generates controller or/and
action (with view and route) inside your application bundle. To generate
Admin\Users class with index action and apropriate view,
run rad:generate:controller Admin/Users:index.rad:generate:twig-extension generates twig extension inside your application bundle. To generate
AppExtension run rad:generate:twig-extension App.
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.