lunes, 22 de julio de 2013

Diving Into Symfony 2

Frameworks are hot topics in the web-industry and have been for some time. In this vast sea of flavors is Symfony – an extensive PHP framework that follows the ever popular MVC paradigm. Its learning curve is probably a little steeper than its fellow competitors, like CodeIgniter. Don't worry, once it grows on you, you'll feel more powerful than ever and you'll be able to develop fantastic applications.


1. Requirements

In this article you will have to use a console program. I personally like Git Bash, but any will do. You will also need to have curl installed in order to install Composer.

If you are a Windows user, you can get all of the above bundled by installing Git for Windows, which is available right here: Git downloads.


2. What You Will Learn

During this article you will learn more about:

  • The Symfony application flow
  • Installing Symfony 2.1 by using Composer
  • The Symfony file structure and bundles
  • The console
  • Routes and controllers
  • Responses
  • Twig

3. A Symfony Lifecycle

Before we get our hands dirty and gritty, I want to use a moment to explain the flow of the Symfony life cycle. symfony-2-diagram

The Request

Like everything else on the web it all begins with a request. This is picked up by Symfony which will match it with our defined routes (don't worry, this will be explained later), which are then matched with controllers. So we tell Symfony which URLs we want to match with certain controllers and their functions.

The Kernel

This is where the magic happens. Symfony will dissect the URL and match it to one of our routes. It will then load the controller we've assigned to the route.

The Controller

The controller is loaded and a given action is executed based on the route.

The Response

Just like with a normal HTTP request, a Symfony request must return a response object.
The response object can be formed in various ways, for instance with headers.
An action must return a valid response object or else an exception will be thrown.

So now you have had a brief introduction to the mechanics of Symfony – now it's time to dive in.


4. Installation Through Composer

One of the beautiful things about web development today, is the tools available for easing your process. One of them is Composer – a package manager for PHP. This article will not include the details of using Composer, but if you are interested, this is a great introduction: Easy Package Management with Composer

First of all, if you don't have Composer globally installed you can download a local installation by running the following command:

      curl -s http://getcomposer.org/installer | php  

Test that the installation worked by typing:

      php composer.phar  

If all went smoothly, you should see a menu of available commands. If not, check that you have setup PHP in your environment variables.

Now we will move on to the installation of Symfony. Replace your-folder with whatever folder name you want for your project. You can also replace the version at the end with any of your choosing. I do recommend checking Packagist: Symfony Framework Standard Edition for the latest stable release.

      php composer.phar create-project symphony/framework-standard-edition your-folder/ 2.1.4   

You should now see Composer downloading dependencies into the folder. If your installation went well, go to your-project.local/web/config.php – here Symfony will tell you about the server requirements that are missing or optional extensions which could boost performance or ease your development.

When you have enabled the required and optional extensions, go to /web/app_dev.php where you should see a welcome screen with links for various learning experiences. This means Symfony was successfully installed – congrats!


5. Introduction to the Symfony File Structure and Bundles

At first glance, your root folder might seem a bit confusing. Don't worry, there is a logically explanation behind the structure. Your root should consist of these four folders and some files. You can ignore the files, since they are not important right now.

      app/      src/      vendor/      web/  

The App Directory

This is where your project's high-level configuration goes. For instance, this is the home of the AppKernel class which loads all of your code and third-party libraries into the framework for your use.

The app directory also contains all of the main configuration files, which contain information about database login, security models, routing and much more.

Your base HTML layout also resides in here.

The Src Directory

The src directory is home for all of your own code, which is grouped in bundles.

Who or What Is a Bundle?

All Symfony code is grouped logically into what is known as bundles. For instance, say your project has a user system, then all of your user-oriented controllers, CSS, JavaScript, database entities and so forth would be contained in a UserBundle. What's great about this system, is that you can take a bundle (for instance a user management bundle) and plug it into any Symfony project.

You can even download ready-to-go bundles from websites such as KNP Bundles. Amongst popular choices are user system bundles and administration CRUD generator bundles. At the time of writing this article, the site has 1779 bundles and 4068 developers.

The Vendor Directory

Here we will store all third-party libraries. This is already packed with libraries, for instance Symfony, Doctrine, Assetic and more.

The Web Directory

This should be the root directory of your domain because this is the only public accessible directory of your project. This is the home of your front controllers app.php and app_dev.php files, which are the two public access points to your application. A user will enter your site through a URL like /app.php/products/jeans.

  • app_dev.php is the main entry point while developing your app. When you are using this as your entry point, Symfony will skip caching and supply you with an awesome development toolbar.
  • app.php is the entry point for production mode. This is actually made optional through mod_rewrite, so the URLs /app.php/products/jeans and /products/jeans actually both point to the same location.

6. Coding With the Console… Wait, What?

The console has proven to be a brilliant tool in my development process and thus I say unto thee: Thou shall not fear thy console, for thee is the creator of all things.

For me, one of the (wonderfully) weird things about shifting to Symfony was the heavy use of the console.

Let's just dive right into it. Open your console and locate the project root. Enter this command:

      php app/console  

These are the commands at your disposal. Mostly you will be using the generators, cache and asset management. You will also be using this to generate bundles, generate database schemas, debugging routes, cache clearing and more.


7. Enough Talk. I Wanna Code!

With some basic knowledge about the structure and the console, you are now ready to dive into Symfony!

Go to app_dev.php. The welcome screen you see here is actually a bundle, like the one we will create in a minute. Go to src/ and delete the directory Acme. If you refresh the page, you will see an error. This is because the AppKernel class is trying to load the bundle that we just deleted. Whenever you add or remove a bundle, you will have to edit the AppKernel class.

So open app/AppKernel.php. You will see an array somewhat like this:

      $bundles = array(          new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),          // ... Other bundles here      );  

This is where you will initialize new bundles. If you create a bundle through the console, it will be added automatically.

Further down you should see an if-block like this:

      if (in_array($this->getEnvironment(), array('dev', 'test'))) {          $bundles[] = new Acme\DemoBundle\AcmeDemoBundle();          // ... Other bundles here      }  

These are development bundles, i.e., bundles that are only loaded when you are in the development environment (app_dev.php). You will see that this is where our deleted bundle is initialized. Remove the AcmeDemoBundle line and save the file.

If you refresh, now you will see the Symfony exception page. This is where all caught exceptions will redirect you to and display more information. You will see an exception that goes something like this:

      Cannot import resource "@AcmeDemoBundle/Controller/SecuredController.php" ...  

This is because Symfony looks for defined routes in the controller file SecuredController.php (which was in the deleted AcmeDemoBundle).

So What's a Route?

Now is probably a good time to explain a little bit more about routes. Basically a route is an URL-template. Imagine you have a blog, with posts, by different categories. So you will want the user to enter in a URL something like this:

      www.myblog.com/categories/Category1      www.myblog.com/categories/Category2      and so forth...  

In Symfony you can define URL-templates which you match to a controller. Imagine the previous example. You would need a function, which was given the category name and looked for blog posts using it. In an MVC application, and therefore in Symfony, this function is wrapped in a controller. So it would basically look like this:

      class ControllerExample {          public function showCategory($category_name) {              // Pull out blog posts by category name and display them          }      }  

Note: This is not valid Symfony code, but just an example of a simple blog controller.

Now you will just need to link together the controller action and the URL. This is done by routes. The route in this case would look like this:

      /categories/{name}  

When a string is written in curly braces, it's interpreted as a variable, which is then passed to the given action. In Symfony you can define routes in XML, YML or by annotations. For the sake of keeping it simple, we will only use annotations in this tutorial.

You can see all defined routes by using the following command in the console:

      php app/console router:debug  

But remember, we had an error. This is because Symfony is still looking for the defined routes in our AcmeDemoBundle controller (which does not exist). So open up app/config/routing_dev.yml and for now, all you need to know is that all routes are defined or imported in routing.yml and routing_dev.yml. Delete the _demo, _welcome and _demo_secured keys. If you refresh, now you will see No route found for "GET /". This is because there are no routes that match the current URL – so let's make one that does.

But First, a Controller

When writing routes as annotations, you write them right above the action you will want to execute when a user enters the given route. Therefore, what we need now is a bundle, which will hold our controller and action.

Open up your console and enter the following command:

      php app/console generate:bundle  

First, you must enter the namespace of your bundle. The general template for this is:

      Vendor/BundleName  

Vendor is the author of the bundle. Here you can put in your company name or whatever you like. I like to use my initials EP. Use whatever you would like, but keep it short.

The name of the bundle must end with Bundle. So I'll enter in the following:

      EP/BlogBundle  

Next, you can choose the name you want to use when identifying the bundle in your code.
I usually omit the vendor name, but for the sake of this tutorial, just hit enter.
There are more steps in the generator, but you will want the default values for this tutorial, so just hit enter until you are through.

Now open src/YourVendorName/BlogBundle/ in your editor. You will see that a basic bundle structure has been created for you. Right now we will skip the details and head straight for the controller directory. Open DefaultController.php in controller/.

This looks a lot like the basic example I wrote before, except the controller is a derivative of Controller - a class from the Symfony framework bundle, which holds the basic functionality for a controller.

If you look at the action, you will notice some annotations that look like this:

      /**      * @Route("/hello/{name}")      * @Template()      */  

The @Route annotation tells Symfony that we want to match the route /hello/{name} with the action \EP\BlogBundle\Controller\DefaultController::indexAction() and that there is a variable called name in the URL. So if somebody enters in URLs similar to these:

      www.myblog.com/hello/Esben      www.myblog.com/hello/Santa       www.myblog.com/hello/Jesus   

... they will all go to the same place, because they will all be matched to the /hello/{name} route.

The @Template annotation tells Symfony which View to use. When left empty, Symfony will guess which view to use view based on the controller name and action name.

But Shouldn't All Actions Return a Valid Response Object?

The observant Padawan will have noticed by now that there is not a returned response object in this action, which I claimed was a requirement earlier in the article.

A response is an object that contains the code you want to display, service codes, headers, etc. For instance, if you want to display a "Hello World" page you will do something like this:

      return new Response('Hello World!',200);  

If you wanted to create a page for an AJAX call, it could be done like this:

      return new Response(json_encode(array('some_variable'=>'some value')),200,array('content-type'=>'application/json'));  

If you want to redirect the user, you can do it with a RedirectResponse object.

Note: You can always modify your response to suit your needs - status codes, headers and more. Nothing is off limits.

Normally, if you want to render a view to the user, you would return a new response object like this:

      return $this->container->get('templating')->renderResponse('EPBlogBundle:Default:index.html.twig',array('name'=>$name));  

This is a long shortcut that returns a response object with a rendered template as its content. Luckily the base controller class, which our controller extends from, has a lot of nifty shortcut functions. We can use its render() method to save us some time:

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

This is just a shortcut to the first method I showed above. The first parameter is the view to render. All of our views are inside our bundle in Resources/views/. The views are separated into directories based on the controller responsible for the view. Hence the naming convention Bundle:Controller:View.

Your base layout view (the main template of your application) is in app/Resources/views/. Since this is not in any Bundle nor Controller directory it is simply referred to as ::base.html.twig. A view in your bundle, which is placed in the root bundle-views directory, is referred to as Bundle::View.

      app/Resources/views/base.html.twig // ::base.html.twig      src/BlogBundle/Resources/views/someView.html.twig // EPBlogBundle::someView.html.twig      src/BlogBundle/Resources/views/Default/index.html.twig // EPBlogBundle:Default:index.html.twig  

And lastly, the second parameter of our render() function are the variables that we want accessible to us in our View.

Templating With Twig

Twig is a template engine built by Sensiolabs - the creators of Symfony. Symfony comes bundled with Twig, though it's not a requirement to use it.

Twig features a lot of nice functionality which is beyond the scope of this article, but feel free to check it out at the official Twig website.

If you open up EPBlogBundle:Default:index.html.twig you will see code that looks like this:

      Hello {{ name }}!  

Twig uses {{ }} and {% %} as start and end tags. Double curly braces means to output something, similar to the PHP equivalent of <%=someVar %>. So {{ name }} means to output the value of our variable $name (which we told Symfony we wanted to use when we created our response object).

To give you a small sample of Twig's awesomeness I will show you some filters. A filter is a function you can apply to a variable. It's applied by using this syntax {{ var|filter }}. Here are some examples.

      {{ name|upper }} // returns the variable in UPPERCASE      {{ name|length }} // returns the length of the variable      {{ name|url_encode }} // returns the URL encoded version of the variable  

You can see a full list of tags, filters and functions at the official Twig documentation. What really makes Twig awesome is that it's actually very easy to create your own filters and functions. But you will have to wait for another tutorial for more information about Twig and its extensibility.


Conclusion

This is the end of our magnificent journey – and what an adventure!

Not only have we learned about the structure of Symfony and bundles, but also the might of routes and their controllers. Sprinkle this with a little bit of Twig and you have been introduced to Symfony! I shall return shortly with more in-depth tutorials about more specific subjects like asset management with Assetic and templating with Twig. Thanks for reading.

No hay comentarios:

Publicar un comentario