Andrei Pall

Linux Software Engineering

Symfony notes

Symfony was heavily inspired by other web application frameworks such as Ruby on Rails, Django, and Spring.

Symfony makes heavy use of existing PHP open-source projects as part of the framework, including:

  • Propel or Doctrine as object-relational mapping layers
  • PDO database abstraction layer
  • PHPUnit, a unit testing framework
  • Twig, a templating engine
  • Swift Mailer, an e-mail library
Creating the Symfony Application

Once the Symfony Installer is available, create your first Symfony application with the new command:

symfony new my_project_name

Creating Symfony Applications without the Installer

composer create-project symfony/framework-standard-edition my_project_name
Creating a JSON Response

The Response object you return in your controller can contain HTML, JSON or even a binary file like an image or PDF. You can easily set HTTP headers or the status code. Suppose you want to create a JSON endpoint that returns the lucky number. Just add a second method to LuckyController:

// src/AppBundle/Controller/LuckyController.php
// ...
class LuckyController
{
    // ...
    /**
    * @Route("/api/lucky/number")
    */
    public function apiNumberAction()
    {
        $data = array('lucky_number' => rand(0, 100));
        return new Response(
            json_encode($data),
            200,
            array('Content-Type' => 'application/json')
        );
    }
}

You can even shorten this with the handy JsonResponse:

// src/AppBundle/Controller/LuckyController.php
// ...
// --> don't forget this new use statement
use Symfony\Component\HttpFoundation\JsonResponse;
class LuckyController
{
    // ...
    /**
    * @Route("/api/lucky/number")
    */
    public function apiNumberAction()
    {
        $data = array(
            'lucky_number' => rand(0, 100),
        );
        // calls json_encode and sets the Content-Type header
        return new JsonResponse($data);
    }
}
Using the templating Service

This doesn’t change anything, but it does give you access to Symfony’s container: an array-like object that gives you access to every useful object in the system. These useful objects are called services, and Symfony ships with a service object that can render Twig templates, another that can log messages and many more. To render a Twig template, use a service called templating:

// src/AppBundle/Controller/LuckyController.php
// ...
class LuckyController extends Controller
{
    /**
    * @Route("/lucky/number/{count}")
    */
    public function numberAction($count)
    {
        // ...
        $numbersList = implode(', ', $numbers);
        $html = $this->container->get('templating')->render(
            'lucky/number.html.twig',
            array('luckyNumberList' => $numbersList)
        );
        return new Response($html);
    }
    // ...
}

But this can get even easier! By extending the Controller class, you also get a lot of shortcut methods, like render():

// src/AppBundle/Controller/LuckyController.php
// ...
/**
* @Route("/lucky/number/{count}")
*/
public function numberAction($count)
{
    // ...
    /*
    $html = $this->container->get('templating')->render(
    'lucky/number.html.twig',
    array('luckyNumberList' => $numbersList)
    );
    return new Response($html);
    */
    // render: a shortcut that does the same as above
    return $this->render(
    'lucky/number.html.twig',
    array('luckyNumberList' => $numbersList)
    );
}
Managing Errors and 404 Pages

When things are not found, you should play well with the HTTP protocol and return a 404 response. To do this, you’ll throw a special type of exception. If you’re extending the base Controller class, do the following:

public function indexAction()
{
    // retrieve the object from database
    $product = ...;
    if (!$product) {
        throw $this->createNotFoundException('The product does not exist');
    }
    return $this->render(...);
}

The createNotFoundException() method is just a shortcut to create a special NotFoundHttpException object, which ultimately triggers a 404 HTTP response inside Symfony. Of course, you’re free to throw any Exception class in your controller - Symfony will automatically return a 500 HTTP response code.

throw new \Exception('Something went wrong!');
Managing the Session

Symfony provides a nice session object that you can use to store information about the user (be it a real person using a browser, a bot, or a web service) between requests. By default, Symfony stores the attributes in a cookie by using the native PHP sessions. To retrieve the session, call getSession() method on the Request object. This method returns a SessionInterface with easy methods for storing and fetching things from the session:

use Symfony\Component\HttpFoundation\Request;
public function indexAction(Request $request)
{
    $session = $request->getSession();
    // store an attribute for reuse during a later user request
    $session->set('foo', 'bar');
    // get the attribute set by another controller in another request
    $foobar = $session->get('foobar');
    // use a default value if the attribute doesn't exist
    $filters = $session->get('filters', array());
}
Flash Messages

You can also store special messages, called “flash” messages, on the user’s session. By design, flash messages are meant to be used exactly once: they vanish from the session automatically as soon as you retrieve them. This feature makes “flash” messages particularly great for storing user notifications. For example, imagine you’re processing a form submission:

use Symfony\Component\HttpFoundation\Request;
public function updateAction(Request $request)
{
    $form = $this->createForm(...);
    $form->handleRequest($request);
    if ($form->isValid()) {
        // do some sort of processing
        $this->addFlash(
            'notice',
            'Your changes were saved!'
        );
        // $this->addFlash is equivalent to $this->get('session')->getFlashBag()->add
        return $this->redirectToRoute(...);
    }
    return $this->render(...);
}

After processing the request, the controller sets a flash message in the session and then redirects. The message key (notice in this example) can be anything: you’ll use this key to retrieve the message. In the template of the next page (or even better, in your base layout template), read any flash messages from the session:

Visualizing & Debugging Routes
php bin/console debug:router
Linking to Assets
<img src="" alt="Symfony!" />
<link href="" rel="stylesheet" />
<img src="" alt="Symfony!" />
What is a Service?

Put simply, a Service is any PHP object that performs some sort of “global” task. It’s a purposefully-generic name used in computer science to describe an object that’s created for a specific purpose (e.g. delivering emails). Each service is used throughout your application whenever you need the specific functionality it provides. You don’t have to do anything special to make a service: simply write a PHP class with some code that accomplishes a specific task.

What is a Service Container?

A Service Container (or dependency injection container) is simply a PHP object that manages the instantiation of services (i.e. objects). For example, suppose you have a simple PHP class that delivers email messages. Without a service container, you must manually create the object whenever you need it:

use AppBundle\Mailer;

$mailer = new Mailer('sendmail');
$mailer->send('ryan@example.com', ...);
Creating/Configuring Services in the Container

A better answer is to let the service container create the Mailer object for you. In order for this to work, you must teach the container how to create the Mailer service. This is done via configuration, which can be specified in YAML, XML or PHP:

#app/config/services.yml
  services:
    app.mailer:
       class:      AppBundle\Mailer
       arguments:  [sendmail]

When Symfony initializes, it builds the service container using the application configuration (app/config/config.yml by default). The exact file that’s loaded is dictated by the AppKernel::registerContainerConfiguration() method, which loads an environment-specific configuration file (e.g. config_dev.yml for the dev environment or config_prod.yml for prod). An instance of the AppBundle\Mailer class is now available via the service container. The container is available in any traditional Symfony controller where you can access the services of the container via the get() shortcut method:

class HelloController extends Controller
{
    // ...
    public function sendEmailAction()
    {
        // ...

        $mailer = $this->get('app.mailer');

        $mailer->send('ryan@foobar.net', ...);
    }
}

When you ask for the app.mailer service from the container, the container constructs the object and returns it. This is another major advantage of using the service container. Namely, a service is never constructed until it’s needed. If you define a service and never use it on a request, the service is never created. This saves memory and increases the speed of your application. This also means that there’s very little or no performance hit for defining lots of services. Services that are never used are never constructed.

Service Parameters
# app/config/services.yml
  parameters:
    app.mailer.transport: sendmail
    
  services:
    app.mailer:
       class:      AppBundle\Mailer
       arguments:  ['%app.mailer.transport%']

The end result is exactly the same as before - the difference is only in how you defined the service. By enclosing the app.mailer.transport string with percent (%) signs, the container knows to look for a parameter with that name. When the container is built, it looks up the value of each parameter and uses it in the service definition.

# app/config/parameters.yml
parameters:
  # This will be parsed as string '@securepass'
  mailer_password: '@@securepass'
Debugging Services
php bin/console debug:container