avouez... vous regrettez déjà de ne pas l'avoir connu plus tôt.
Une présentation par Anthony Rossi / AMI Software
Montpellier, le soleil, tout ça...
2 tâches :
<?php
use Symfony\Component\HttpFoundation\Response;
public function helloAction()
{
return new Response('Hello world!');
}
namespace NinjaFactory\GameBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class GameController extends Controller
{
public function indexAction(Request $request)
{
$games = $this->doctrine->getRepository(Game::class)->findAll();
return $this->render('game/index.html.twig', array(
'games' => $games,
));
}
}
game_show:
path: /games/{slug}
defaults: { _controller: NinjaFactory:GameBundle:Game:show }
methods: [GET]
article_list:
path: /articles/{page}
defaults: { _controller: NinjaFactory:BlogBundle:Aticle:list, page: 1 }
requirements:
page: \d+
methods: [GET, HEAD]
$uri = $this->get('router')->generate('game_show', array(
'slug' => 'halo-5'
));
// /games/halo-5
<a href="{{ path('game_show', {'slug': 'halo-5'}) }}">
See Halo 5 page
</a>
{# app/Resources/views/game/list.html.twig #}
{% extends 'layout.html.twig' %}
{% block body %}
<h1>Last games created<h1>
{% for game in games %}
{{ include('game/game_list.html.twig', { 'game': game }) }}
{% endfor %}
{% endblock %}
// 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;
}
assets:install
.// src/AppBundle/Entity/Product.php
namespace AppBundle\Entity;
class Product
{
private $name;
private $price;
private $description;
// Getters and Setters here (can be auto generated by doctrine command)
}
// 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")
*/
private $id;
/**
* @ORM\Column(type="string", length=100)
*/
private $name;
/**
* @ORM\Column(type="decimal", scale=2)
*/
private $price;
/**
* @ORM\Column(type="text")
*/
private $description;
// Getters and Setters here
}
// src/AppBundle/Controller/DefaultController.php
// ...
use AppBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...
public function createAction()
{
$product = new Product();
$product->setName('Keyboard');
$product->setPrice(19.99);
$product->setDescription('Ergonomic and stylish!');
$em = $this->getDoctrine()->getManager();
// tells Doctrine you want to (eventually) save the Product (no queries yet)
$em->persist($product);
// actually executes the queries (i.e. the INSERT query)
$em->flush();
// After the flush, product has an ID ($product->getId())
return new Response('Saved new product with id '.$product->getId());
}
public function showAction($productId)
{
$product = $this->getDoctrine()
->getRepository('AppBundle:Product')
->find($productId);
if (!$product) {
throw $this->createNotFoundException(
'No product found for id '.$productId
);
}
// ... do something, like pass the $product object into a template
}
// query for a single product by its primary key (usually "id")
$product = $repository->find($productId);
// dynamic method names to find a single product based on a column value
$product = $repository->findOneById($productId);
$product = $repository->findOneByName('Keyboard');
// dynamic method names to find a group of products based on a column value
$products = $repository->findByPrice(19.99);
// find *all* products
$products = $repository->findAll();
// query for a single product matching the given name and price
$product = $repository->findOneBy(
array('name' => 'Keyboard', 'price' => 19.99)
);
// query for multiple products matching the given name, ordered by price
$products = $repository->findBy(
array('name' => 'Keyboard'),
array('price' => 'ASC')
);
public function updateAction($productId)
{
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AppBundle:Product')->find($productId);
if (!$product) {
throw $this->createNotFoundException(
'No product found for id '.$productId
);
}
$product->setName('New product name!');
$em->flush();
return $this->redirectToRoute('homepage');
}
$em->remove($product);
$em->flush();
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT p
FROM AppBundle:Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', '19.99');
$products = $query->getResult(); // Return array of Product objects
$em = $this->getDoctrine()->getManager();
// createQueryBuilder automatically selects FROM AppBundle:Product
// and aliases it to "p"
$query = $repository->createQueryBuilder('p')
->where('p.price > :price')
->setParameter('price', '19.99')
->orderBy('p.price', 'ASC')
->getQuery();
$products = $query->getResult();
// to get just one result:
// $product = $query->setMaxResults(1)->getOneOrNullResult();
// src/AppBundle/Entity/Category.php
// ...
use Doctrine\Common\Collections\ArrayCollection;
class Category
{
// ...
/**
* @ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
private $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
}
// src/AppBundle/Entity/Product.php
// ...
class Product
{
// ...
/**
* @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* @ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
}
// ...
use AppBundle\Entity\Category;
use AppBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
class DefaultController extends Controller
{
public function createProductAction()
{
$category = new Category();
$category->setName('Computer Peripherals');
$product = new Product();
$product->setName('Keyboard');
$product->setPrice(19.99);
$product->setDescription('Ergonomic and stylish!');
// relate this product to the category
$product->setCategory($category);
$em = $this->getDoctrine()->getManager();
$em->persist($category);
$em->persist($product);
$em->flush();
return new Response(
'Saved new product with id: '.$product->getId()
.' and new category with id: '.$category->getId()
);
}
}
$product = $productRepository->find($productId);
$categoryName = $product->getCategory()->getName();
// src/AppBundle/Entity/Product.php
/**
* @ORM\PrePersist
*/
public function setUpdatedDate()
{
$this->updatedDate = new \DateTime();
}
Events disponibles : preRemove, postRemove, prePersist, postPersist, preUpdate, postUpdate, postLoad, loadClassMetadata, onClassMetadataNotFound, preFlush, onFlush, postFlush, onClear.
php bin/console doctrine:database:create
php bin/console doctrine:generate:entity
php bin/console doctrine:generate:entities AppBundle/Entity/Product
php bin/console doctrine:schema:update --force
// src/AppBundle/Entity/Author.php
// ...
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank()
*/
public $name;
}
// ...
use Symfony\Component\HttpFoundation\Response;
use AppBundle\Entity\Author;
public function authorAction()
{
$author = new Author();
// ... do something to the $author object
$validator = $this->get('validator');
/** @var \Symfony\Component\Validator\ConstraintViolationList */
$errors = $validator->validate($author);
if (count($errors) > 0) {
/*
* Uses a __toString method on the $errors variable which is a
* ConstraintViolationList object. This gives us a nice string
* for debugging.
*/
$errorsString = (string) $errors;
return new Response($errorsString);
}
return new Response('The author is valid! Yes!');
}
if (count($errors) > 0) {
return $this->render('author/validation.html.twig', array(
'errors' => $errors,
));
}
{# app/Resources/views/author/validation.html.twig #}
<h3>The author has the following errors</h3>
<ul>
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
Symfony inclu directement les contraintes les plus utilisées :
Bien sûr, nous pouvons créer nos propres validations... !
// src/AppBundle/Form/Type/TaskType.php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('task', TextType::class)
->add('dueDate', DateType::class, array('widget' => 'single_text'))
->add('save', SubmitType::class, array('label' => 'Create Task'))
;
}
}
{# app/Resources/views/default/new.html.twig #}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
Chaque utilisateur a des rôles, qui est une collection de string. Par exemple, ROLE_ADMIN, ROLE_COMMENT_WRITE, ...
# app/config/security.yml
security:
# ...
access_control:
# require ROLE_ADMIN for /admin*
- { path: ^/admin, roles: ROLE_ADMIN }
// ...
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!');
// Old way :
// if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) {
// throw $this->createAccessDeniedException('Unable to access this page!');
// }
// ...
}
Deux options
// check for "view" access: calls all voters
$this->denyAccessUnlessGranted('view', $post);
Traductions définies en
use Symfony\Component\HttpFoundation\Response;
public function indexAction()
{
$translated = $this->get('translator')->trans('app.comment.action.add');
return new Response($translated);
}
# Obtao\ForumBundle\Resources\translations\messages.fr.yml
app: # Bundle name
comment: # Object name
action: # Type: action (button, ...)
add: Nouveau commentaire
title: # Type: title (h1, h2...)
listPage: Tous les commentaires
# ...
// src/AppBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank(message = "app.author.name.notBlank")
*/
public $name;
}
$ php bin/console debug:translation fr AcmeDemoBundle
(($number % 10 == 1) && ($number % 100 != 11))
? 0
: ((($number % 10 >= 2)
&& ($number % 10 <= 4)
&& (($number % 100 < 10)
|| ($number % 100 >= 20)))
? 1
: 2
);
$translator->transChoice(
'There is one apple|There are %count% apples',
10,
array('%count%' => 10)
);
'There is one apple|There are %count% apples'
'Il y a %count% pomme|Il y a %count% pommes'
'{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf[ There are many apples'
use AppBundle\Mailer;
$mailer = new Mailer('sendmail');
$mailer->send('ryan@example.com', ...);
# app/config/services.yml
services:
app.mailer:
class: AppBundle\Mailer
arguments: [sendmail]
class HelloController extends Controller
{
// ...
public function sendEmailAction()
{
// ...
$mailer = $this->get('app.mailer');
$mailer->send('ryan@foobar.net', ...);
}
}
# app/config/services.yml
parameters:
app.mailer.transport: sendmail
services:
app.mailer:
class: AppBundle\Mailer
arguments: ['%app.mailer.transport%']
# app/config/services.yml
services:
app.mailer:
# ...
app.newsletter_manager:
class: AppBundle\Newsletter\NewsletterManager
arguments: ['@app.mailer']
// src/AppBundle/Newsletter/NewsletterManager.php
namespace AppBundle\Newsletter;
use AppBundle\Mailer;
class NewsletterManager
{
protected $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
// ...
}
services:
app.mailer:
# ...
app.newsletter_manager:
class: AppBundle\Newsletter\NewsletterManager
calls:
- [setMailer, ['@app.mailer']]
// src/AppBundle/Newsletter/NewsletterManager.php
namespace AppBundle\Newsletter;
use AppBundle\Mailer;
class NewsletterManager {
protected $mailer;
public function setMailer(Mailer $mailer)
{
$this->mailer = $mailer;
}
// ...
}
services:
newsletter_manager:
class: Acme\HelloBundle\Newsletter\NewsletterManager
arguments: ["@request_stack"]
namespace Acme\HelloBundle\Newsletter;
use Symfony\Component\HttpFoundation\RequestStack;
class NewsletterManager
{
protected $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
public function anyMethod()
{
$request = $this->requestStack->getCurrentRequest();
// ... do something with the request
}
// ...
}
# app/config/services.yml
services:
app.newsletter_manager:
class: AppBundle\Newsletter\NewsletterManager
arguments: ['@?app.mailer']
public function __construct(Mailer $mailer = null)
{
// ...
}
# app/config/services.yml
services:
foo.twig.extension:
class: AppBundle\Extension\FooExtension
public: false
tags:
- { name: twig.extension }
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class TransportCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->has('acme_mailer.transport_chain')) {
return;
}
$definition = $container->findDefinition(
'acme_mailer.transport_chain'
);
$taggedServices = $container->findTaggedServiceIds(
'acme_mailer.transport'
);
foreach ($taggedServices as $id => $tags) {
$definition->addMethodCall(
'addTransport',
array(new Reference($id))
);
}
}
}
/**
* @Gedmo\Timestampable(on="create")
* @ORM\Column(type="datetime")
*/
private $created;
/**
* @Gedmo\Blameable(on="create")
* @ORM\Column(type="string")
*/
private $createdBy;