The goal of the framework is to integrate many independent tools in order to provide a consistent experience for the developer.
Even the framework itself is a Symfony bundle (i.e. a plugin) that can be configured or replaced entirely.
The collection of over twenty independent libraries called the Symfony Components.
Contains the Request and Response classes, as well as other classes for handling sessions and file uploads.
// the URI being requested (e.g. /about) minus any query parameters
$request->getPathInfo();
// retrieve GET and POST variables respectively
$request->query->get('foo');
$request->request->get('bar', 'default value if bar does not exist');
// retrieve SERVER variables
$request->server->get('HTTP_HOST');
// retrieves an instance of UploadedFile identified by foo
$request->files->get('foo');
// retrieve a COOKIE value
$request->cookies->get('PHPSESSID');
// retrieve an HTTP request header, with normalized, lowercase keys
$request->headers->get('host');
$request->headers->get('content_type');
$request->getMethod(); // GET, POST, PUT, DELETE, HEAD
$request->getLanguages(); // an array of languages the client accepts
public function saveBlacklistAction(Request $request)
{
$subscriberData = $request->request->get('subscriber');
//.....
return new JsonResponse( [ 'example' => 'example data' ] );
// or
return new Response( 'Some plain text, <b>or html</b>' );
}The Routing component maps an HTTP request to a set of configuration variables.
Route definition can be done by
// ...
class BlogController extends Controller
{
// ...
/**
* @Route("/blog")
*/
public function indexAction()
{
// ...
}
}// ...
class CampaignController extends Controller
{
/**
* @Route(
* "/campaign/{campaignId}",
* name="delete_campaign",
* requirements={
* "campaignId": "\d+"
* }
* )
* @Method("DELETE")
*/
public function deleteCampaignAction($campaignId)
{
}
}The Templating component provides all the tools needed to build any kind of template system.
// src/AppBundle/Controller/ArticleController.php
namespace AppBundle\Controller;
// ...
class ArticleController extends Controller
{
public function recentArticlesAction($max = 3)
{
// make a database call or other logic
// to get the "$max" most recent articles
$articles = ...;
return $this->render(
'article/recent_list.html.twig',
array('articles' => $articles)
);
}
}
{# app/Resources/views/article/recent_list.html.twig #}
{% for article in articles %}
<a href="/article/{{ article.slug }}">
{{ article.title }}
</a>
{% endfor %}{% extends 'base.html.twig' %}
{% block stylesheets %}
<link href="{{ asset('assets/vendor/materialize/dist/css/materialize.css') }}" rel="stylesheet">
<link href="{{ asset('assets/vendor.css') }}" rel="stylesheet">
<link href="{{ asset('assets/app.css') }}" rel="stylesheet">
{% endblock %}
{% block javascripts %}
<script src="{{ asset('assets/vendor/jquery/dist/jquery.min.js') }}"></script>
<script src="{{ asset('assets/vendor/materialize/dist/js/materialize.min.js') }}"></script>
<script src="{{ asset('assets/vendor/angular/angular.min.js') }}"></script>
<script src="{{ asset('assets/vendor/angular-ui-router/release/angular-ui-router.min.js') }}"></script>
<script src="{{ asset('assets/vendor.js') }}"></script>
<script src="{{ asset('assets/app.js') }}"></script>
<script src="{{ asset('assets/templates.js') }}"></script>
<script>
window.PRELOADED_DATA = {
currentUser: {{ currentUserData | json_encode | raw }},
permissions: {{ permissions | json_encode | raw }}
};
</script>
{% endblock %}
{% block body %}
<div class="app" ng-app="app">
<div ui-view></div>
</div>
{% endblock %}
class DefaultController extends Controller
{
/**
* @Route("/", name="home")
* @Method("GET")
*/
public function indexAction()
{
$currentUserData = $this->get('repository.user')
->getCurrentUserData($this->getUser());
$permissions = $this
->get('repository.permission')->findAllAsArray();
return $this->render('AppBundle:Default:index.html.twig',
[
'currentUserData' => $currentUserData,
'permissions' => $permissions
]
);
}
}The Security component provides a complete security system for your web application.
security:
encoders:
AppBundle\Entity\User: plaintext
providers:
webauth:
id: webauth.security.authentication.user_provider
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
rest:
pattern: ^/(template\/view\/\d+\/\d+)/
security: false
webauth_secured:
pattern: ^/
webauth: true
anonymous: false
access_control:
- { path: ^/campaign, roles: ROLE_ADMIN }
- { path: ^/asset, roles: [ ROLE_ADMIN , ROLE_USER ] }
public function helloAction($name)
{
// The second parameter is used to specify on what object the role is tested.
$this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');
// ...
}(aka) Service Container
class HelloController extends Controller
{
// ...
public function sendEmailAction()
{
// ...
$mailer = $this->get('app.mailer');
$mailer->send('ryan@foobar.net', ...);
}
}# app/config/services.yml
services:
app.mailer:
class: AppBundle\Mailer
arguments: [sendmail]# app/config/config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
framework:
secret: '%secret%'
router: { resource: '%kernel.root_dir%/config/routing.yml' }
# ...
# Twig Configuration
twig:
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
# ...A bundle is similar to a plugin in other software.
The key difference is that everything is a bundle in Symfony
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
new Symfony\Bundle\DoctrineBundle\DoctrineBundle(),
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new AppBundle\AppBundle(),
);
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
$bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
}
return $bundles;
}Doctrine is totally decoupled from Symfony and using it is optional.
Doctrine ORM aims to let you map objects to a relational database (such as MySQL, PostgreSQL or Microsoft SQL). If you prefer to use raw database queries, this is easy, and explained in the "How to Use Doctrine DBAL" cookbook entry.
// src/AppBundle/Entity/Product.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="product")
*/
class Product
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string", length=100)
*/
protected $name;
/**
* @ORM\Column(type="decimal", scale=2)
*/
protected $price;
/**
* @ORM\Column(type="text")
*/
protected $description;
}// src/AppBundle/Controller/DefaultController.php
// ...
use AppBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...
public function createAction()
{
$product = new Product();
$product->setName('A Foo Bar');
$product->setPrice('19.99');
$product->setDescription('Lorem ipsum dolor');
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
return new Response('Created product id '.$product->getId());
}$repository = $this->getDoctrine()
->getRepository('AppBundle:Product')
// query by the primary key (usually "id")
$product = $repository->find($id);
// dynamic method names to find based on a column value
$product = $repository->findOneById($id);
$product = $repository->findOneByName('foo');
// find *all* products
$products = $repository->findAll();
// find a group of products based on an arbitrary column value
$products = $repository->findByPrice(19.99);
// query for one product matching by name and price
$product = $repository->findOneBy(
array('name' => 'foo', 'price' => 19.99)
);
// query for all products matching the name, ordered by price
$products = $repository->findBy(
array('name' => 'foo'),
array('price' => 'ASC')
);