Tutoriel Vidéo Laravel Mieux gérer les formulaires

Télécharger la vidéo Télécharger les sources

Dans ce tutoriel je vous propose de découvrir comment vous créer une classe personnalisée pour gérer les formulaires HTML avec le Framework Laravel

Objectif

Laravel intègre une classe qui permet de générer des formulaires HTML simple (Illuminate\Html\FormBuilder). Le problème c'est que si l'on souhaite créer des formulaire en respectant une structure HTML particulière, son utilisation devient très vite pénible

{{ Form::open() }}

    <div class="form-group">
        {{ Form::label('first_name', 'First Name', array('class' => 'control_label')) }}
        {{ Form::text('first_name', null, array('class' => 'form-control')) }}
    </div>

    ...

{{ Form::close() }}

Ce code devient rapidement pénible à écrire et à lire. Le but est donc de se créer une classe qui permet de créer des formulaires plus simplement.

La classe BootForm

Pour générer ces super formulaires nous allons créer une classe BootForm qui contiendra toutes les méthodes dont on a besoin. Histoire de respecter les conventions on va placer cette classe dans un namespace particulier afin d'éviter les conflits (j'ai choisi Grafikart pour faire super original).

On peut placer cette classe où on le souhaite, il faudra juste modifier le composer.json pour mettre à jour l'autoload. Cette classe étant spécifique à mon application j'ai décidé de créer le fichier dans un dossier lib dans app : app/lib/Grafikart/BootForm.php

<?php
namespace Grafikart;
class BootForm {

}

Une fois cette classe créée on doit mettre à jour l'autoloader en modifier le composer.json en ajoutant dans la partie autoload

"autoload": {
    "classmap": [
        ....
    ],
    "psr-0": { "Grafikart" : "app/lib/" }
},

Enfin on y est presque il faut demander à composer de mettre à jour l'autoloading en lançant la commande

composer update

Maintenant on peut à tout moment utiliser notre classe en l'initialisant

$BootForm = new \Grafikart\BootForm();
$BootForm->maSuperMethod($param1, $param2);

L'intégration dans Laravel

Notre classe fonctionne mais ce que j'aimerais c'est pouvoir l'utiliser de manière beaucoup plus simple. A la mnière du FormBuilder pouvoir faire

BootForm::`text('firstname')

Afin d'arriver à ce résultat il nous faudra 2 choses : Un ServiceProvider et une façade.

Le Service Provider

Le service provider permet de créer un alias pour une instance ou le singleton d'une classe. Par exemple, pour utiliser le FormBuilder, on n'est pas obligé de l'instancier mais on peut directement faire App::make('form') et on obtiendra une instance (ou un singleton dans le cas du FormBuilder). Ce container form est défini dans un Service Provider dans le core du framework Illuminate/Html/HtmlServiceProvider.php:44

Nous allons donc créer un service provider dans notre namespace Grafikart (app/lib/Grafikart/ServiceProvider.php)

<?php
namespace Grafikart;

class ServiceProvider extends \Illuminate\Support\ServiceProvider {

    public function register(){
        $this->app->bindShared('bootform', function($app){
            return new BootForm();
        });
    }

}

Cette classe doit toujours éritr de \Illuminate\Support\ServiceProvider et implémenter une fonction register(). Dans notre cas je crée un container bootform qui retourne une instance de BootForm. J'utilise la méthode bindShared pour indiquer que je souhaite utiliser un singleton. Chaque appel de App::make('bootform') retournera donc une seule et même instance.

Pour utiliser ce service provider au sein de notre application et pouvoir utiliser App::make('bootform') il faut l'ajouter à nos ServiceProvider en modifiant le fichier app/config/app.php

'providers' => array(
    ....
    'Grafikart\ServiceProvider'
),

Et voila nous pouvons maintenant utiliser notre classe BootForm via App::make('bootform')->maMethod(). Mais comment peut-on remplacer ça par BootForm::maMethod() ?

La Facade

Comme je l'ai dit précédemment pour le FormBuilder, l'appelle statique Form:: n'est qu'une façade pour App::make('form'). Donc vous l'aurez compris il va falloir nous aussi créer une façade pour notre classe BootForm

Dans app/lib/Grafikart/Facades/BootForm.php

<?php
namespace Grafikart\Facades;

class BootForm extends \Illuminate\Support\Facades\Facade {

    protected static function getFacadeAccessor() {
        return 'bootform';
    }

}

Encore une fois rien de bien complexe ici il nous faut créer une classe qui hérite de \Illuminate\Support\Facades\Facade et implémenter la méthode getFacadeAccessor() qui retourne le nom du container (dans notre cas 'bootform')

Si on souhaite maintenant utiliser cette façade dans notre application il suffit de modifier le fichier app/config/app.php et d'ajouter notre façade et son alias.

    'aliases' => array(
        ....
        'BootForm'        => 'Grafikart\Facades\Bootform'
    ),

Et voila le tour est joué on peut maintenant utiliser BootForm::maMethod()

L'injection de dépendance

C'est bien sympa de se créer une classe et de pouvoir l'appeller statiquement mais il nous manque une donnée importante. En effet je veux profiter des fonctionnalité de Laravel dans ma classe BootForm et par exemple implémenter ma propre fonction text() qui utilisera la méthode text du FormBuilder.

Une solution serait d'utiliser les facade au sein même de notre classe.

public function text($name, $label) {
    ...
    \Form::text($name, null...)
}

Cette méthode est à éviter car si pour une raison X ou Y on choisit de modifier l'alias de la façade Form notre classe BootForm ne marchera plus correctement. La solution est d'utiliser l'injection de dépendance en injectant les instances dont ont a besoin dans le constructeur

class BootForm {

    public function __construct($form, $session){
        $this->form = $form;
        $this->session = $session;
    }

Dans ma classe j'aurais besoin de la Session et du Form j'injecte donc ces 2 paramètres dans le constructeur. Je peux aussi forcer le typage de ces variables si je le souhaite (ça facilite l'injection de dépendance mais ça rend ma class BootForm beaucoup moins modulable).

Vu que l'on a modifié le constructeur il faut penser à injecter ces instances depuis notre service provider.

<?php
namespace Grafikart;

class ServiceProvider extends \Illuminate\Support\ServiceProvider {

    public function register(){
        $this->app->bindShared('bootform', function($app){
            return new BootForm($app['form'], $app['session']);
        });
    }

}

Maintenant on peut créer des méthodes et utiliser les fonctions de ces 2 objets.

    public function close() {
        return $this->form->close();
    }

L'avantage c'est qu'on peut adapter cette classe à nos besoin et y implémenter les méthodes que l'on souhaite. Par exemple le fichier BootForm créé pendant la vidéo contient une bonne base de travail qui automatise beaucoup de codes.