Dans ce TP vous allez réaliser une application web qui utilise le langage PHP. Nous allons suivre pas à pas la construction de cette application de la création de la structure jusqu’à la réalisation de pages. Pour cela nous allons utiliser un framework qui met en oeuvre le cadre de conception MVC. Il y a de nombreuses propositions sur le réseau qui sont utilisées pour le développement d’applications professionnelles :
pour ne citer que deux exemples très connus.
Nous avons choisi pour nos TP le framework le plus abordable(pour un débutant) c’est à dire Laravel. Néanmoins il faut savoir que l’apprentissage de Laravel facilitera la compréhension et la mise en oeuvre d’un projet utilisant le framework Symfony.
Laravel propose une documentation très bien faite, ce sera un outil précieux dans notre travail de développement.
Nous allons avoir besoin d’un outil de développement (IDE) qui va accélérer notre production de code.
L’utilisation de PHPStorm n’est pas obligatoire, mais les enseignants utiliseront ce logiciel pour illustrer leurs propos pendant les séances de TPs.
composer
:composer create-project --prefer-dist laravel/laravel tp_mvc
laravel
:Dans ce cas il faut dans un premier temps ajouter la commande laravel
dans votre environnement personnel (à faire une fois).
composer global require "laravel/installer"
puis ajouter la commande dans votre PATH
à l’aide de la commande :
export PATH=$HOME/.composer/vendor/bin:$PATH
Si vous ne voulez pas le faire à chaque fois que vous ouvrez un terminal, il faut ajouter la commande précédente dans votre fichier ~/.bashrc
.
La commande laravel
est maintenant disponible. Pour créer un nouveau projet Laravel, il faut utiliser la commande
laravel new tp_mvc
Si le réseau ne supporte pas la charge, demandez à l’enseignant de vous fournir l’archive de départ. Après l’avoir décompressée, vous avez un répertoire qui contient le même résultat que celui obtenu à l’aide des commandes précédentes.
Déplacez vous dans le répertoire créé et tapez la commande suivante :
Nous allons modifier le thème fourni par Laravel
package.json
avec celui-ci (@fortawesome/fontawesome-free
en plus).assets.zip
et décompressez la dans le répertoire resources
.bienvenue.blade.php
dans le répertoire resources/views
.webpack.mix.js
avec celui-ci.Adaptation à l’environnement de l’IUT
La gestion du thème est assuré par le fichier webpack.mix.js
qui contient les instructions pour agréger les fichiers javascript et générer un fichier de style css. L’utilisation de webpack associé avec webpack.mix.js
nécessite de nombreuses librairies qui sont téléchargées depuis internet à l’aide de la commande npm install
. Pour accelerer cette phase vous suivrez les instructions suivantes :
robert.duchmol
dans le répertoire /local (remplacez robert.duchmol
par votre prénom et votre nom).node_modules.tar.gz
( Note : la décompression de cette archive correspond à l’exécution de la commande npm install
).Copier le fichier webpack.mix.js
dans le répertoire /local/robert.duchmol
Dans le fichier routes/web.php
, remplacez la ligne
par
Le répertoire tp_mvc
sera créé dans le répertoire courant. Sans aucune intervention supplémentaire, vous pouvez tester votre application à l’aide de la commande suivante.
cd /le/chemin/de/tp_mvc
php artisan serve
qui vous place dans le répertoire racine de votre application web. La commande php artisan serve
lance un serveur web qui écoute sur le port 8000
par défaut. Il va permettre de tester le résultat de votre développement. Vous pouvez tester que l’initialisation de votre projet est correcte en tapant l’URL http://localhost:8000 dans votre navigateur.
La commande
php artisan
sera très souvent utilisée lancez la commandephp artisan
pour connaitre les options possibles
De ce TP nous allons reprendre la gestion de tâches du TP précédent. Les objectifs sont :
blade
,blade
Le moteur de templates Blade permet de créer les pages web sous forme de composants :
On peut en imaginer d’autres (_sidebar_, …). Les pages d’une application utilisent généralement la même structure, seules les données changes. En partant de ce principe, à l’aide de blade
nous allons construire le squelette de la page et y inclure des composants.
Voici le fichier master.blade.php
à placer dans le répertoire resources/views/layout
(à créer) :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Web App</title>
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
@include('layout.navbar')
<div class="container">
@yield('content')
</div>
@include('layout.footer')
@yield('script')
<script src="/js/app.js"></script>
</body>
</html>
Les balises @include
indique l’inclusion d’un autre fichier
Note: Le
@include('layout.navbar')
permet d’inclure le fichierlayout/navbar.blade.php
Le fichier layout/navbar.blade.php
:
<!-- navigation bar -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="/">Gestion de tâches</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="/accueil">Accueil<span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/apropos">A Propos</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/contact">Contact</a>
</li>
</ul>
<ul class="navbar-nav ">
<li class="nav-item">
<a class="nav-link" href="/taches">Tâches</a>
</li>
<!-- PROFILE DROPDOWN - scrolling off the page to the right -->
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" id="navDropDownLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Profile
</a>
<div class="dropdown-menu" aria-labelledby="navDropDownLink">
<a class="dropdown-item" href="#">Preferences</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Logout</a>
</div>
</li>
</ul>
</div>
</nav>
et le fichier layout/footer.blade.php
:
<footer class="app-footer">
<div class="container">
<!--Copyright-->
<div >
<div class="container-fluid text-center">
Copyright © <?php echo date('Y'); ?> <a
href="http://www.iut-lens.univ-artois.fr/" target="_blank">
<b>IUT Lens - département Info.</b></a>
</div>
</div>
</div>
</footer>
Pour tester notre thème, nous allons modifier la page resources/views/welcome.blade.php
avec le code qui suit.
Note: La balise
@yield('content')
du fichierlayout/master.blade.php
sera remplacée par le code qui se trouve entre les balides@section('content')
et@endsection
@extends("layout.master")
@section('content')
<!--Start your project here-->
<div class="container">
<div class="row">
<div class="col-md-3">
<div class="view overlay hm-white-light z-depth-1-half">
<img src="{{asset('images/logo-iut.png')}}" class="img-fluid" alt="" style="margin-top: 2rem;">
</div>
<br>
</div>
<!--Main information-->
<div class="col-md-9">
<h2 class="h2-responsive">IUT de Lens</h2>
<hr>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ultricies euismod arcu. Aliquam
imperdiet laoreet nibh, sed posuere dolor feugiat sed. In in dolor et metus cursus ultrices. Nullam
blandit eu est a mollis. Cras elit quam, semper id ornare vitae, sodales eget nibh. Nulla dignissim
sodales quam, id placerat eros porttitor nec. Proin molestie eros vel tortor tempus, sit amet
placerat est pharetra. Vivamus a congue nibh, at lacinia libero. Nullam tristique posuere
feugiat.</p>
<p>Nam fermentum eget lectus commodo luctus. Pellentesque ornare pellentesque justo eget suscipit. Nam
pellentesque pulvinar neque sit amet elementum. Aliquam euismod neque sed ipsum scelerisque, sed
tempor enim varius. Sed quis scelerisque orci. Ut id vehicula magna. Etiam sed rutrum nunc, a
vulputate mi. Phasellus tortor tortor, tempor quis metus et, efficitur congue dui. Suspendisse ac
leo eros. Praesent lobortis diam nunc, eget tincidunt ipsum laoreet eget. Proin ut sapien sed neque
lacinia accumsan. Vivamus consectetur porta elementum. Etiam id laoreet risus, vitae blandit ipsum.
Integer libero libero, sodales et feugiat ut, finibus vel arcu. Ut quis tortor rhoncus, blandit est
ut, sodales purus.
Sed gravida consectetur ex, non viverra dui venenatis a.</p>
<a href="http://www.iut-lens.univ-artois.fr" class="btn btn-primary">Plus d'info!</a>
</div>
</div>
</div>
@endsection
Vérifier que le thème fonctionne.
Nous allons maintenant ajouter les pages suivantes :
Pour cela nous allons devoir modifier les règles de routage. Ces pages sont des pages de données que l’on peut considérer comme statique (très peu souvent modifiées). Nous allons les gérer à l’aide d’un contrôleur WelcomeController
. Pour créer le contrôleur WelcomeController
, il est possible d’utiliser la commande
Cette commande va créer le fichier WelcomeController.php
dans le répertoire app/Http/Controllers
, il contient
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class WelcomeController extends Controller {
//
}
La page d’accueil va afficher la déclaration universelle des droits de l’homme à partir d’un fichier au format markdown. Nous allons avoir besoin d’une librairie tiers michelf/php-markdown
, Pour la récupérer vous utiliserez la commande
Le fichier droitsDeLHomme.md
contient le code markdown de la déclaration universelle des droits de l’homme. Il faut ajouter le fichier droitsDeLHomme.md
dans le répertoire public/md
(qu’il faut créer)
Nous allons créer la classe MarkdownService
qui contient la méthode parse
qui transforme un fichier markdown
en html
. Voici le code de cette classe :
<?php
<?php
namespace App\Helpers;
use Michelf\MarkdownExtra;
class MarkdownService {
public static function parse($file) {
$file = file_get_contents(public_path('md').'/'.$file);
if ($file) {
$parser = new MarkdownExtra();
$parser->code_class_prefix = "language-";
$parser->code_attr_on_pre = true;
$html = $parser->transform($file);
return $html;
} else {
return $file."<br>";
}
}
}
L’affichage de la page d’accueil se fera à partir de la règle
La fonction index
du contrôleur va déclencher la transformation du code markdown en code html.
function index() {
return view('welcome.index')->with('htmlContent',MarkdownService::parse('droitsDeLHomme.md'));
}
Note: La méthode
index
utilise le templatewelcome.index
qui correspond au fichierresources/views/welcome/index.blade.php
. Le template utilise une balise de type{!! $var !!}
pour pouvoir afficher du code html.
Note: La route (
http://localhost:8000
) déclenche l’exécution de la méthodeindex
dans le contrôleurWelcomeController
. Il faudra donc modifier la route qui est indiqué dans le fichierroutes/web.php
.
Ajouter le fichier MarkdownService.php
dans le répertoire app
(trouvez le bon endroit !).
Ajouter la fonction index
dans le contrôleur WelcomeController
.
Créer le template ìndex
dans le fichier resources/views/welcome/index.blade.php
.
Récupérer le code des pages apropos.blade.php
et contact.blade.php
.
Modifier le contrôleur WelcomeController
.
Modifier le fichier des routes (le fichier routes/web.php
) en accord avec le contrôleur WelcomeController
.
Note: Les liens dans le fichier
navbar.blade.php
doivent être cohérents avec les routes que vous avez ajoutées.
Nous allons maintenant réutiliser la base de données utilisée dans le TP précédent.
Laravel propose une connexion simplifiée avec un système de base de données. Le fichier de configuration .env
permet de spécifier
Voici un extrait du fichier .env
qui concerne les informations de connexion avec un SGBD :
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=lar_tache
DB_USERNAME=duchmol
DB_PASSWORD=secret
Les informations indiquées dans le fichier .env
surchargent les informations données dans le fichier config/database.php
.
Modifier le fichier .env
pour établir une connexion entre votre application web et votre SGBD
Note: Pour que les modification dans le fichier
.env
soient prises en compte, il faut relancer le serveur de développement.php artisan serve
taches
Laravel propose une commande qui permet de créer l’infrastructure de gestion de la persistance de données dans une base de données.
Pour chaque table cette infrastructure se compose de trois éléments :
Pour créer l’infrastructure il faut exécuter la commande :
php artisan make:model Tache -mcr
Note: il est possible de créer chaque élément de l’infrastructure indépendamment des autres
php artisan make:model Tache php artisan make:migration --create=taches create_taches_table (-m) php artisan make:controller TacheController --resource (-cr)
Note: il est possible d’utiliser des tables d’une base de données qui existent déjà. Dans ce cas il suffit de créer uniquement le modèle et le contrôleur.
php artisan make:model Tache php artisan make:controller TacheController
Si le nom de la table n’est pas celui attendu (nom du modèle avec un pluriel et sans majuscule modèle
Tache
-> tabletaches
) il faut le spécifier, de la même façon pour la valeur de la clé primaire et l’ajout optionnel de la date de création et de modification d’un enregistrement.namespace App; use Illuminate\Database\Eloquent\Model; class Tache extends Model { protected $table = 'mes_taches'; public $primaryKey = 'ma_cle'; public $timestamps = false; }
Utiliser la commande pour créer l’infrastructure de la table taches
taches
Laravel dispose d’une commande qui permet de modifier les tables de la base de données.
php artisan migrate
Cependant avant d’utiliser cette commande il faut indiquer le contenu de la ou des tables. Pour cela il faut éditer le contenu du fichier database/migrations/xxx_create_taches_table.php
et plus particulièrement la fonction up()
(voir la documentation pour obtenir plus d’information sur les types de colonne disponibles).
public function up()
{
Schema::create('taches', function (Blueprint $table) {
$table->increments('id');
$table->date('expiration');
$table->string('categorie',30);
$table->text('description');
$table->enum('accomplie',['O','N']);
$table->timestamps();
});
}
Si vous souhaitez réinitialiser votre tables dans la base de données, Laravel propose la commande
php artisan migrate:refresh
Note: Le mécanisme de migration est géré par Laravel à l’aide d’une table
migration
qui est ajoutée dans votre base de données. Si vous avez des erreurs lors de la migration, et que vous ne voulez pas revenir en arrière, la suppression de cette table peut vous aider à repartir de zéro.
Note: Si vous avez l’erreur suivante :
[PDOException] SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes
Il faut ajouter le code suivant dans la classe
AppServiceProvider.php
(répertoireapp/Providers
)use Illuminate\Support\Facades\Schema; public function boot() { Schema::defaultStringLength(191); }
Modifier la fonction up()
et utiliser la commande qui permet de créer la table taches
taches
Laravel propose un mécanisme ‘seed’ qui permet d’ajouter un contenu aux tables de votre base de données. Nous allons utiliser ce mécanisme en association avec la librairie Faker
pour générer un contenu aléatoire dans la table taches
.
Note: Ce mécanisme est généralement utilisé pour mettre en place plus facilement un jeu de données pour tester votre application. Il vous faudra récupérer la librairie qui contient la classe
Faker
à l’aide de la commande :composer require --dev fzaninotto/faker
Ici on a utilisérequire --dev
comme commande car la librairie ne sera utilisée que pendant la phase de développement et de tests.
Dans un premier temps nous allons définir le domaine aléatoire de chaque colonne de la table taches
. A l’aide de la librairie Faker
, on peut définir simplement une fabrique d’enregistrements.
Le code qui suit placé dans le fichier database/factories/TacheFactory.php
permet de créer un enregistrement pour la table taches
.
$factory->define(
App\Tache::class,
function (Faker\Generator $faker) {
$createAt = $faker->dateTimeInInterval(
$startDate = '-6 months',
$interval = '+ 180 days',
$timezone = date_default_timezone_get()
);
return [
'expiration' => $faker->dateTimeInInterval(
$startDate = '-6 months',
$interval = '+ 180 days',
$timezone = date_default_timezone_get()
),
'categorie' => $faker->randomElement($array = array('Urgent', 'A Faire', 'Optionnel')),
'description' => $faker->paragraph,
'accomplie' => $faker->randomElement($array = array('O', 'N')),
'created_at' => $createAt,
'updated_at' => $faker->dateTimeInInterval(
$startDate = $createAt,
$interval = $createAt->diff(new DateTime('now'))->format("%R%a days"),
$timezone = date_default_timezone_get()
),
];
}
);
Dans un deuxième temps nous allons utiliser cette création aléatoire de données pour ajouter des données dans la table taches
. Pour cela nous allons créer une classe TacheTableSeeder
dans le répertoire database/seeds
.
La classe a le contenu suivant:
class TacheTableSeeder extends Seeder{
public function run()
{
factory(App\Tache::class, 10)->create();
}
}
L’exécution de la méthode run
va créer les enregistrements (10 ici) et les ajouter à la table taches
de la base de données.
Laravel propose une commande pour déclencher la génération et l’ajout de données aléatoire (et ou de tests) dans la base de données.
php artisan migrate:refresh --seed
qui a pour effet de ré-initialiser la base de données et d’y ajouter des données.
Cette commande lance l’exécution de la méthode run
de la classe DatabaseSeeder
dans le répertoire database/seeds
. Pour que notre classe TacheTableSeeder
soit utilisée, il faut ajouter la ligne
$this->call(TacheTableSeeder::class);
dans la méthode run
de la classe DatabaseSeeder
.
Note: Pour que la classe
TacheTableSeeder
soit visible, il faut réinitialisercomposer
à l’aide de la commande suivante :composer dumpautoload
Ajouter 20 tâches aléatoires dans la table taches
Dans la suite de ce TP nous allons utiliser l’API DB proposée par Laravel et en particulier les requêtes au format Query Builder. La documentation vous donne un aperçu des possibilités de l’API. Pour tester le bon fonctionnement de l’accès à notre modèle à partir de l’API, le plus simple est d’écrire un fichier de tests.
Pour cela nous allons utiliser une base de données de tests. Nous allons ajouter une configuration de base de données supplémentaire dans le fichier config/database.php
. Nous allons ajouter une connexion sqlite_testing
qui permet de faire les tests dans une base de données sqlite en mémoire. Après modification :
// début du fichier config/database.php
// ...
'connections' => [
'sqlite_testing' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
'sqlite' => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
],
//... autres configurations
La configuration
La classe DBTest
dans le répertoire tests/Unit
permet de tester les quatre opérations de base (Création, Selection, Modification, Suppression):
<?php
namespace Tests\Unit;
use App\Tache;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class DBTest extends TestCase {
/**
* Creates the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication() {
// Utilisation de l'environnement de test
putenv('DB_DEFAULT=sqlite_testing');
$app = require __DIR__.'/../../bootstrap/app.php';
$app->make('Illuminate\Contracts\Console\Kernel')->bootstrap();
return $app;
}
public function setUp() {
parent::setUp();
Artisan::call('migrate');
// Création des données de tests
factory(Tache::class)->create(
[
'expiration' => "2017-08-31",
'categorie' => 'Urgent',
'description' => 'Teste la table tache',
'accomplie' => 'N',
]
);
factory(Tache::class, 5)->create(['categorie' => 'Urgent']);
factory(Tache::class, 5)->create(['categorie' => 'Bidon']);
}
public function testModelTest() {
$taches = factory(Tache::class, 3)->make();
$this->assertEquals(count($taches), 3);
}
public function testAccesDBTest() {
$taches = DB::table('taches')->get();
$this->assertEquals(count($taches), 11);
}
public function testAccesDBWithIDTest() {
$tache = DB::table('taches')->where('id', 1)->first();
$this->assertEquals($tache->expiration, '2017-08-31');
$this->assertEquals($tache->categorie, 'Urgent');
$this->assertEquals($tache->description, 'Teste la table tache');
$this->assertEquals($tache->accomplie, 'N');
}
public function testUpdateDBWithIDTest() {
DB::table('taches')->where('id', 1)->update(
[
'categorie' => 'A Faire',
'description' => 'Teste modif de la table tache',
'accomplie' => 'O',
]
);
$tache = DB::table('taches')->where('id', 1)->first();
$this->assertEquals($tache->expiration, '2017-08-31');
$this->assertEquals($tache->categorie, 'A Faire');
$this->assertEquals($tache->description, 'Teste modif de la table tache');
$this->assertEquals($tache->accomplie, 'O');
}
public function testDeleteDBWithIDTest() {
DB::table('taches')->where('id', '>', 1)->delete();
$taches = DB::table('taches')->get();
$this->assertEquals(count($taches), 1);
}
public function tearDown() {
Artisan::call('migrate:reset');
parent::tearDown();
}
}
Tester le bon fonctionnement de l’API DB
En utilisant la documentation Query Builder et en analysant le fichier de tests, comment fait-on pour récupérer les tâches dans la table taches
Comment modifier un enregistrement de la table taches
à l’aide de l’API DB
Comment supprimer un enregistrement de la table taches
à l’aide de l’API DB
Laravel propose une commande qui crée un contrôleur ‘CRUD’ (_Create_, Read, Update, Delete) pour gérer les données d’une table. Ici nous allons donc créer un contrôleur CRUD pour gérer les tâches.
php artisan make:controller TacheController --resource
crée un fichier TacheController
dans le répertoire app/Http/Controllers
qui contient les méthodes permettant de gérer la table taches
.
Note: Vous avez peut être déjà créé le controleur dans une question précédente, dans ce cas il faut le supprimer et relancer la commande pour disposer de toutes les méthodes.
Le tableau tirer de la documentation laravel controllers résume les méthodes et l’utilisation qui en est faite.
Commande | URI | Action | Route Name |
---|---|---|---|
GET |
/taches |
index |
taches.index |
GET |
/taches/create |
create |
taches.create |
POST |
/taches |
store |
taches.store |
GET |
/taches/{tache} |
show |
taches.show |
GET |
/taches/{tache}/edit |
edit |
taches.edit |
PUT/PATCH |
/taches/{tache} |
update |
taches.update |
DELETE |
/taches/{tache} |
destroy |
taches.destroy |
Dans le fichier routes/web.php
la règle
Route::resource('taches', 'TacheController');
permet de relier, en une fois, les couples commandes, URI et les actions.
Laravel permet de simplifier l’écriture des templates blade en utilisant des directives. Dans la suite du TP nous allons utiliser deux directives :
accomplie
qui renvoie un boolean qui vaut false si l’expression $expr
est vide ou contient la chaîne de caractères “N” et true sinon;frdatetime
qui renvoie la date donnée en paramètre en français.Le code qui suit doit être ajouter dans la fonction boot
de la classe AppServiceProvider
dans le répertoire app/Providers
.
Blade::if('accomplie', function ($expr) {
return (empty(with($expr)) || with($expr) === 'N' ? false : true);
});
Blade::directive(
'frdatetime',
function ($expr) {
$ret = "<?php ";
$ret .= "setlocale(LC_TIME, 'fr_FR');";
$ret .= "echo Carbon\Carbon::parse(with({$expr}))->formatLocalized(\"%A %d %B %Y\");";
$ret .= "?>";
return $ret;
}
);
Créer le contrôleur TacheController
Ajouter les routes dans le fichier le fichier routes/web.php
Ajouter les directives dans le fichier AppServiceProvider
Nous avons créé le contrôleur TacheController
ainsi que les règles qui déclenchent la méthode à partir de l’URI, pour afficher la liste des tâches, il reste à
index
du contrôleur TacheController
;Les tâches vont être affichées dans un tableau une ligne du tableau correspond à une tâche. La ligne contiendra
frdatetime
pour avoir le bon format),La structure générale de la page tache/index.blade.php
est la suivante
{{-- A partir du layout master --}}
@extends("layout.master")
{{-- insertion de la section 'content' --}}
@section('content')
<h1>Liste des tâches</h1>
<div>
{{-- Bouton pour pouvoir ajouter une nouvelle tâche --}}
<p><a href="taches/create" class="btn btn-primary" role="button">Ajouter une tâche</a></p>
</div>
{{-- On affiche un tableau si il y a des tâches ... --}}
@if(!(count($taches) === 0))
<table class="table">
<thead style="background-color: #ddd; font-weight: bold;">
<tr>
<td class="header">Expiration</td>
<td class="header">Catégorie</td>
<td class="header">Description</td>
<td class="header">Réalisée</td>
<td>Modification/Suppression</td>
</tr>
</thead>
<tbody>
@foreach ($taches as $tache)
<tr>
<td>{{-- affichage de la date --}}</td>
<td>{{-- affichage de la catégorie --}}</td>
<td>{{-- affichage de la description --}}</td>
<td> {{-- affichage d'un icon qui indique si la tâche est accomplie ou pas --}}
@if($tache->accomplie == 'O')
<span class="far fa-check-square fa-2x"
aria-hidden="true"></span>
@else
<span class="far fa-square fa-2x"
aria-hidden="true"></span>
@endif
</td>
<td>{{-- affichage d'un bouton pour la modification et un bouton pour la suppression --}}
<div class="btn-group">
<a href="taches/{{$tache->id}}/edit" class="btn btn-warning" role="button">
<span class="far fa-edit fa-2x" aria-hidden="true"></span>
</a>
<a href="taches/{{$tache->id}}" class="btn btn-danger" role="button">
<span class="far fa-trash-alt fa-2x" aria-hidden="true"></span>
</a>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
@else
<div class="alert alert-warning" role="alert">Aucune tâche dans la base</div>
@endif
@endsection
Voici une figure qui représente la liste attendue :
Réaliser la page permettant d’affichier la liste des tâches
Il vous faudra pour cela compléter les fichiers : TacheController.php
(la méthode index) et index.blade.php
Ici, c’est la méthode create
du contrôleur TacheController
qui doit être déclenchée pour afficher l’écran de saisie d’une tâche. Une fois la saisie effectuée par l’utilisateur, il faut déclenchée la méthode store
qui doit vérifier que les données saisies sont valides et les stocker dans la base de données.
Voici un exemple de page html qui contient un formulaire de saisie d’une tâche :
@extends("layout.master")
@section('content')
{{-- Affiche les erreurs --}}
@if ($errors->any())
<div class="alert alert-danger" style="margin-top: 2rem">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
{{--
formulaire de saisie d'une tâche
la fonction 'route' utilise un nom de route
'csrf_field' ajoute un champ caché qui permet de vérifier
que le formulaire vient du serveur.
--}}
<form action="{{route('taches.store')}}" method="POST">
{!! csrf_field() !!}
<div class="text-center" style="margin-top: 2rem">
<h3><i class="far fa-edit"></i> Création d'une tâche</h3>
<hr class="mt-2 mb-2">
</div>
<div class="form-group row">
<label class="col-md-3 form-control-label" for="expiration"><strong>Date d'expiration
: </strong></label>
<div class="col-md-3">
<div class="input-group date">
<span class="input-group-addon">
<i class="far fa-calendar"></i>
</span>
<input type="date" class="form-control" name="expiration" id="expiration"
value="{{ old('expiration') }}" class="form-control"
placeholder="aaaa-mm-jj">
</div>
</div>
</div>
<div class="form-group row">
{{-- la catégorie --}}
<label class="col-md-3 form-control-label" for="categorie"></label>
<div class="col-md-4">
{{-- input de la catégorie --}}
</div>
<label class="col-md-2 form-control-label" for="select">
<strong>Accomplie ?</strong>
</label>
<div class="btn-group btn-group-toggle" data-toggle="buttons" class="col-md-2">
<label class="btn btn-outline-primary @accomplie(old('accomplie')) active @endaccomplie">
<input type="radio" name="accomplie" value = "O" id="accomplie-on"> Oui
</label>
<label class="btn btn-outline-primary @accomplie(old('accomplie')) @else active @endaccomplie">
<input type="radio" name="accomplie" value = "N" id="accomplie-off"> Non
</label>
</div>
</div>
<div class="form-group row">
<label class="col-md-3 form-control-label" for="textarea-input">Description :</label>
<div class="col-md-9">
<textarea name="description" id="description" rows="6" class="form-control"
value="{{ old('description') }}" placeholder="Description.."></textarea>
</div>
</div>
<div class="text-center">
<button class="btn btn-success" type="submit">Valide</button>
</div>
</form>
@endsection
La ligne
{!! csrf_field() !!}
permet de vérifier que le formulaire provient bien du serveur.
Les premières lignes vont permettre d’afficher les erreurs constatées lors de la précédente saisie. La variable errors
est gérée par Laravel et contient le résultat de la vérification des données saisies. On verra dans la suite du TP comment on spécifie des règles de validation des données saisies.
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
Note: Vous aurez noté que le formulaire renvoie le traitement du formulaire vers la route nommée
taches.store
en utilisant la commandePOST
. Ce qui provoquera l’exécution de la méthodestore
du controleurTacheController
(voir le tableau de correspondance entre les couples commandes, URI et les actions).
old
Laravel met à votre disposition (stocke dans une variable flash) les valeurs saisies dans un formulaire pour pouvoir les ré-injecter dans le même formulaire en cas d’erreur (voir la documentation old input).
Note: En effet, en cas d’erreur dans la saisie d’un formulaire (voir la suite du TP), Laravel redirige la requête vers la page qui contient le formulaire. La fonction
old
, vous aide à extraire les valeurs précédemment saisies pour les afficher dans le formulaire. Sans ce mécanisme, du fait de la redirection (nouvelle requête), les données saisies seraient perdues (il faudrait les stocker dans une session).
Mettre en place la saisie d’une tâche
Comme vu précédemment, la fin de la saisie du formulaire déclenche l’exécution de la fonction store
du contrôleur TacheController
. Cette fonction va valider les données saisies et seulement si toutes les données sont correctes alors la sauvegarde dans la base de données est effectuée. En cas d’erreur, Laravel renvoie automatiquement vers la page contenant le formulaire, le programmeur peut alors indiquer les erreurs à l’utilisateur en utilisant la variable errors
.
Laravel propose un mécanisme de validation qui fonctionne à l’aide de règles de validation. Pour plus de précisions voir la documentation sur les règles de validation. Il est possible d’indiquer :
L’extrait de programme suivant donne un exemple de règles pour valider la saisie d’une tâche.
$this->validate(
$request,
[
'expiration' => 'required|',
'categorie' => 'required',
'description' => 'required',
'accomplie' => 'nullable',
]
);
Si la fonction s’exécute sans erreur alors le code qui suit est exécuté, sinon la requête est redirigée vers la page contenant le formulaire.
Voici le code complet de la fonction store
:
public function store(Request $request)
{
$this->validate(
$request,
[
'expiration' => 'required|',
'categorie' => 'required',
'description' => 'required',
'accomplie' => 'nullable',
]
);
$input = $request->only(['expiration', 'categorie', 'description', 'accomplie']);
DB::table('taches')->insert(
[
'expiration' => $input['expiration'],
'categorie' => $input['categorie'],
'description' => $input['description'],
'accomplie' => (isset($input['accomplie']) ? 'O' : 'N'),
]
);
return redirect('/taches');
}
Le paramètre request
de type Request
contient les informations de la requête (voir la documentation HTTP requests) et en particulier les données saisies dans le formulaire. L’instruction
$input = $request->only(['expiration', 'categorie', 'description', 'accomplie']);
stocke dans la variable $input
de type tableau associatif les champs contenant les informations de la tâche à créer. L’instruction qui suit (dans le code qui précède), déclenche l’insertion d’un nouvel enregistrement dans la table taches
.
Mettre en place la validation et le stockage d’une tâche
Pour modifier une tâche, il faut commencer par récupérer son numéro. Celui-ci sera fourni à partir de la route
/taches/{tache}/edit
qui va déclencher la méthode edit
du contrôleur. Cette méthode accepte un paramètre qui est le numéro de la tâche
public function edit($id) {...}
La modification d’une tâche utilise à peu de chose près, le même formulaire de saisie.
Les différences sont
tache/edit.blade.php
)Voici le code de la page tache/edit.blade.php
@extends("layout.master")
@section('content')
{{-- Affiche les erreurs --}}
@if ($errors->any())
<div class="alert alert-danger" style="margin-top: 2rem">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{route('taches.update', ['id' => $tache->id])}}" method="POST">
{!! csrf_field() !!}
{!! method_field('PUT') !!}
<div class="text-center">
<h3><i class="far fa-edit"></i> Modification d'une tâche</h3>
<hr class="mt-2 mb-2">
</div>
<div class="form-group row">
<label class="col-md-3 form-control-label" for="select"><strong>Date d'expiration : </strong></label>
<div class="col-md-3">
<div class="input-group date">
<span class="input-group-addon">
<i class="far fa-calendar-alt"></i>
</span>
<input type="text" class="form-control" name="expiration"
value="{{ $tache->expiration }}" class="form-control"
placeholder="aaaa-mm-jj">
</div>
</div>
</div>
<div class="form-group row">
{{-- categorie--}}
{{-- Accomplie--}}
</div>
{{-- Description--}}
<div class="form-group row">
</div>
<div class="text-center">
<button class="btn btn-warning" type="submit" name="valide" value="valide">Valide</button>
<button class="btn btn-success" type="submit" name="annule" value="annule">Annule</button>
</div>
</form>
@endsection
Note : La ligne
{!! method_field('PUT') !!}
est utilisée car la balise
form
en HTML ne permet pas d’indiquerPUT
,PATCH
,DELETE
comme valeur pour l’attributmethod
. Laravel utilise un champ caché dans le formulaire pour l’indiquer au service de routage du serveur qui traitera la requête comme une requête avec la commandePUT
.Le nom de la route
taches.update
est utilisée avec un paramètreid
de valeur$tache->id
.
Le formulaire après validation doit déclencher la fonction update
du contrôleur, qui comme la fonction store
doit valider la saisie avant de modifier la base de données.
$tache = DB::table('taches')->where('id', $id)->first();
$acc = $request->get('accomplie') === null ? $tache-> accomplie : $request->get('accomplie');
// ...
DB::table('taches')->where('id', $id)
->update(
[
'expiration' => $input['expiration'],
'categorie' => $input['categorie'],
'description' => $input['description'],
'accomplie' => $acc,
]
);
Mettre en place la modification d’une tâche
Pour supprimer une tâche, il faut exécuter l’instruction suivante
DB::table('taches')->where('id', $id)->delete();
Mais il est d’usage de demander une validation à l’utilisateur avant la suppression définitive des données. Pour cela nous allons dans un premier temps afficher les données de la tâche en utilisant la fonction show
du contrôleur et demander à l’utilisateur de valider la suppression.
Voici le code de la page tache/show.blade.php
@extends("layout.master")
@section('content')
<h1 style="margin-top: 2rem">Suppression d'un tâche</h1>
<div class="card">
<div class="card-block">
<h3 class="card-title">@frdatetime($tache->expiration)
<span class="badge badge-danger">
{{$tache->categorie}}
</span>
@if($tache->accomplie == 'O')
<span class="tag red fa fa-check fa-2x"
aria-hidden="true"></span>
@endif
</h3>
<p class="card-text">{{$tache->description}}</p>
<form action="/taches/{{$tache->id}}" method="POST">
{!! csrf_field() !!}
{!! method_field('DELETE') !!}
<div class="text-center">
<button class="btn btn-danger" type="submit" name="valide" value="valide"><span
class="fas fa-trash-alt fa-2x"
aria-hidden="true"></span></button>
<button class="btn btn-success" type="submit" name="annule" value="annule"><span
class="fas fa-undo-alt fa-2x"
aria-hidden="true"></span></button>
</div>
</form>
</div>
</div>
@endsection
Note : Même chose que précédemment pour la ligne
{!! method_field('DELETE') !!}
qui indique au service de routage une requête de suppression.
On exécutera la fonction destroy
du contrôleur qui dans le cas d’une validation de la suppression exécutera l’instruction précédente et en cas d’annulation ne fera rien. La dernière instruction redirigera vers l’affichage de la liste des tâches.
return redirect('/taches');
Mettre en place la suppression d’une tâche
IUT de Lens Département Informatique
2018