On Github maxpou-slides / symfony2-basics-conserto
Maxence POUTORD
class Drink { protected $id; protected $name; protected $description; public function __construct() { //... } }
class Beer extends Drink { protected $alcohol; protected $color; protected $type; public function __construct() { //... } public function drinkIt() { echo "glug glug"; } }
class Beer { private $color; //... public function getColor() { return $this->id; } public function setColor($color) { $this->color = $color; } }
class Beer { //... } $myBeer = new Beer(); $myBeer->drinkIt(); //will output "glug glug" $myBeer->setColor("blond"); $color = $myBeer->getColor();
class MySingleton { protected static $instance; protected function __construct() { } public static function getInstance() { if (!isset(self::$instance)) { self::$instance = new self; } return self::$instance; } }
Hollywood Principle: "Don't call us, we'll call you"
Projects using Symfony: Drupal, Laravel, eZ Publish, Magento, BlaBlaCar, You****(You-Know-Who)
So, Symfony2 is a MVC Framwork ? NO!
Why SF is not a realy MVC Framwork# 1. install Symfony Installer (Linux/OS X) $ sudo curl -LsS http://symfony.com/installer -o /usr/local/bin/symfony $ sudo chmod a+x /usr/local/bin/symfony # 1. install Symfony Installer (Windows) $ php -r "file_put_contents('symfony', file_get_contents('http://symfony.com/installer'));" # Download&Install Symfony $ symfony new beerApp # OR (choose a version) $ symfony new beerApp 2.8 # 3. Create the first bundle $ php app/console generate:bundle --format=yml
4 Folders:
Request to Response
# app/config/routing conserto_beer: resource: "@ConsertoBeerBundle/Resources/config/routing.yml" prefix: / # src/Conserto/BeerBundle/Resources/config/routing.yml conserto_beer_show: path: /{id}/show defaults: { _controller: "ConsertoBeerBundle:Beer:show" } conserto_beer_update: path: /{id}/update defaults: { _controller: "ConsertoBeerBundle:Beer:update" } methods: [POST, PUT]
Lost ?
$ php app/console debug:router $ php app/console router:match /beer/4/show
beer_show: path: /beers/{_locale}/{brand}/{name}.{_format} defaults: { _controller: ConsertoBundle:Beer:show, _format: html } schemes: [https] requirements: _locale: en|fr _format: html|rss year: \d+
In a nutshell:
//Before <p><?php echo $var; ?></p>
{# After #} <p>{{ var }}</p>
<!-- Before --> <ul> <?php if (count($beers) > 0) { foreach ($beers as $aBeer) { echo "<li>".$aBeer->name." have ".$aBeer->alcohol." alcohol degree</li>"; } } else { echo "<li><em>no beer found</em></li>"; } ?> </ul>
<!-- After --> <ul> {% for aBeer in beers %} <li>{{ aBeer.name }} have {{ aBeer.alcohol }} alcohol degree</li> {% else %} <li><em>no beer found</em></li> {% endfor %} </ul>
{{ "now"|date("m/d/Y") }} // 2/11/2015 {{ 'abcd...'|reverse }} //...dcba {{ '12345'|slice(1, 2) }} //23 {{ 101.51|round }} //102 {# Multiple filters #} {{ 'abcd...'|slice(1, 2)|reverse }} //cb
{{ path('conserto_beer') }} {{ path('conserto_beer_show', { 'id': 42 }) }} {{ path('conserto_beer_show', { 'id': entity.id }) }} <link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}"> {{ dump(myObject) }}
<!-- app/Ressources/views/base.html.twig --> <!DOCTYPE html> <html> <head>...</head> <body> {% block body %}{% endblock %} </body> </html> {# src/Conserto/Bundle/BeerBundle/Resources/views/Beer/index.html.twig #} {% extends '::base.html.twig' %} {% block body %} {% if beer %} <p>Beer: {{ beer }}!</p> {% endif %} {% endblock %}
# Generate entity with doctrine (also generate getters&setters): $ php app/console generate:doctrine:entity # Database creation/update $ php app/console doctrine:schema:create $ php app/console doctrine:schema:update
/** * Beer * * @ORM\Table() * @ORM\Entity(repositoryClass="Conserto\BeerBundle\Entity\BeerRepository") */ class Beer { /** * @var integer * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="name", type="string", length=255) */ private $name; //...
$myBeer = new Beer(); $myBeer->setName('Kwak'); $myBeer->setAlcohol(8.4); $anotherBeer = new Beer(); $anotherBeer->setName('Tripel Karmeliet'); $anotherBeer->setAlcohol(8.4); //Step 1: Retrieve EntityManager service $em = $this->getDoctrine()->getManager(); //Step 2: persist $em->persist($myBeer); $em->persist($anotherBeer); //Step 3: Flush (generate&execute SQL) $em->flush();
$em = $this->getDoctrine()->getManager(); $aBeer = $em->getRepository('ConsertoBeerBundle:Beer')->find($id); $beers = $em->getRepository('ConsertoBeerBundle:Beer')->findAll(); $aBeer = $em->getRepository('ConsertoBeerBundle:Beer')->findOneByAbbey($abbey); $beers = $em->getRepository('ConsertoBeerBundle:Beer')->findByAlcohol($alcohol);
$em = $this->getDoctrine()->getManager(); //Update $aBeer = $em->getRepository('ConsertoBeerBundle:Beer')->find(42); aBeer->setAlcohol = 8.4; $em->flush(); //Delete $aBeer = $em->getRepository('ConsertoBeerBundle:Beer')->find(51); $em->remove($beer); $em->flush();
//src\Conserto\BeerBundle\Repository\BeerRepository.php public function findByAbbeyAndAlcohol($abbey, $alcohol) { $qb = $this->createQueryBuilder('b'); $qb->where('b.abbey = :abbey') ->setParameter('abbey', $abbey) ->andWhere('b.alcohol < :alcohol') ->setParameter('alcohol', $alcohol) ->orderBy('b.name', 'DESC'); return $qb ->getQuery() ->getResult() ; }
//src\Conserto\BeerBundle\Form\BeerType.php public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name') ->add('alcohol') ->add('desciption') ; }
//src\Conserto\BeerBundle\Controller\BeerController.php $beer = $em->getRepository('ConsertoBeerBundle:Beer')->find($id); $editForm = $this->createForm(BeerType::class, $beer, array( 'action' => $this->generateUrl('conserto_beer_update'), 'method' => 'PUT' )); return $this->render('ConsertoBeerBundle:Beer:edit.html.twig', array( 'beer' => $beer, 'edit_form' => $editForm->createView() ));
<!-- edit.html.twig --> {{ form(edit_form) }} {# Will generate a HTML Form #}
# app/config/config.yml twig: form_themes: - 'bootstrap_3_layout.html.twig'More information
<?php use Symfony\Component\Validator\Constraints as Assert; class Beer { //... /** * @var string * * @ORM\Column(name="name", type="string", length=255) * @Assert\NotBlank() * @Assert\Length( * min = 2, * minMessage = "Your Beer must be at least {{ limit }} characters long" * ) */ private $name;Validation documentation
Use Dependency Injection pattern
# app/config/config.yml services: my_mailer: class : Conserto\HelloBundle\Mailer arguments: [sendmail]
class WhateverController extends Controller { public function sendEmailAction() { // ... $mailer = $this->get('aMailer'); $mailer->send('contact@beerapp.io', ...); } }
Crud generation (Controller, Form, Views, Routes)
$ php app/console generate:doctrine:crud
Reverse engineering (generate entities from database)
$ php app/console doctrine:mapping:import --force AcmeBlogBundle xml $ php app/console doctrine:mapping:convert annotation ./src $ php app/console doctrine:generate:entities AcmeBlogBundle
$ alias sf='php app/console'list Lists commands debug:router Displays current routes for an application doctrine:generate:entity Generates a new Doctrine entity inside a bundle doctrine:generate:entities Generates entity classes and method stubs from your mapping information doctrine:schema:update Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata
Please, stick the PSR (PHP Standard Recommendations)
http://www.php-fig.orgTools: PHP_CodeSniffer and PHP-CS-Fixer
# .editorconfig (editorconfig.org) root = true [*] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [**.js] indent_style = space indent_size = 2 [**.php] indent_style = space indent_size = 4