Archives de catégorie : Symfony

Symfony 4 – Doctrine Event Subscriber

« Symfony, peux-tu mettre automatiquement une date d’ajout à chaque création d’une entité ? », voici un des exemples pour lesquels vous pourriez avoir besoin des subscribers.


(Il peut y avoir d’autres méthodes qui pourront faire la même chose, comme les listeners mais pour ce cours nous resterons sur les subscribers, par contre il faut savoir que les listeners sont chargés uniquement quand l’event est déclenché contrairement aux subscribers qui eux sont chargés chaque fois que l’application s’exécute).

J’avais prévu pas mal de blabla mais finalement je me dis que de travailler sur un exemple va être beaucoup mieux, et pour ça nous allons développer… un distributeur de friandises !

Le MCD sera hyper simple :

Un produit aura un nom et la quantité restante dans le distributeur. Une activité sera soit un achat (on réalimente le distributeur) soit une vente (on vend une friandise)

A chaque nouvelle activité il faut remettre à jour la quantité restante du produit, vous pouvez le faire à la main avec quelque chose comme :

$activite->getProduit()->setQteRestante($activite->getProduit->getQteRestante() - $activite->getQuantite());

Et ça c’est dans le cas d’une vente, dans le cas d’achat il faudra faire une addition. A la rigueur si vous ne devez le faire qu’à un seul endroit ça peut encore passer, mais si vous avez plusieurs endroits où c’est mis à jour la maintenance du code peut devenir compliqué.
Vous pourriez par exemple à avoir à créer des activités (donc achat ou vente) via l’import d’un fichier excel, via une tâche cron qui tournerai toutes les nuits, etc…

C’est là ou les subscribers vont nous être utiles : la mise à jour de la quantité restante (ainsi que la dateActivite) ne sera écrit qu’à un seul endroit et sera exécuté automatiquement.

Je ne vous mettrai ici que le code intéressant, mais vous pourrez retrouver l’intégralité du code sur mon github :

https://github.com/gponty/distributeur

Et une démo du résultat final ici :

https://distributeur.dev-web.io/

On va commencer par dire à Symfony que notre subscriber existe (ou en tout cas va exister!), dans le fichier config/services.yaml :

App\EventListener\ActiviteSubscriber:
    tags:
        - { name: doctrine.event_subscriber, connection: default }

Là vraiment rien de compliqué mais indispensable.

Avant de passer à l’écriture du subscriber on va regarder à quel moment il peut se déclencher, je ne vais pas tous les énumérer, vous pourrez les retrouver en intégralité sur le site de doctrine. En ce qui concerne notre projet nous en utiliserons 2 :

- postPersist : Le subscriber va se déclencher après la persistance d'une entité, ce qui va nous intéresser pour la mise à jour des quantités restantes

- prePersist :Le subscriber va se declencher avant la persistance d'une entité ce qui sera intéressant pour la mise à jour de la date d'activité.

La magie de Symfony c’est qu’on va pouvoir regrouper ces 2 events dans le même code :

namespace App\EventListener;

use App\Entity\Activite;
use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;

class ActiviteSubscriber implements EventSubscriber
{
    public function getSubscribedEvents()
    {
        return [
            Events::postPersist,
            Events::prePersist,
        ];
    }

    public function postPersist(LifecycleEventArgs $args)
    {
        $this->updateQuantite($args);
    }

    public function prePersist(LifecycleEventArgs $args)
    {
        $this->updateDateActivite($args);
    }

    public function updateQuantite(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();
        $entityManager = $args->getObjectManager();

        if ($entity instanceof Activite) {
            if($entity->getTypeActivite()==='A') {
                $entity->getProduit()->addQteRestante($entity->getQuantite());
            }

            if($entity->getTypeActivite()==='V'){
                $entity->getProduit()->subQteRestante($entity->getQuantite());
            }

            $entityManager->flush();

        }
    }

    public function updateDateActivite(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();

        if ($entity instanceof Activite) {

            $entity->setDateAtivite(new \DateTime());

        }
    }
}

On pourrait très bien n’avoir qu’une seule fonction qui regroupe addQteRestante/subQteRestante mais je trouvais que pour l’exemple c’était mieux comme ça.

Vous pouvez maintenant tester et ajouter des « activite », vous devrez voir que la quantité restante et la date sur activite se met bien à jour. Et vous pouvez ajouter des activités de de n’importe quelle façon sans ajouter de code (mais attention, seulement à partir du moment où ça passe par doctrine, un INSERT pur dans la base ne mettra pas à jour la donnée)

Je pense qu’il n’y a pas beaucoup plus à ajouter, le code est suffisamment parlant, mais si vous avez des questions n’hésitez pas à commenter cet article.

Utiliser Symfony dans docker

Dans ce tutorial nous allons voir comment développer un site Symfony à l’aide d’une stack docker.

On part du principe qu’il n’y a rien d’installé sur votre poste de travail, à part docker et docker-compose (et que vous êtes sous linux!) Vous trouverez le tutorial d’installation de docker sur le site officiel (par exemple si vous êtes sous Debian : https://docs.docker.com/install/linux/docker-ce/debian/)

Continuer la lecture

Symfony 4 : Gestion utilisateurs sans FOSUserBundle v2018 : Chapitre 3

Cet article est écrit en 3 parties :

Dans ce chapitre nous allons voir comment s’enregistrer et récupérer son mot de passe lors d’un oubli.

Continuer la lecture

Symfony 4 : Gestion utilisateurs sans FOSUserBundle v2018 : Chapitre 2

Cet article est écrit en 3 parties :

Au chapitre précédent on a mis en place notre base de données et l’avons alimenté de quelques users, maintenant il va bien falloir qu’ils se connectent !

Continuer la lecture

Symfony 4 : Gestion utilisateurs sans FOSUserBundle v2018 : Chapitre 1

Cet article est écrit en 3 parties : J’avais écrit l’année dernière une série d’articles sur comment gérer les utilisateurs sans FOSUserBundle, il me restait encore à publier « comment modifier son mot de passe et comment faire un « J’ai oublié mon mot de passe ». Vu que les choses ont pas mal bougé en 1 an j’ai décidé de repartir de 0 (mais en reprenant les grandes lignes de l’ancien article). Mon environnement :
  • Linux Mint 19
  • Php 7.2.10
  • MariadB 10.2.18
  • Symfony 4.1.6
Continuer la lecture

Appli de gestion de mot de passe en Symfony et Vue.js (Partie 3)

Dernier chapitre de notre développement, où nous allons ajouter, modifier et supprimer des mots de passe.

Cet article est écrit en 3 parties :

Vous pouvez retrouver le projet complet ici : https://github.com/gponty/gespassvuejs

Continuer la lecture

Appli de gestion de mot de passe en Symfony et Vue.js (Partie 2)

Suite de notre développement !

Dans le chapitre précédent on a pu mettre en place tout notre environnement ainsi que notre première page.

Cet article est écrit en 3 parties :

Vous pouvez retrouver le projet complet ici : https://github.com/gponty/gespassvuejs

Continuer la lecture

Appli de gestion de mot de passe en Symfony et Vue.js (Partie 1)

Le tutorial du jour va être de développer une application qui servira à stocker vos nombreux mots de passe. (site internet, numéro CB, code PIN, etc…)
Elle portera le doux nom de Gespass.
Cette application servira uniquement d’exercice, je ne vous conseille pas de l’utiliser en production sans en améliorer la sécurité (par exemple au moins ajouter une authentification .htaccess)

Mon environnement de dev actuel :

  • Mint 19 xfce
  • php 7.2.5
  • mariadb 10.1.29
  • symfony 4.1.1
  • server web de symfony
  • vue.js 2.5.16

Cet article sera écrit en 3 parties :

Vous pouvez retrouver le projet complet ici : https://github.com/gponty/gespassvuejs

Continuer la lecture

[Symfony 4] Création de fixtures aléatoires – Faker

Rien de plus ennuyant que de créer des jeux de données afin de tester votre application, d’autant qu’il existe des scripts pour ça, comme Faker qui fait très bien le boulot.
Ces données s’appellent plus communément des « fixtures ».

On va commencer par créer un nouveau projet Symfony 4 :

composer create-project symfony/skeleton sf4-faker

Et y ajouter quelques librairies qui nous serons utiles :

composer req orm
composer req --dev make doctrine/doctrine-fixtures-bundle

Puis tout de suite ajouter le composant qui nous intéressent : Faker (https://github.com/fzaninotto/Faker) :

composer req --dev fzaninotto/faker

Ensuite on va créer une nouvelle entité appelée « Personne » :

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\PersonneRepository")
 */
class Personne
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    private $nom;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $adresse;

    /**
     * @ORM\Column(type="string", length=100)
     */
    private $ville;

    /**
     * @ORM\Column(type="string", length=15)
     */
    private $codePostal;

    /**
     * @ORM\Column(type="text")
     */
    private $description;

    /**
     * @ORM\Column(type="string", length=100)
     */
    private $email;

    /**
     * Get the value of id
     */ 
    public function getId()
    {
        return $this->id;
    }

    /**
     * Get the value of nom
     */ 
    public function getNom()
    {
        return $this->nom;
    }

    /**
     * Set the value of nom
     *
     * @return  self
     */ 
    public function setNom($nom)
    {
        $this->nom = $nom;

        return $this;
    }

    /**
     * Get the value of adresse
     */ 
    public function getAdresse()
    {
        return $this->adresse;
    }

    /**
     * Set the value of adresse
     *
     * @return  self
     */ 
    public function setAdresse($adresse)
    {
        $this->adresse = $adresse;

        return $this;
    }

    /**
     * Get the value of ville
     */ 
    public function getVille()
    {
        return $this->ville;
    }

    /**
     * Set the value of ville
     *
     * @return  self
     */ 
    public function setVille($ville)
    {
        $this->ville = $ville;

        return $this;
    }

    /**
     * Get the value of codePostal
     */ 
    public function getCodePostal()
    {
        return $this->codePostal;
    }

    /**
     * Set the value of codePostal
     *
     * @return  self
     */ 
    public function setCodePostal($codePostal)
    {
        $this->codePostal = $codePostal;

        return $this;
    }

    /**
     * Get the value of description
     */ 
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Set the value of description
     *
     * @return  self
     */ 
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get the value of email
     */ 
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * Set the value of email
     *
     * @return  self
     */ 
    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }
}

lors de l’installation du composant fixtures ce dernier a installé un nouveau répertoire : DataFixtures, c’est dans ce dernier que nous allons générer nos fixtures.

Vous trouverez ici tous les types de données que Faker peut générer : https://github.com/fzaninotto/Faker#formatters

Voilà notre fichier FakerFixtures :

<?php
// src/DataFixtures/FakerFixtures.php
namespace App\DataFixtures;

use App\Entity\Personne;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Faker;

class FakerFixtures extends Fixture
{
    public function load(ObjectManager $manager)
    {

        // On configure dans quelles langues nous voulons nos données
        $faker = Faker\Factory::create('fr_FR');

        // on créé 10 personnes
        for ($i = 0; $i < 10; $i++) {
            $personne = new Personne();
            $personne->setNom($faker->name);
            $personne->setAdresse($faker->streetAddress);
            $personne->setVille($faker->city);
            $personne->setCodePostal($faker->postcode);
            $personne->setDescription($faker->text);
            $personne->setEmail($faker->email);
            $manager->persist($personne);
        }

        $manager->flush();
    }
}

Un petit :

php bin/console doctrine:fixtures:load

Et voilà notre base remplie de 10 personnes :

Vous pouvez retrouver les sources ici :

https://github.com/gponty/sf4-faker

[Symfony 4] Utiliser vue.js

Dans cet article nous allons nous attaquer à la mise en place de vue.js avec Symfony 4.

Vue.js est un framework javascript que j’apprécie particulièrement pour sa simplicité.

Avant de commencer assurez-vous d’avoir bien nodeJS sur votre poste.

On va tout d’abord créer un nouveau projet symfony :

composer create-project symfony/skeleton symfony-vue

Puis ajouter quelques librairies :

composer require annotations twig encore asset
composer require server --dev

Et enfin installer vue.js :

npm install --dev vue vue-loader vue-template-compiler

Afin d’initialiser le projet, il suffit de faire :

npm install

Ces commandes vont créer les répertoires assets, node_module ainsi que le fichier webpack.config.js

C’est ce dernier que nous allons tout de suite modifier :

var Encore = require('@symfony/webpack-encore');

Encore
    .setOutputPath('public/build/')
    .setPublicPath('/build')
    .cleanupOutputBeforeBuild()
    .enableSourceMaps(!Encore.isProduction())
    .enableVersioning(Encore.isProduction())
    .addEntry('app', './assets/js/main.js')
    .enableVueLoader()
;

module.exports = Encore.getWebpackConfig();

Et il faut enfin donner le chemin à symfony du manifest.json afin qu’il trouve les différents assets (le manifet.json sera créé au moment du build)

    # config/packages/framework.yaml
    assets:
        json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'

Voilà pour l’installation, on va maintenant passer aux choses sérieuses en créant nos pages.

On va d’abord créer notre controller, rien de compliqué ici :

<?php
// src/Controller/DefaultController.php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{
    /**
     * @Route("/")
     */
    public function default()
    {
        return $this->render('base.html.twig');
    }
}

Puis le fichier twig templates/base.html.twig :

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Welcome!{% endblock %}</title>
        {% block stylesheets %}{% endblock %}
    </head>
    <body>
        {% block body %}
        <div id="app">Oups... Problème</div>
        {% endblock %}

        {% block javascripts %}
        <script src="{{ asset('build/app.js') }}"></script>
        {% endblock %}
    </body>
</html>

Le composant vue.js dans assets/js/App.vue :

<template>
  <div id="app">
    <hello></hello>
  </div>
</template>

<script>
import Hello from './components/Hello'

class TestClassSyntax {

}

export default {
  name: 'app',
  components: {
    Hello
  }
}
</script>

Le fichier main de vue dans assets/js/main.js :

import Vue from 'vue'
import App from './App'

new Vue({
  el: '#app',
  template: '<App/>',
  components: { App }
})

Et enfin le fichier assets/js/components/Hello.vue :

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'hello',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

Si vous lancer votre serveur à ce moment :

php bin/console server:run

vous allez avoir une erreur 404 vous disant qu’il ne trouve pas le fichier build/app.js, c’est tout à faire normal puisqu’il faut que l’on lance la génération de ce fichier en faisant :

./node_modules/.bin/encore dev

Si tout se passe bien, en allant sur http://127.0.0.1:8000 vous devez avoir un « Welcome to Your Vue.js App » s’afficher, et un « Oups… Problème » si il y a un… problème.

Petite astuce si voulez voir vos modifs en direct sans à chaque fois refaire une génération, à la place de la commande ci-dessus il faut faire :

./node_modules/.bin/encore dev-server --hot

Vous pouvez retrouver les sources ici : https://github.com/gponty/symfony-vue