PHP Symfony [Guide]

发布于 - 最后修改于

Symfony is a Web framework and a set of reusable components. Many other PHP frameworks, like Laravel (you can find related articles about Laravel here and here), reuse some of the components that Symfony has. To efficiently use Symfony, you first have to clearly understand the basics of Web development, like how HTTP Request and Response works, and what the different HTTP verbs (GET, POST, DELETE, PUT, PATCH, OPTIONS) represent.

Installation and Project Structure

Symfony has a Symfony installer that's used to create Symfony applications. The Symfony installer can be installed using these commands:

$ sudo curl -LsS http://symfony.com/installer -o /usr/local/bin/symfony
$ sudo chmod a+x /usr/local/bin/symfony

The system is ready for creating Symfony projects:

$ symfony new symfony_guide

This command will create a folder named symfony_guide. It will alsocreate the default application structure for the Symfony framework.

The second option for creating Symfony projects is to use Composer. This can be done using:

$ composer create-project symfony/framework-standard-edition symfony_guide

Some errors may appear when creating the project, but Symfony offers meaningful error messages:

In this case, date.timezone was not set in one of the php.ini configuration files. Please note that your system may have more than one php.ini file. For example, my system has 7:

greg@earth:~/Development/_freelancer/git/symfony_guide$ locate php.ini
/etc/php5/apache2/php.ini
/etc/php5/cli/php.ini
/opt/lampp/etc/php.ini
/opt/lampp/etc/php.ini-pre1.7.2
/usr/share/php5/php.ini-development
/usr/share/php5/php.ini-production
/usr/share/php5/php.ini-production.cli

Since the Symfony project generator uses PHP's command line interface (CLI), I had to update the /etc/php5/cli/php.ini file to make the error disappear. I modified the date.timezone value inside the php.ini file to Europe/Budapest:

Once the project was successfully created, the console should display a similar message to the one seen on the image below:

Here's the structure of the Symfony application:

Most of the time, developers need to touch the app and the src folders. The app folder holds the Resources, cache, logs config folders. The Resources folder contains the views and template files.

The src folder holds the source code for the application, controllers, and services.

Starting the Applications

For starting the Symfony application, the following command should be used:

php app/console server:run

If there were no errors on http://localhost:8000 the following page should be displayed:

Creating a New API Controller

In the src/AppBundle/Controller folder, I create a new controller called MathController.

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;

class MathController extends Controller
{
    /**
     * @Route("/math/add/{number1}/{number2}")
     */
    public function addAction($number1, $number2)
    {
        $sum = $number1 + $number2;
        return new JsonResponse($sum);
    }
}

The code file starts with a namespace definition; the MathController is added to the AppBundle\Controller namespace. The Route, Controller and JsonResponse classes are from the Symfony Framework and its HttpFoundation component. The MathController extends the Controller base class, while the controller only has one function defined, addAction.

The @Route annotation is important because it defines the URL route for the method. Symfony supports variable route parameters, defined as {number1} and {number2} in the example above. The addAction function also has two parameters, $number1 and $number2, which have values that are set to the value in the URL.

At the end of the addAction function, the sum of the two numbers is returned as a JsonResponse.

In this image, I accessed the /math/add/3/4 route and the response was a single value, which is the sum of the two numbers.

Creating a Page Controller

I create a new Controller inside the folder src/AppBundle/Controllers and name it as TextController.

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class TextController extends Controller
{
    /**
     * @Route("/text/quote")
     */
    public function quoteAction()
    {
        return $this->render('text/quote.html.twig',
            array('myquote'=>'Roses are red, violets are pink flowers smell good but your eyes are beautiful.'));
    }
} 

What's the main difference between the TextController and the MathController?The MathController returns a JsonResponse, while the TextController renders a twig template. Twig is a powerful template engine used inside Symfony. Twig parses the templates and converts the content into optimized PHP code, and deals with whitespacing and automatic HTML escaping.

The Twig templates are stored under app/Resources/views folder. It is a best practice to create a folder for each controller and store the related Twig templates inside that folder. In this case, I created the text folder and added the quote.html.twig template file:

{% extends 'base.html.twig' %}

{% block body %}
    <h2>The quote for the day</h2>
    <p>{{myquote}}</p>
{% endblock %}

The syntax of Twig template engine is very similar to the Jinja2 template engine, which is used by the Flask micro-framework.

  1. Database Access Using Doctrine

To access the database, the configuration of the application needs to be adjusted. I edited the app/config/parameters.yml configuration file, added the details for my MySQL database server, and specified the credentials:

# This file is auto-generated during the composer install
parameters:
    database_host: 127.0.0.1
    database_port: 3306
    database_name: jsexpense
    database_user: ujs
    database_password: ujs1234
    mailer_transport: smtp
    mailer_host: 127.0.0.1
    mailer_user: null
    mailer_password: null
    secret: 1861da1091bb6bb8b86662dd1eae15a6bc4488e1

Next, I create the entity for Doctrine, call the entity Currency, and store it under src/AppBundle/Entity folder. This is the default location of entities in Symfony. Doctrine is an ORM library in Symfony, and supports loading/saving data to the database.

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="currency")
 */
class Currency
{
   /**
     * @ORM\Column(type="string", length=10)
     * @ORM\Id
     */
    protected $name;

    /**
      * @ORM\Column(type="float", name="conversionRate")
      */
    protected $conversionRate;

    /**
      * @ORM\Column(type="datetime", name="forDate")
      */
    protected $forDate;
...

As in the case of Controllers, the configuration can also be done through annotations here. I mark the class as an entity using the @ORM\Entity annotation and specify the table that it belongs to. The column mappings are done using the @ORM\Column annotation, so the type—and in some cases, the name of the column in the database—has to be specified.

Symfony offers a command line tool for generating entities:

$ php app/console doctrine:generate:entity

I can use the entity in a controller to load data, so I create a new Controller and name it CurrencyController.

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use AppBundle\Entity\Currency;

class CurrencyController extends Controller
{
    /**
     * @Route("/currency")
     */
    public function currenciesAction()
    {
        $currencies = $this->getDoctrine()
              ->getRepository('AppBundle:Currency')
              ->findAll();

        $respData = array();
        for ($i=0; $i < count($currencies); $i++) {
          $obj = array('name' => $currencies[$i]->getName(), 'conversionRate' => $currencies[$i]->getConversionRate());
          array_push($respData, $obj);
        }

        return new JsonResponse($respData);
    }
}

In the controller, I define the currenciesAction and map this to the /currency route. The database can be accessed using the getDoctrine() and getRepository() methods. The repository key is specified here as AppBundle:Currency, and all the data in the database is loaded using the findAll() method. I then construct the response data, send back the name and the conversionRate of the currencies loaded from the database to the client. Below is a response rendered in the browser:

Doctrine supports MySQL, PostgreSQL and even MongoDB for storing and reading data. There is more information about Doctrine operations in the Symfony Book.

In this article, I covered the most important building blocks of Symfony: Controllers, Templates and database access using Doctrine, but Symfony can do much more with a security module, a form handler module, translation module, and a dependency injection module. You can read about these in more details on this page.

发布于 24 十月, 2015

Greg Bogdan

Software Engineer, Blogger, Tech Enthusiast

I am a Software Engineer with over 7 years of experience in different domains(ERP, Financial Products and Alerting Systems). My main expertise is .NET, Java, Python and JavaScript. I like technical writing and have good experience in creating tutorials and how to technical articles. I am passionate about technology and I love what I do and I always intend to 100% fulfill the project which I am ...

下一篇文章

The Top 20 Finalists for Expose Our Logo 2015