Laravel - noelno/dovelei GitHub Wiki

Laravel

Installation

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.

Routes

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.";
    });

});

Middlewares

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(){

    //…

});

Contrôleurs

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

Vues

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])

Base de données

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.

Syntaxe Eloquent

INSERT

$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'];
}

UPDATE

$post = App\Post::find(1);
$post->title = "Test";
$post->save();

	class Post extends Model {

		protected $fillable = ['title', 'slug', 'content'];

	}

SELECT

$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

DELETE

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();

TP : créer un raccourcisseur d'url

Etape 1 : modification de la base de données

  • 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 table links :
	public function up()
	{
		Schema::create('links', function(Blueprint $table)
		{
			$table->increments('id');
			$table->string('url')->unique();
			$table->timestamps();
		});
	}

Lancer la migration : php artisan migrate

Etape 2 : Création de la page links/create

  • Créer un nouveau contrôleur : php artisan make:controller LinksController
  • Editer le controller pour lui ajouter une fonction create(), qui va retourner la vue links/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.

Etape 3 : Création de la page links/store

  • 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 dans routes/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'));
    }
}

Etape 4 : Création de la page links/success

  • Créer dans resources/views/links un fichier success.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().

Etape 5 : Optimisation du routing

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).

Formulaire

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));
    }
}

Debug

dd() : die and debug - un peu l'équivalent Laravel de var_dump() + die()

Tinker

php artisan tinker //pour tester des fonctions dans le cli

Sources

  • Tutoriels vidéo de Grafikart
⚠️ **GitHub.com Fallback** ⚠️