Autopergamene

Accueil Retour à En averse d'encre

Laravel — l’élégance perdue du PHP

laravel

Dans la vaste école des frameworks web, de langage en langage le paysage est bien différent. Là où les développeurs Ruby et Python ont su se mobiliser pour une même cause autour des piliers que sont désormais Rails et Django, il faut bien avouer que du côté du PHP ressort un certain côté cours de récré. Des frameworks à la pelle, chacun dérivés de X ou Y, pas un pour s’entendre sur des conventions, pas tous respectueux des standards du PSR. Si dans cette masse on en dégage une poignée devenus majeurs (tels Symfony ou encore CodeIgniter), reste cette impression constante de brouhaha dès que l’on ose partir à la recherche d’une réponse claire et précise à la question « Lequel dois-je utiliser ? ».

taylor Puis il y a quelques années de cela, a commencé à naître Laravel — petit bijou de Taylor Otwell, un développeur venant du monde du .NET et ayant décidé sans trop que l’on sache pourquoi de partir dans le PHP en parallèle. La différence est simple mais elle est là : Taylor a dans son framework insufflé tout ce qu’il avait de bonnes habitudes et de respect des conventions, de composants indépendants et j’en passe. Bref, tout ce que la communauté PHP a du mal a faire avaler à ses nouveaux venus, malgré des efforts récents comme PHP: The Right Way.

Il faut dire que dans le domaine des langages web, PHP a mis du temps à rattraper son retard. Là où Ruby et Python avaient déjà des bases rodées du fait qu’ils n’étaient pas faits pour le web, PHP a connu la démarche inverse. D’abord conçu pour injecter des bribes de dynamismes çà et là dans des pages statiques, il a fallu ensuite combler les manques et ce n’est que depuis peu que la plupart des fonctions perçues comme acquises chez les devs Ruby/Python ont vu le jour (entre autres OOP, classes, les namespaces, les magic methods, composants indépendants etc).
Depuis longtemps aussi il manquait à PHP un manager de composants comme NPM pour Javascript ou Rubygems pour Ruby, qui rendait plus complexe la distribution et la séparation du code en de petits paquets. Un vide comblé par la récente arrivée de Composer qui gagne lentement du terrain. Si Laravel 3 est d’ailleurs pour l’instant distribué manuellement, la version à venir – Laravel 4 – sera un ensemble de composants utilisables indépendaments et sera entièrement distribué et mis-à-jour via Composer.

Une syntaxe élégante

Sans doute un des principes les plus importants de Laravel, c’est la simplicité et la clarté de sa syntaxe. Là où des frameworks comme Symfony, bien que surpuissants, vomissent des centaines de lignes et de lignes qui partent dans tous les sens pour faire des choses parfois basiques, Laravel se lit plus comme du Ruby ou Python – avec des noms de classes et de méthodes orientées pour que le code se lise presque comme des phrases :

Route::GET('album/(:any)', function($album_id) {

  // Récupération et mise en cache de ressources
  $album = Album::where_category(3)->find($album_id);
  Cache::forever('album-artist', $album->artist->name);

  // Appels depuis la partie front-end
  if (Request::ajax()) {
    return Response::json($album);
  }

  // Classe d'authentification intégrée
  if (Auth::guest()) {
    return Redirect::to_action('users@login')
      ->with_message(Lang::line('errors.unlogged'));
  }

  if ($file = Input::has('file')) {

    // Helpers généraux
    $input = Input::except('file');
    Input::upload('file', 'uploads/', $file->name);

    // Validation des modèles
    $validation = Validator::make($input, Album::$rules);
    if ($validation->fails()) {
      return Redirect::back()
        ->with_input()
        ->with_errors($validation);
    }

    $album = Album::update($input);
  }

  return View::make('albums.show')
    ->with_album($album);

});

Plusieurs choses qui ressortent si on est habitué à la syntaxe plus “instance” qui est coutume. D’une, ces classes statiques qui servent d’interface entre l’utilisateur et le moteur de Laravel – c’est ce qui fait toute la différence. Si derrière les rideaux le framework est codé comme n’importe quel autre, Taylor y a supplanté le concept des façades, faisant qu’ainsi quand l’utilisateur tape URL::to_route('admin') il appelle en réalité une autre classe bien plus robuste, tout en laissant champ libre pour utiliser les composants du framework à des fins plus avancées.
Ce souci tout particulier du développeur est à mes yeux ce qui le fait ressortir du lot ; utiliser les classes données est un vrai régal et on se prend à sourire en faisant en quelques lignes ce qui auparavent en nécessitait des dizaines. Les façades évitent aussi le problème récurrent des frameworks où la moindre page doit auparavent être préfixée d’une dizaine de use souvent extrèmement longs.
Laravel fournit un array d’aliases que l’on peut compléter à sa guise pour raccourcir les noms de classes souvent utilisées, cet array étant directement fourni à l’autoloader du framework.

Autre chose que l’on remarque : contrairement à d’autres frameworks récents, Laravel a été pensé pour PHP 5.3 et plus. Ce qui veut dire exploitation à fond du potentiel des Closures, des magic methods et j’en passe. Le fait de ne pas avoir des années d’utilisateurs à prendre en compte et à porter sur ses épaules, ça a permis de tirer parti des dernières avancées du langage et de placer Laravel hors du lot.

Démystification du MVC

J’ai longtemps eu énormément de mal avec le MVC. Je connaissais le principe mais j’avais beau lire article sur article au bout d’un moment l’application concrète m’échappait. Pour ceux qui ne connaissent pas, le (controversé) pattern MVC (Model - View - Controller) prône la “séparation des buts” (separation of concerns en anglais) via une division du code en trois grands concepts. Le modèle récupère, aide et organise les données ; la vue représente et affiche les données voulues ; le controlleur sert de pont entre l’utilisateur et les modèles.
C’est le controlleur qui, quand l’utilisateur demande albums/read/3 va aller chercher le troisième album et remplir la vue albums/read.php avec les bonnes informations.
Très grossièrement hein.

Laravel rend incroyablement plus aisé le concept et son appréhension en, entre autres, ne forçant pas l’utilisation de controlleurs pour afficher la moindre page. De vues non plus en fait. De manière générale c’est peu difficile de cerner ce qui rend le MVC plus abordable avec Laravel mais force m’est de constater à moi et aux autres utilisateurs que le résultat est là. Comparer les premiers pas sur Laravel avec les premiers pas sur Symfony, c’est un monde à part. Un simple Hello World avec Laravel se résume à ça :

Route::get('hello/(:any)', function($name) {
  return 'Hello ' .$name;
});

Alors qu’un autre framework requierait au minimum la création d’une vue hello et d’un controlleur pour y accéder. De manière générale, mettre en place un site est beaucoup plus direct que sur d’autres frameworks. Je ne sais pas si vous avez déjà essayé de vous mettre dans la peau d’un débutant lisant le Getting Started de Symfony, mais il y a de quoi pleurer pour ces pauvres âmes.

Artisan, l’assistant CLI

Laravel vient préfourni avec un assistant CLI (Command Line Interface) nommé Artisan, permettant d’exécuter rapidement une multitude de fonctions de maintenance et de test. Il est même possible en quelques lignes de créer ses propres tâches pour Artisan.
Les tâches n’étant que de petits scripts PHP, on a accès à toutes les classes de Laravel et il est très facile de se créer des tâches de routines pour se rendre la tâche plus facile.
Artisan s’intègre aussi dans PHPUnit pour créer facilement des tests unitaires – que ce soit pour son application, un plugin, tester le moteur de Laravel si on fait une fork, ou autre. Il y a un petit dossiers tests dans application, il y a juste à y glisser des fichiers et tout roule. C’est essentiel parce que les tests unitaires font partie de ces habitudes cruciales et rentrées dans les habitudes des devs d’autres langages mais qui pourtant ont du mal à s’ancrer chez les devs PHP.

artisan

Les migrations

Concept que l’on retrouve nativement dans Ruby on Rails ou via South dans Django, les migrations permettent de versionner l’évolution de votre base de données.
Le principe est simple : une migration représente une modification de la base de données. “Hier soir j’ai renommé la colonne name en lastname“. Via Artisan il ne suffit alors plus que de faire artisan migrate et si des modifications ont eu lieu entre temps, Laravel saura où vous en êtes et mettra à jour la structure et les données de votre BDD. Un exemple de fichier type :

class Create_Categories
{
  public function up()
  {
    Schema::create('categories', function($table) {

      // Classe de schemas intégrée
      $table->increments('id');
        $table->string('name');
        $table->boolean('visible')->default(true);
        $table->integer('user_id')->unsigned();
      $table->timestamps();

      // Gestion des foreign keys
      $table->foreign('user_id')
        ->references('id')
        ->on('users')
        ->on_delete('cascade');
    });
  }

  public function down()
  {
    Schema::drop('categories');
  }
}

Ceux qui n’ont jamais touché à des migrations se demanderont sans doute l’intérêt par rapport à de simples fichiers SQL. L’intérêt c’est qu’un schéma est par définition language-agnostic – au moment de dessiner le schema de votre table, le code n’a aucune idée de si vous utilisez MySQL, SQLite, un serveur Redis, ou encore de simples fichiers textes. Il sait simplement à quoi ressemble la table et au moment de la créer, Laravel va utiliser ses classes de grammaire pour traduire chacune des commandes en son langage voulu.
C’est valable pour les schémas mais surtout – et plus intéressant – pour toute intéraction avec la base.

Fluent & Eloquent

Fluent est le moteur d’abstraction de BDD de Laravel. Il ressemble à ceci :

// Récupérer des données
DB::table('users')->where('group', '>=', 2)->only('name')
DB::table('categories')->order_by('name', 'asc')->first()
DB::table('users')->where_null('category')->or_where_level(1)->count()
DB::table('products')->where_in('id', array(2, 3, 4))->max('sales')
DB::table('users')->increment('votes')
DB::table('users')
  ->join('phone', function($join)
  {
    $join->on('users.id', '=', 'phone.user_id');
    $join->or_on('users.id', '=', 'phone.contact_id');
  })
  ->skip(5)->take(10)
  ->get(array('users.email', 'phone.number'));

// Insérer, modifier, supprimer
DB::table('users')->where_age(18)->update(array('age' => DB::raw('age + 1')));
DB::table('users')->delete(5)
DB::table('users')->insert(array(
  'name' => 'name',
  'age'  => 18,
));

Rien d’extraordinaire, si ce n’est la constante lisibilité qu’apportent les magic methods telles __callStatic et qui permettent des choses comme ->where_name_or_firstname('foo'). Si Fluent est intéressant, c’est parce que c’est le béton fondateur d’Eloquent, l’ORM (Object-relational Mapping) de Laravel. Eloquent est la base (non obligatoire) de vos modèles dans Laravel, ce qui fait que lorsque vous créez un modèle vous pouvez simplement faire ceci :

class Artist extends Eloquent {}

…Et c’est tout. Laravel est un framework qui sous ses airs simplistes traverse de larges distances pour comprendre ce que le développeur essaye de faire plutôt que de placer devant lui vingt outils et de lui dire « Bonne chance mec ». Ici Laravel reconnait le modèle Artist et fait le lien avec la table artists présente dans votre base de données. De là il en extrapole le schéma de votre modèle – pas besoin de mapper manuellement chaque colonne.
Mais la clé de la puissance d’Eloquent c’est tout ce qu’il est ensuite possible de faire depuis le canvas vide de votre classe désormais propre de tout spaghetti prérequis.

D’une, vous pouvez indiquer pour un attribut donné sur un modèle, ce qu’il lui arrive à l’entrée/sortie. Forcer un mot de passe à être toujours crypté, forcer une date à apparaître toujours au format X. De deux, la gestion des relations de Laravel est d’une simplicité déconcertante tout en étant incroyablement optimisée. Quelques exemples simples :

// Définition simple de relations
class Album extends Eloquent
{
  public function artist()
  {
    return $this->belongs_to('Artist');
  }

  public function tracks()
  {
    return $this->has_many('Track');
  }
}

Album::find(4)->artist->name
Album::tracks()->where_rating(5)->take(5)

Artist::with('album', 'album.tracks')->get()

Comme on peut le voir, Eloquent étant construit par-dessus Fluent, toutes les méthodes de l’un se retrouvent chez l’autre.

La dernière requête toute particulière va utiliser la fonction eager-load d’Eloquent, signifiant que ce qui suit, bien que faisant appel à de multiples relations éparpillées sur de multiples modèles différents, à des profondeurs différentes ne va pourtant exécuter que deux/trois requêtes. Laravel va optimiser et précacher les relations pour un rendement optimal, employant les données qu’il a déjà à chaque fois.

foreach($artists as $artist) {
  foreach($artist->albums as $album) {
    echo $album->name;
    echo $album->tracks()->count(). ' pistes';
    foreach($album->tracks as $track) echo $track->name;
  }
}

Eloquent via ses setters/getters dynamiques simplifie aussi immensément la syntaxe de vos vues.

class User extends Eloquent
{
  // Assure que dès que l'on modifiera/attribuera un mot de passe à un
  // utilisateur, celui sera crypté
  public function set_password($pasword)
  {
    $this->set_attribute('password', Hash::make($password))
  }

  // Crée un attribute fictif "name" vous permettant de faire
  // $user->name et d'obtenir son nom/prénom, même si ce champ
  // n'existe pas dans la base
  public function get_name()
  {
    return $this->get_attribute('firstname'). ' ' .$this->get_attribute('lastname');
  }

  // Vous pouvez aussi "écraser" des champs pour
  // y faire du travail préalable à l'affichage
  public function get_created_at()
  {
    return new DateTime($this->get_attribute('created_at'))
      ->format('H:i:s');
  }
}

Blade

Autre point fort de Laravel, son moteur de template, Blade. Contrairement à d’autres moteurs de template comme le célèbre Twig, Blade utilise PHP pour son rendu, ce qui veut dire qu’en plus des tags préfournis il est à tout moment possible d’ouvrir des balises PHP et d’y taper ce qu’on veut. C’est à double tranchant et beaucoup voient celà comme un inconvenient, incitant peut-être trop les nouveaux venus à mettre leur code business dans leurs vues.
Ci-dessous un exemple de vue Blade :

@layout('layouts.main')

@section('title')
  Articles - @parent
@endsection

@section('content')
  @forelse($articles as $article)
    <h1>{{ $article->name }}</h1>
    <article>{{ $article->content }}</article>

    {{-- Commentaires --}}
    @if($article->comments)
      @render_each('partials.comment', $article->comments, 'comment')
    @endif
  @empty
    {{ Alert::info('Aucun article à afficher') }}
  @endforelse

  @unless(Auth::guest())
    @include('admin.footer')
  @endif
@endsecton

Les bundles

Similaires aux plugins de tout framework, les bundles sont là pour compléter toute fonction non disponible nativement. Pour l’instant distribué via un repository privé, ils seront dès Laravel 4 de simples composants distribués via Composer. La différence avec beaucoup de frameworks c’est que les bundles de Laravel sont vraiment chez eux – ils ne sont pas dans une bulle fermée, mais au contraire sont au même niveau que l’application elle-même (puisque le dossier application est en réalité lui-même un bundle).
Les bundles peuvent enregistrer des routes, ajouter des tâches à Artisan, fournir des classes, des vues, et j’en passe. On peut à tout moment accéder à des données d’un bundle via la convention bundle::. Par exemple View::make('admin::index') génerera la vue index du bundle admin.
Laravel fournit aussi toute une poignée d’helpers liés à l’autoload, permettant en une ligne d’ajouter et d’aliaser un ensemble de classes PSR, à la manière de Composer.

J’ai moi-même un ou deux bundles publiés, celui marchant le mieux étant Former qui permet avec facilité de créer des formulaire complèxes :

Former::horizontal_open()
  ->id('MyForm')
  ->secure()
  ->rules(array( 'name' => 'required' ))
  ->method('GET')

  Former::xlarge_text('name')
    ->class('myclass')
    ->value('Joseph')
    ->required();

  Former::textarea('comments')
    ->rows(10)->columns(20)
    ->autofocus();

  Former::actions (
    Former::large_primary_submit('Submit'),
    Former::large_inverse_reset('Reset')
  )

Former::close()

Conclusion

Laravel est un framework extrèmement prometteur, avec une communauté forte derrière-lui. La prochaine version majeure est prévue pour la fin du mois et apportera le support complet de Composer majoritairement, plus d’autres fonctions très utiles comme les ressource controllers qui faciliteront extrèmement la mise en place d’API à la manière de ce que Rails Scaffold propose.

Si vous êtes à la recherche d’un framework pour votre prochain projet, je ne peux que conseiller Laravel – il s’intègre parfaitement à des frameworks JS comme Backbone ou Express, est simple tout en étant puissant, et la fréquence de ses mises à jour laisse deviner un avenir radieux.
Pour savoir par où commencer, je ne peux que recommander la documentation officielle constament tenue à jour, ainsi que le livre gratuit Code Happy qui vous guide pas à pas dans Laravel et en présente les principales fonctions.

Publié le : 11/16/12
Accueil Retour à En averse d'encre