Il commence à apparaître des aides dans l'utilisation de Laravel 4. Il y en a une qui me plaît bien qui a été créé par Jeffrey Way, c'est un ensemble de générateurs qui accélèrent le codage.
Installation du package
Vous pouvez trouver le package ici :
Vous trouvez la procédure d'installation sur githug. La première chose à faire est d'ajouter la référence au fichier composer.json :
{ "require": { "laravel/framework": "4.0.*" "way/generators": "dev-master" }, "autoload": { "classmap": [ "app/commands", "app/controllers", "app/models", "app/database/migrations", "app/database/seeds", "app/tests/TestCase.php" ] }, "scripts": { "pre-update-cmd": [ "php artisan clear-compiled" ], "post-install-cmd": [ "php artisan optimize" ], "post-update-cmd": [ "php artisan optimize" ] }, "config": { "preferred-install": "dist" }, "minimum-stability": "dev" }
Il serait d'ailleurs plus judicieux de le placer dans une rubrique require-dev :
"require-dev": { "way/generators": "dev-master" },
Lors de la mise à jour les packages situés dans cette rubriques sont chargés par défaut (mettez bien à jour votre composer s'il vous le demande !). Lorsque vous voulez avoir ensuite seulement les packages utiles à la production vous ajoutez --no-dev à la commande update.
Faites ensuite une mise à jour avec composer :
Vous attendez que tout soit en place. Il ne reste plus alors qu'à ajouter le service provider dans le fichier app/config/app.php :
'Illuminate\Validation\ValidationServiceProvider', 'Illuminate\View\ViewServiceProvider', 'Illuminate\Workbench\WorkbenchServiceProvider', 'Way\Generators\GeneratorsServiceProvider' ),
Maintenant si vous utilisez la commande php artisan vous remarquez l'apparition de nouvelles commandes :
Si vous utilisez Sublime Text il existe un plugin dédié pour ces générateurs.
Créer une migration
Créer une table
Voyons ce que donne la génération d'une migration avec cet outil :
J'ai demandé la création d'une migration pour une table nommée photos. Voici le résultat dans le fichier 2013_05_31_132938_create_photos_table.php (évidemment la date dépend du moment où vous lancez la commande ) :
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; class CreatePhotoTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('photos', function(Blueprint $table) { $table->increments('id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('photos'); } }
J'obtiens bien la création de la table avec déjà en place la déclaration de l'index et du timestamp. J'ai aussi la suppression de la table dans la fonction down ! Supprimez ce fichier et allons plus loin en déclarant aussi des champs :
J'obtiens le même code avec en complément les deux déclarations de champ :
public function up() { Schema::create('photos', function($table) { $table->increments('id'); $table->string('nom'); $table->integer('categorie'); $table->timestamps(); }); }
Ajouter un champ
Allons encore plus loin, imaginons que je veux ajouter un champ style_id à ma table :
Voyons le contenu de ce nouveau fichier de migration :
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; class AddStyleIdToPhotosTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('photos', function(Blueprint $table) { $table->integer('style_id'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('photos', function(Blueprint $table) { $table->dropColumn('style_id'); }); } }
On a bien la création du champ et aussi dans la fonction down la suppression de ce champ. Tout semble bien se passer
Supprimer un champ
Voyons maintenant comment supprimer un champ : Résultat :use Illuminate\Database\Schema\Blueprint; class RemoveCategorieFromPhotosTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('photos', function(Blueprint $table) { $table->dropColumn('categorie'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('photos', function(Blueprint $table) { $table->integer('categorie'); }); } }On a bien la suppression de la colonne categorie en up et sa recréation en down.
Créer un modèle
On peut facilement créer un modèle : On retrouve bien le fichier app/models/Photo.php :class Photo extends Eloquent { protected $guarded = array(); public static $rules = array(); }
Les deux propriétés ajoutées ne vous seront utiles que si vous utilisez certaines fonctionnalités des packages de Jeffrey Way.
Créer des seeds
Il est aussi facile de créer des seeds, autrement dit du code pour remplir les tables : Résultat avec le fichier app/database/seeds/PhotosTableSeeder.php :class PhotosTableSeeder extends Seeder { public function run() { // Uncomment the below to wipe the table clean before populating // DB::table('photos')->delete(); $photos = array( ); // Uncomment the below to run the seeder // DB::table('photos')->insert($photos); } }Le générateur est suffisamment gentil pour mettre à jour aussi le fichier app/database/seeds/DatabaseSeeder.php :
class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { Eloquent::unguard(); // $this->call('UserTableSeeder'); $this->call('PhotosTableSeeder'); } }Il ne vous reste plus qu'à compléter le tableau... Par exemple :
class PhotosTableSeeder extends Seeder { public function run() { $photos = array( array( 'nom' => 'montagne', 'categorie' => '1' ), array( 'nom' => 'mer', 'categorie' => '2' ) ); DB::table('photos')->insert($photos); } }En utilisant artisan (ça va marcher si vous avez effectivement créé la table avec une migration auparavant) :
Créer une vue
On peut créer une vue : Mais là on ne crée pas grand chose, juste le fichier app/views/photo.blade.php :photo.bladeÉvidemment le générateur ne peut pas aller plus loin .
Créer une ressource
Ici le générateur est particulièrement puissant. Imaginez que vous voulez créer une ressource pour des livres. Voilà la syntaxe si on veut une table livres avec les champs titre et auteur :
On voit que le générateur crée pas mal de choses !
D'abord le modèle app/models/Livre.php :
class Livre extends Eloquent { protected $guarded = array(); public static $rules = array(); }
Ensuite la migration app/database/migrations/2013_05_31_132938_create_photo_table.php :
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; class CreateLivresTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('livres', function(Blueprint $table) { $table->increments('id'); $table->string('titre'); $table->string('auteur'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::drop('livres'); } }
Ensuite 4 vues dans un dossier app/views/livres :
Et pour terminer un seed pour remplir la table app/database/seeds/LivresTableSeeder.php :
class LivresTableSeeder extends Seeder { public function run() { // Uncomment the below to wipe the table clean before populating // DB::table('livres')->delete(); $livres = array( ); // Uncomment the below to run the seeder // DB::table('livres')->insert($livres); } }
On trouve aussi la route :
Route::resource('livres', 'LivresController');
Et le contrôleur app/controllers/LivresController.php :
class LivresController extends BaseController { /** * Display a listing of the resource. * * @return Response */ public function index() { // } /** * Show the form for creating a new resource. * * @return Response */ public function create() { // } /** * Store a newly created resource in storage. * * @return Response */ public function store() { // } /** * Display the specified resource. * * @param int $id * @return Response */ public function show($id) { // } /** * Show the form for editing the specified resource. * * @param int $id * @return Response */ public function edit($id) { // } /** * Update the specified resource in storage. * * @param int $id * @return Response */ public function update($id) { // } /** * Remove the specified resource from storage. * * @param int $id * @return Response */ public function destroy($id) { // } }
Utilisation de la ressource
On ne va pas rester en si bon chemin et maintenant utiliser la ressource que nous venons de créer. D'abord la migration :
Et insérer quelques livres :
class LivresTableSeeder extends Seeder { public function run() { $livres = array( array( 'titre' => 'La grande barrique', 'auteur' => 'Le Gnouf' ), array( 'titre' => 'Ma mer', 'auteur' => 'Jean Veut' ) ); DB::table('livres')->insert($livres); } }
Index
Voyons à présent si tout ça fonctionne. On va renseigner la méthode index du contrôleur :
public function index() { return View::make('livres.index')->with('livres', Livre::all()); }
Et évidemment renseigner aussi la vue app/view/livres/index.blade.php :
@foreach ($livres as $livre) <p>Titre du livre : {{ $livre->titre }}</p> <p>Auteur : {{ $livre->auteur }}</p> @endforeach
Et voilà le résultat avec l'URL http://localhost/laravel/public/livres :
Titre du livre : La grande barrique Auteur : Le Gnouf Titre du livre : Ma mer Auteur : Jean Veut
Show
Puisqu'on est bien partis renseignons aussi la fonction show :
public function show($id) { return View::make('livres.show')->with('livre', Livre::find($id)); }
Et la vue app/view/livres/show.blade.php :
<p>Titre du livre : {{ $livre->titre }}</p> <p>Auteur : {{ $livre->auteur }}</p>
Et voilà le résultat avec l'URL http://localhost/laravel/public/livres/1 :
Titre du livre : La grande barrique Auteur : Le Gnouf
Create
Voyons à présent la fonction create, renseignons le contrôleur :
public function create() { return View::make('livres.create'); }
Et la vue app/view/livres/create.blade.php :
{{ Form::open(array('url' => 'livres', 'method' => 'POST')) }} {{ Form::label('titre', 'Titre :')}} {{ Form::text('titre') }} {{ Form::label('auteur', 'Auteur :')}} {{ Form::text('auteur') }} {{ Form::submit('Envoyer') }} {{ Form::close() }}
Et voilà le résultat avec l'URL http://localhost/laravel/public/livres/create :
On doit utiliser la méthode POST. Au retour on tombe sur la fonction store. Pour simplifier on ne va pas prévoir de validation, le but est juste de voir le mécanisme de la ressource REST :
public function store() { $livre = new Livre; $livre->create(Input::all()); return 'Livre enregistré'; }
Faisons un essai :
Edit
Le résultat est satisfaisant. Voyons maintenant l'édition d'un livre avec la fonction edit :
public function edit($id) { return View::make('livres.edit')->with('livre', Livre::find($id)); }
Et la vue app/view/livres/edit.blade.php :
{{ Form::open(array('url' => 'livres/'.$livre->id, 'method' => 'PUT')) }} {{ Form::label('titre', 'Titre :')}} {{ Form::text('titre', $livre->titre) }} {{ Form::label('auteur', 'Auteur :')}} {{ Form::text('auteur', $livre->auteur) }} {{ Form::submit('Envoyer') }} {{ Form::close() }}
Et voilà le résultat avec l'URL http://localhost/laravel/public/livres/1/edit :
On doit utiliser la méthode PUT. Remarquez dans le code généré que Laravel ajoute ce contrôle caché pour que la méthode PUT soit prise en compte (nos navigateurs ne connaissent que POST et GET) :
<input name="_method" type="hidden" value="PUT">
Au retour on tombe sur la fonction update. Pour simplifier encore on ne va pas prévoir de validation :
public function update($id) { $livre = Livre::find($id); $livre->update(Input::all()); return 'Livre modifié'; }
On va juste changer l'orthographe du nom :
Destroy
Tout a encore l'air de bien se passer . Il ne nous reste plus qu'à voir la suppression d'un livre avec la fonction destroy :
public function destroy($id) { $livre = Livre::find($id); $livre->delete(); return 'Livre supprimé'; }
Maintenant si on utilise l'URL http://localhost/laravel/public/livres/4 avec la méthode DELETE on va supprimer le livre avec l'id 4. Mais la question est : comment avoir cette méthode ? On peut envisager diverses possibilités, je vous en propose une simple pour tester le contrôleur en modifiant un peu la vue app/view/livres/index.blade.php :
@foreach ($livres as $livre) <p>Titre du livre : {{ $livre->titre }}</p> <p>Auteur : {{ $livre->auteur }}</p> @endforeach <p>Choisissez le livre à supprimer :</p> @foreach ($livres as $livre) {{ Form::open(array('url' => 'livres/'.$livre->id, 'method' => 'DELETE')) }} {{ Form::submit($livre->titre.' de '.$livre->auteur) }} {{ Form::close() }} @endforeach
Vous obtenez des boutons pour supprimer les livres avec l'URL http://localhost/laravel/public/livres :
On peut évidemment utiliser Ajax pour créer des solutions plus efficaces .
Synthèse
Faisons une petite synthèse en modifiant encore une fois la vue app/view/livres/index.blade.php :<style type="text/css"> form {margin: 0} a {text-decoration: none} table { border: medium solid #6495ed; border-collapse: collapse; } th, td { border: thin solid #6495ed; padding: 5px; } th {background-color: #D0E3FA} td {background-color: #ffffff} </style> @if (Session::has('flash_notice')) <p>{{ Session::get('flash_notice') }}</p> @endif <table> <caption><h2>Livres en stock</h2></caption> <tr> <th>Titre</th> <th>Auteur</th> <th colspan=3><a href="{{ url('livres/create')}}"> <input type="button" value="Ajouter un livre"> </a></th> </tr> @foreach ($livres as $livre) <tr> <td>{{ $livre->titre }}</td> <td>{{ $livre->auteur }}</td> <td> <a href="{{ url('livres/'.$livre->id)}}"> <input type="button" value="Voir"> </a> </td> <td> <a href="{{ url('livres/'.$livre->id.'/edit')}}"> <input type="button" value="Modifier"> </a> </td> <td> {{ Form::open(array('url' => 'livres/'.$livre->id, 'method' => 'DELETE')) }} {{ Form::submit('Supprimer', array('onclick' => 'return confirm(\'Vraiment supprimer ce livre ?\')')) }} {{ Form::close() }} </td> </tr> @endforeach </table>Et voilà le résultat avec l'URL http://localhost/laravel/public/livres :
Cette fois j'ai fait un petit effort de présentation . Vous voyez qu'on peut obtenir quelque chose de fonctionnel assez rapidement avec ce générateur. Pour boucler l'application on modifie le contrôleur pour renvoyer à cette page après chaque opération :
class LivresController extends BaseController { /** * Display a listing of the resource. * * @return Response */ public function index() { return View::make('livres.index')->with('livres', Livre::all()); } /** * Show the form for creating a new resource. * * @return Response */ public function create() { return View::make('livres.create'); } /** * Store a newly created resource in storage. * * @return Response */ public function store() { $livre = new Livre; $livre->create(Input::all()); return Redirect::to('livres')->with('flash_notice', 'Livre bien ajouté.'); } /** * Display the specified resource. * * @param int $id * @return Response */ public function show($id) { return View::make('livres.show')->with('livre', Livre::find($id)); } /** * Show the form for editing the specified resource. * * @param int $id * @return Response */ public function edit($id) { return View::make('livres.edit')->with('livre', Livre::find($id)); } /** * Update the specified resource in storage. * * @param int $id * @return Response */ public function update($id) { $livre = Livre::find($id); $livre->update(Input::all()); return Redirect::to('livres')->with('flash_notice', 'Livre bien modifié.'); } /** * Remove the specified resource from storage. * * @param int $id * @return Response */ public function destroy($id) { $livre = Livre::find($id); $livre->delete(); return Redirect::to('livres')->with('flash_notice', 'Livre bien supprimé.'); } }
Notez qu'on peut faire la redirection en désignant aussi le contrôleur, par exemple pour la fonction update :
return Redirect::action('LivresController@index')->with('flash_notice', 'Livre bien modifié.');
Créer un "scaffold"
Vous pouvez aller encore plus loin avec ces générateurs et utiliser la commande scaffold. C'est quoi cette bête ? Tout simplement le générateur va non seulement créer tout ce qu'on a vu précédemment mais en plus les vues seront complètes et fonctionnelles ! La syntaxe est la même que pour les ressources. Voyons un exemple :
Au niveau de la génération très peu de différences si ce n'est un layout et un fichier de tests (on peut se demander d'ailleurs pourquoi la génération d'une ressource ne crée plus le fichier de test ). Lançons la migration pour créer la table :
Que se passe-t-il maintenant avec l'URL http://localhost/laravel/public/adresses :
Bon... ça fait partie des joies de générateurs . Apparemment quelque part notre classe Adresse a perdu son "e". C'est apparemment un souci avec les pluriels entre la langue anglaise et la notre . Si vous prenez le mot pluriel anglais adresses en le mettant au singulier ça devient adress, alors que pour nous Français ça devient adresse. On trouve le bug dans le contrôleur :
public function __construct(Adress $adress) { $this->adress = $adress; }
Il suffit de corriger les trois références du nom de la classe... Et maintenant c'est mieux :
On peut maintenant créer une adresse :
Vous avez ainsi une trame de base fonctionnelle avec validations et tests .
Générer un formulaire
Voyons maintenant la dernière commande disponible. Elle permet de créer des formulaires. Mais ça fonctionne uniquement si vous avez un modèle. Prenons le cas du modèle vu précédemment pour les livres :
La commande est bien allée chercher les deux champs pour créer le formulaire avec le bon type. On peut juste se demander si l'utilisation d'une liste est judicieuse, pas toujours. On a la possibilité de demander une autre mise en forme avec l'option html :
Par bestmomo
Aucun commentaire