Laravel - noelno/dovelei GitHub Wiki
Avec Composer, en ligne de commande :
composer create-project laravel/laravel --prefer-dist mon-projet-laravel
La page d'accueil est accessible non pas à la racine du projet mais dans le sous-dossier /public
.
Note : si une erreur "The bootstrap/cache directory must be present and writable." apparaît, lancer la commande php artisan cache:clear
à la racine du site.
Les routes permettent de définir pour chaque structure d'URL ce qui sera exécuté / affiché.
Elles sont définies dans le fichier /app/routes/web.php
.
Format de base d'une route :
Route::get('salut', function(){
echo "Salut à toi, inconnu(e).";
});
Pour définir une nouvelle route on utilise une des méthodes statiques de la facade Route
. Ici on définit une simple URL : mon-projet-laravel/public/salut
.
Lorsque le client demande cette page, Laravel exécute la fonction anonyme qui lui est passée en paramètre et affiche le résultat. Notre page /salut
contient donc juste le texte "Salut à toi, inconnu(e).".
Pour récupérer un paramètre :
Route::get('salut/{name}', function($name){
echo "Salut à toi $name";
});
Si j'accède à la page /salut/Jeanne, celle-ci affichera "Salut à toi Jeanne".
On note que la fonction anonyme a besoin que l'on lui transmette le paramètre pour pouvoir le traiter.
Il est possible de récupérer plusieurs paramètres sans qu'ils soient nécessairement séparés par des slashes :
Route::get('salut/{slug}-{id}', function($slug, $id){
echo "Slug : $slug, ID : $id";
})->where('slug','[a-z0-9\-]+');->where('id','[0-9]+');
Si j'accède à la page /salut/slug-comme-ceci-3352
, celle-ci affichera "Slug : slug-comme-ceci, ID : 3352".
La méthode where
permet de définir la structure du paramètre attendu via une expression régulière.
Dans l'exemple qui précède, si l'on n'avait pas précisé que id
était numérique, l'affichage aurait été Slug : slug, ID : comme-ceci-3352
.
Pour récupérer la liste des routes dans le terminal, se placer dans le répertoire projet et saisir la commande suivante :
php artisan route:list
Il est possible de nommer ses routes, ce qui facilite leur réutilisation dans la méthode callback. Pour cela il faut changer le second paramètre de la méthode Route : au lieu de passer directement la méthode callback, on la met dans un tableau, à côté du nom de la route :
Route::get('salut/{name}', ['as' => 'ma-route', function($name){
echo "Url de la page : ".route('salut',['name' => $name]);//on n'oublie pas de passer les paramètres à la route
}]);
On peut créer des groupes de routes qui seront toutes rattachées à la même page. Par exemple toutes les routes déclarées dans le groupe suivant seront accessibles à admin/*
:
Route::group(['prefix' => 'admin'], function(){
//admin/salut
Route::get('salut', function(){
echo "Salut à toi, inconnu.";
});
});
Un middleware connecte deux pans de l'application sous condition, au moment d'une requête utilisateur. Par exemple le middleware auth
vérifie que l'utilisateur est authentifié avant d'accéder à sa requête.
Les middlewares sont déclarés dans app/Http/Kernel.php
:
- dans
$middleware
pour les middlewares utilisés sur toutes les pages (global) - dans
$routeMiddleware
pour les middlewares spécifiques à une route ou un groupe de routes
Créer un nouveau middleware en ligne de commande :
php artisan make:middleware nom_de_mon_middleware
Le fichier nom_de_mon_middleware.php est alors créé dans app/Http/Middleware
.
Il contient une méthode handle
qui reçoit en paramètre la requête ($request
) utilisateur, et la suite de l'exécution du programme ($next
). Il suffit d'ajouter un traitement conditionnel qui si la condition est remplie retourne la suite du programme.
Penser à ajouter le lien vers ce middleware dans Kernel.php
. S'il s'agit d'un middleware spécifique à une route, il faudra le préciser dans la définition de la route.
Route::group(['prefix' => 'admin','middleware' => 'auth'], function(){
//…
});
Les contrôleurs sont dans app/Http/Controllers
. Cf. app/Http/Providers/RouteServiceProvider.php
pour changer le namespace des contrôleurs.
Route::get('/', 'WelcomeController@index'); //quand l'utilisateur demande la racine du site, on charge la méthode index du contrôleur WelcomeController
On peut déclarer son contrôleur dans le constructeur du contrôleur plutôt que dans la déclaration de la route pour une meilleure séparation des préoccupations.
//dans routes.php
Route::get('home', 'HomeController@index'); //pas besoin de spécifier 'middleware' => 'auth' puisqu'on le fait après dans __construct
//dans HomeController.php
public function __construct()
{
$this->middleware('auth');
}
Sinon on utilisera le mot-clé 'uses' pour définir le contrôleur s'il y a plusieurs autres options de configuration.
Route::get('home', [as => 'accueil', 'uses' => 'HomeController@index'); //pas besoin de spécifier 'middleware' => 'auth' puisqu'on le fait après dans __construct
La méthode déclarée dans la route récupère en paramètre les paramètres définis dans la route.
//dans routes.php
Route::get('/salut/{name}', 'WelcomeController@index');
//dans WelcomeController.php
public function index($name)
{
return "Bienvenue $name";
}
Dans un contrôleur on ne reçoit pas $request en paramètre mais on peut accéder à la facade Request. Il faudra cependant penser à ajouter use \Request; en début de fichier, Request n'étant pas dans le même namespace que les controleurs.
Les facades sont déclarées dans le fichier /config/app.php.
Il est aussi possible de lier une route directement à tout le contrôleur. Chaque méthode publique déclarée commençant par get sera lié à une page.
//dans routes.php
Route::get('admin', 'AdminController');
//dans AdminController.php
public function getIndex()
{
return "Bienvenue dans l'administration du site"; //s'affiche quand on accède à admin/index
}
public function getConfig()
{
return "Configuration du site"; //s'affiche quand on accède à admin/config
}
Nouveau contrôleur en ligne de commande
php artisan make:controller monnomdeController //le nom doit toujours finir par Controller
Les vues sont rangées dans resources/views
. Pour appeler une vue on utilisera la syntaxe suivante :
return view('pages/about'); //charge resources/views/pages/about.blade.php
Laravel utilise le moteur de template Blade dont la syntaxe se constitue de formes raccourcies de structures usuelles en PHP. Par exemple pour afficher une variable en PHP on utilise d'habitude <?= $maVar ?>
. Blade utilise la notation {{ $maVar }}
.
Les vues peuvent hériter d'autres vues grâce à la déclaration @extends('mavue'). La vue mavue.blade.php doit exister dans resources/views
.
On peut déclarer du contenu entre @section('masection')
et @endsection
, puis afficher ce contenu dans le template avec @yield('masection').
On peut transmettre des paramètres à la vue :
return view('pages/about', ['title' => $title,'date' => $date]);
puis afficher le contenu de la variable dans le template avec la syntaxe vue précédemment : {{ $title }}
Attention : Blade échappe le HTML des variables sauf si vous utilisez la notation {!! $title !!}
Autres déclarations :
@section('content') / @show
- tout le contenu placé entre sera affiché directement à l'endroit où il est déclaré
@section('content',$content) - équivaut à :
@section('content')
{{ $title }}
@endsection
Dans la logique d'héritage de Blade, on peut surcharger la déclaration d'une section :
//dans default.blade.php
@section('content')
{{ $title }}
@endsection
//dans custom.blade.php
@extends('default')
@section('content')
{{ $name }}
@show // $name sera affiché quand on appellera la vue custom, et $title quand on appeldera default.
Et appeler la section parent directement avec @parent
:
//dans custom.blade.php
@extends('default')
@section('content')
@parent
{{ $name }}
@show // $title et $name seront affichés quand on appellera la vue custom.
Les principales structures de php existent en version Blade :
@if (!empty($name))
<p>{{ $name }}</p>
@elseif (!empty($surname))
<p>{{ $surname }}</p>
@endif
@foreach($numbers as $number)
<li>{{ $number }}</li>
@endforeach
Ainsi que forelse / empty pour offrir une alternative peu verbeuse quand il n'y a rien dans l'objet à itérer :
@forelse($searchresults as $result)
<li>{{ $result }}</li>
@empty
<p>Aucun résultat n'a été trouvé.</p>
@endforelse
Enfin on peut inclure des éléments récurrents dans des vues grâce à @include :
@include('share', ['title' => $title,'date' => $date])
Le paramétrage de la base de données se fait dans /.env
.
Les migrations sont des fichiers qui permettent de versionner la base de données. Elles se trouvent dans database/migrations
.
Par défaut il existe deux migrations prêtes à l'usage : une table users
et une table des demandes de reset des mots de passe utilisateurs.
Créer une nouvelle table, par exemple une table "Posts" :
php artisan make:migration create_posts_table --create=posts
create_posts_table
est le nom de ma migration, et --create=posts
est un flag indiquant à artisan de mettre en place le code source par défaut pour la création d'une table s'appelant posts
dans le fichier de migration.
Il suffit ensuite de modifier le fichier de migration nouvellement créé (sous la forme AAAA_MM_JJ_hhiiss_nom_de_ma_migration
, en définissant les champs de la table dans la fonction up()
.
La fonction up()
s'exécute quand la migration est lancée, et la fonction down()
s'exécute pour annuler cette migration.
Lancer la migration : php artisan migrate
Annuler la migration : php artisan migrate:rollback
Les modèles permettent d'interagir avec la base de données. Ils se trouvent à la racine de \app
.
Créer un nouveau modèle : php artisan make:model Post
Le modèle correspond au nom de la table au singulier, première lettre en majuscule. Par exemple un modèle Post
correspond à une table posts
dans la base de données.
$post = new App\Post();
$post (pour afficher son contenu)
$post->title = "Article de test";
$post->slug = "article-de-test";
$post->content = "Lorem ipsum";
$post->save();
$post = App\Post::create(['title' => 'Article 2', 'slug' => 'article-2', 'content' => 'azezaezae']);
$post->save();
Pour utiliser cette syntaxe, il faut obligatoirement spécifier les champs autorisés à recevoir un input utilisateur dans le modèle associé, dans $fillable
:
class Post extends Model {
protected $fillable = ['title', 'slug', 'content'];
}
$post = App\Post::find(1);
$post->title = "Test";
$post->save();
class Post extends Model {
protected $fillable = ['title', 'slug', 'content'];
}
$post1 = App\Post::find(1); //renvoie null si non trouvé
$post2 = App\Post::findOrFail(1); //lance une exception si non trouvé
$post3 = App\Post::where('title','Article 2')->get(); //retourne un tableau
$post4 = App\Post::where('title','Article 2')->first(); //retourne le premier correspondant
$post5 = App\Post::where('title','Article 2')->select(['id', 'title', 'slug'])->get(); //retourne uniquement les champs spécifiés en paramètre de select();
$post6 = App\Post::where('title','Article 2')->select(['id', 'title', 'slug'])->get()->toArray(); //retourne les posts sous forme de tableau plutôt que des objets
App\Post::find(1)->delete();
App\Post::destroy([1,2,4]);//supprime les posts d'id 1, 2 et 4
App\Post::where('title','Article 2')->delete();
- Créer un nouveau modèle
Link
(+ une migration) :php artisan make:model Link
- Éditer la migration
…_create_links_table
pour déclarer les champs de la tablelinks
:
public function up()
{
Schema::create('links', function(Blueprint $table)
{
$table->increments('id');
$table->string('url')->unique();
$table->timestamps();
});
}
Lancer la migration : php artisan migrate
- Créer un nouveau contrôleur :
php artisan make:controller LinksController
- Editer le controller pour lui ajouter une fonction
create()
, qui va retourner la vuelinks/create
(que l'on créera dans/resources/views/links/create.blade.php
) :
public function create(){
return view('links.create');
}
- Créer la vue
links/create
dans/resources/views/links/create.blade.php
, et créer un formulaire bootstrap qui contiendra notamment un token csrf (nécessaire pour valider les données postées) :
@extends('default')
@section('content')
<form action="" method="post">
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
<div class="form-group">
<label for="url">Url à raccourcir</label>
<input type="url" class="form-control" id="url" name="url">
<button type="submit" class="btn btn-primary">Raccourcir</button>
</div>
</form>
@stop
- Déclarer une nouvelle route dans
routes/web.php
:Route::get('links/create', 'LinksController@create');
La page est désormais accessible à monsite/links/create.
- Editer le modèle pour spécifier que le champ
url
peut être rempli :
class Link extends Model {
protected $fillable = ['url'];
}
-
Déclarer une nouvelle route pour la récupération de la valeur du champ
url
dansroutes/web.php
:Route::post('links/create', 'LinksController@store');
-
Créer une nouvelle fonction
store
dans le controller, qui va récupérer la valeur de url et créer une nouvelle entrée dans la table Link (db)
Si la classe \Input
n'existe pas, ajouter cette ligne dans le tableau $aliases
de config/app.php
:
'Input' => Illuminate\Support\Facades\Input::class,
- dans LinksController@store, récupérer et sauvegarder l'url postée dans la table Link (si l'url n'y est pas déjà), et transmettre à la vue "success" (qui sera créée par la suite) le lien.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Link;
use \Input;
class LinksController extends Controller
{
public function create(){
return view('links.create');
}
public function store(){
$url = \Input::get('url');
$link = Link::firstOrCreate(['url' => $url]);
return view('links.success', compact('link'));
}
}
- Créer dans
resources/views/links
un fichiersuccess.blade.php
. Cette vue reçoit en paramètre$link
.
@extends('default')
@section('content')
<h1>Bravo !</h1>
<a class="btn btn-primary">/r/{{ $link->id }}</a>
@stop
Lorsque l'utilisateur générera un lien raccourci, il obtiendra un lien de type monsite.com/r/12
- Créer une route correspondant à l'url raccourcie :
Route::get('r/{id}', 'LinksController@show')->where('id', '[0-9]+');
- Créer une fonction show() dans le LinksController qui va se charger de rediriger l'utilisateur à partir de son lien raccourci :
use Illuminate\Http\RedirectResponse;
public function show($id){
$link = Link::findOrFail($id);
return new RedirectResponse($link->url, 301); //ou son alias redirect()
}
-
action()
génère une url à partir de la route d'un controller et de ses éventuels paramètres.
@extends('default')
@section('content')
<h1>Bravo !</h1>
<a class="btn btn-primary" href="{{ action('LinksController@show',['id' => $link->id]) }}">{{ action('LinksController@show',['id' => $link->id]) }}</a>
@stop
action()
génerera un lien monsite.com/public/r/12
: Lorsque l'utilisateur accèdera à cette url, il sera automatiquement redirigée à l'url correspondante via show()
.
Dans ce TP on a mis en place notre propre architecture pour créer et séléctionner des liens, mais il aurait aussi été possible d'automatiser la création de routes pour créer une API REST :
#Route::get('links/create', 'LinksController@create');
#Route::post('links/create', 'LinksController@store');
Route::resource('link','LinksController', ['only' => ['create','store']]); //link est la table, LinksController le controlleur associé.
Les controlleurs liés à ces routes sont entre autres index, create, store, edit et update (edit affiche la vue d'édition, alors qu'update fait un update dans la base de données.
cf. php artisan route:list
pour voir les méthodes à créer.
Le système mis en place dans ce TP est conforme à l'API, à un détail près : si l'on veut conserver une url réecrite de forme monsite/r/id
on ne pourra pas utiliser la route par défaut pour la méthode show (qui est monsite/link/id
).
On déclarera donc la route suivante :
Route::get('r/{link}', ['as' => 'link.show', 'uses' => 'LinksController@show'])->where('link', '[0-9]+');
Ici on donne un nom à cette route afin de pouvoir l'utiliser en lieu et place de la fonction action()
:
<a class="btn btn-primary" href="{{ route('link.show', $link) }}">{{ route('link.show', $link) }}</a>
Notez que l'on ne passe plus l'id du lien en paramètre mais le lien entier ($link
).
Installer le package "laravelcollective/html".
composer require laravelcollective/html:5.4.*
Exemple de la vue edit créée avec ce package :
@extends('default')
@section('content')
<h1>Editer</h1>
{{ Form::open(['method' => 'put', 'url' => route('news.update', $post)]) }}
<div class="form-group">
{{ Form::label('label', 'Titre de l\'article') }}
{{ Form::text('title', $post->title, ['class' => 'form-control']) }}
</div>
<div class="form-group">
{{ Form::label('slug', 'URL') }}
{{ Form::text('slug', $post->slug, ['class' => 'form-control']) }}
</div>
<div class="form-group">
{{ Form::label('content', 'Contenu') }}
{{ Form::textarea('content', $post->content, ['class' => 'form-control']) }}
</div>
<div class="form-group">
<label for="online">
{{ Form::checkbox('online', 1, $post->online) }}
Publier l'article
</label>
</div>
<button class="btn btn-primary">Envoyer</button>
{{ Form::close() }}
@stop
Et le détail du contrôleur associé :
class PostsController extends Controller
{
public function index(){
$posts = Post::get();
return view('posts.index', compact('posts'));
}
public function create(){
return view('posts.create');
}
public function edit($id){
$post = Post::FindOrFail($id);
return view('posts.edit', compact('post'));
}
public function store(Request $request){
$post = Post::create($request->all());
return redirect(route('news.edit', $post));
}
public function update($id, Request $request){
$post = Post::findOrFail($id);
$post->update($request->all());
return redirect(route('news.edit', $id));
}
}
dd()
: die and debug - un peu l'équivalent Laravel de var_dump()
+ die()
php artisan tinker //pour tester des fonctions dans le cli
- Tutoriels vidéo de Grafikart