XXXII. Chapitre 32 : Organiser son code▲
XXXII-A. Les trois façons d'ajouter du code▲
Quand on veut organiser son code personnel avec Laravel on est de prime abord un peu décontenancé. Tout est si bien organisé qu'on a du mal à décider comment s'y prendre pour ajouter quelque chose. Surtout quand on nous dit qu'on peut le mettre où on veut !
Mais où qu'on le place il faut qu'il soit accessible. On peut résumer la situation à trois cas :
XXXII-A-1. Ajout de fonctions▲
Je veux juste ajouter des fonctions personnelles, par exemple pour ajouter des helpers. Dans ce cas je crée un fichier app/helpers.php avec mes fonctions. Pour que mes fonctions soient accessibles il faut que je renseigne Composer en créant une rubrique files si elle n'existe pas encore :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
"autoload"
:
{
"classmap"
:
[
"app/commands"
,
"app/controllers"
,
"app/models"
,
"app/database/migrations"
,
"app/database/seeds"
,
"app/tests/TestCase.php"
],
"files"
:
[
"app/helpers.php"
]
},
Et évidemment je fais un dumpautoload ensuite, pour que ça fonctionne .
Une autre solution équivalente consiste à déclarer le fichier dans app/start/global.php :
require_once __DIR__.
'
/../helpers.php
'
;
C'est d'ailleurs la solution donnée par Taylor Otwell dans son bouquin. Mais je préfère quand même ne pas toucher au code de base de Laravel, et me contenter d'intervenir uniquement dans Composer, ce qui me paraît plus logique. Mais vous avez le choix !
XXXII-A-2. Ajout de classes▲
Supposons maintenant que je veuille ajouter quelques classes. Je crée un dossier app/lib pour les ranger. Ensuite j'ajoute mon dossier dans la rubrique classmap :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
"autoload"
:
{
"classmap"
:
[
"app/commands"
,
"app/controllers"
,
"app/models"
,
"app/database/migrations"
,
"app/database/seeds"
,
"app/tests/TestCase.php"
,
"app/lib"
,
]
},
Avec un petit dumpautoload toutes mes classes dans ce dossier seront reconnues.
XXXII-A-3. PSR-0▲
Maintenant prenons le cas le plus riche. J'organise mon code dans des dossiers hiérarchisés en suivant les préconisations psr-0. C'est-à-dire que j'utilise des espaces de noms qui correspondent à l'architecture de mes dossiers. Voici la syntaxe dans ce cas :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
"autoload"
:
{
"classmap"
:
[
"app/commands"
,
"app/controllers"
,
"app/models"
,
"app/database/migrations"
,
"app/database/seeds"
,
"app/tests/TestCase.php"
],
"psr-0"
:
{
"MesClasses\\"
:
"app"
}
},
Après un dumpautoload, mes classes organisées dans mon espace de nom MesClasses et situées dans le dossier app seront reconnues.
XXXII-B. Un cas d'exemple▲
Pour voir ces trois possibilités en action je vais prendre un exemple simple d'application. À partir d'une installation neuve de Laravel et après avoir configuré une base de données, on crée une table livres avec cette migration :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
class CreateLivresTable extends
Migration {
public
function
up()
{
Schema::
create('livres'
,
function
(Blueprint $table
) {
$table
->
increments('id'
);
$table
->
timestamps();
$table
->
string('titre'
,
100
);
$table
->
string('auteur'
,
100
);
$table
->
string('editeur'
,
100
);
}
);
}
public
function
down()
{
Schema::
drop('livres'
);
}
}
Donc on obtient une simple table avec un ID, trois champs de données et les timestamps. Pas vraiment réaliste évidemment, mais le but est juste de montrer l'organisation du code, pas de créer une application réelle. Pour les besoins des tests je crée aussi quelques enregistrements :
J'ai maintenant vingt enregistrements à disposition. Il ne me reste plus qu'à créer le code pour gérer tout ça. Il me faut un modèle Livre :
2.
3.
4.
5.
6.
7.
8.
class Livre extends
Eloquent {
protected
$table
=
'livres'
;
public
$timestamps
=
true
;
protected
$softDelete
=
false
;
protected
$guarded
=
array
('id'
);
}
On ajoute un contrôleur qui va se contenter d'afficher les livres avec une pagination et de permettre l'ajout de livres avec une validation :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
class LivreController extends
BaseController {
public
function
getIndex()
{
$livres
=
Livre::
paginate(5
);
return
View::
make('hello'
)->
with('livres'
,
$livres
);
}
public
function
getCreate()
{
return
View::
make('saisie'
);
}
public
function
postCreate()
{
$rules
=
array
(
'titre'
=>
'required|alpha|unique:livres'
,
'auteur'
=>
'required|alpha'
,
'editeur'
=>
'required|alpha'
);
$validator
=
Validator::
make(Input::
all(),
$rules
);
if
($validator
->
passes()) {
$livre
=
new
Livre;
$livre
->
create(Input::
all());
return
'Livre enregistré'
;
}
else
{
return
Redirect::
back()->
withInput()->
withErrors($validator
->
messages());
}
}
}
Comme j'ai prévu un contrôleur RESTful la route est élémentaire :
Route::
controller('
livres
'
,
'
LivreController
'
);
Il ne manque plus que la vue hello :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<!
doctype html
>
<
html lang
=
"
fr
"
>
<
head>
<
meta charset
=
"
UTF-8
"
>
<
title>
Ou placer mon code<
/title
>
<
/head
>
<
body>
{{
link_to_action('LivreController@getCreate'
,
'Ajouter un livre'
) }}
@foreach
($livres
as
$livre
)
<
h1>
Titre du livre : {{
$livre
->
titre }}
<
/h1
>
<
p>
Auteur : {{
$livre
->
auteur }}
<
/p
>
<
p>
Editeur : {{
$livre
->
editeur }}
<
/p
>
@endforeach
{{
$livres
->
links();
}}
<
/body
>
<
/html
>
Et la vue saisie :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
<!
doctype html
>
<
html lang
=
"
fr
"
>
<
head>
<
meta charset
=
"
UTF-8
"
>
<
title>
Ou placer mon code<
/title
>
<
/head
>
<
body>
@if
($errors
->
count())
<
p>
Les erreurs suivantes sont survenues :<
/p
>
<
ul>
@foreach
($errors
->
all() as
$message
)
<
li>
{{
$message
}}
<
/li
>
@endforeach
<
/ul
>
@endif
{{
Form::
open(array
('url'
=>
'livres/create'
)) }}
{{
Form::
label('titre'
,
'Titre :'
)}}
{{
Form::
text('titre'
) }}
{{
Form::
label('auteur'
,
'Auteur :'
)}}
{{
Form::
text('auteur'
) }}
{{
Form::
label('auteur'
,
'Editeur :'
)}}
{{
Form::
text('editeur'
) }}
{{
Form::
submit('Envoyer'
) }}
{{
Form::
close() }}
<
/body
>
<
/html
>
Tout ça est rapide à mettre en place et fonctionne à merveille. À partir de cette situation, je vais envisager plusieurs cas d'organisation du codage pour mettre en lumière ce que j'ai cité au départ de ce fil.
XXXII-C. La validation dans des fonctions▲
Si j'ai plusieurs formulaires dans une application, je risque de multiplier les cas de validation. Au bout d'un moment je me dis que ça serait bien de rassembler toutes ces validations quelque part. Alors je crée un fichier app/validation.php pour le faire. Dans mon cas évidemment je n'ai qu'une validation, alors je construis mon code dans ce fichier :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
function validateLivre($inputs
)
{
$rules
=
array(
'
titre
'
=>
'
required|alpha|unique:livres
'
,
'
auteur
'
=>
'
required|alpha
'
,
'
editeur
'
=>
'
required|alpha
'
);
return validate($inputs
,
$rules
);
}
function validate($inputs
,
$rules
)
{
$validator
=
Validator::
make($inputs
,
$rules
);
if ($validator
->
passes()) {
return true;
}
else {
return $validator
->
messages();
}
}
J'ai prévu une fonction validateLivre qui a comme unique paramètre les entrées du formulaire et qui contient les règles. J'ai ensuite une fonction validate qui accomplit la validation et qui renvoie l'information. Pour que mes fonctions soient connues de l'application j'informe Composer :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
"
autoload
"
:
{
"
classmap
"
:
[
"
app/commands
"
,
"
app/controllers
"
,
"
app/models
"
,
"
app/database/migrations
"
,
"
app/database/seeds
"
,
"
app/tests/TestCase.php
"
],
"
files
"
:
[
"
app/validation.php
"
]
},
Après un dumpautoload je peux alléger la méthode postCreate de mon contrôleur LivreController :
Maintenant, tout ce qui concerne la validation est rassemblé dans un seul fichier, et j'ai pu factoriser quelques actions. Ce n'est pas forcément très joli mais on va améliorer tout ça bientôt…
XXXII-D. La validation dans des classes▲
Supposons maintenant que la solution vue plus haut ne me convienne pas. Je peux placer mon code dans des classes. Pour localiser ces classes je crée un dossier app/validation. J'explique à Composer qu'il doit charger mes classes :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
"autoload"
:
{
"classmap"
:
[
"app/commands"
,
"app/controllers"
,
"app/models"
,
"app/database/migrations"
,
"app/database/seeds"
,
"app/tests/TestCase.php"
,
"app/validation"
]
},
Ensuite je réfléchis un peu. Dans mon application je vais avoir sans doute plusieurs formulaires et je vais donc avoir du code en commun. Je vais donc créer une classe abstraite app/validation/ValidationBase avec ce code commun :
Je prévois donc dans cette classe la méthode pour la validation, qui prend comme paramètre les entrées du formulaire. J'ai aussi une propriété $rules qui sera différente selon le formulaire. Pour mon cas je crée donc une classe app/validation/ValidationLivre qui hérite de la précédente :
2.
3.
4.
5.
6.
7.
8.
9.
10.
class ValidationLivre extends
ValidationBase
{
protected
$rules
=
array
(
'titre'
=>
'required|alpha|unique:livres'
,
'auteur'
=>
'required|alpha'
,
'editeur'
=>
'required|alpha'
);
}
Je me retrouve pour cette classe avec seulement la propriété $rules renseignée.
Je dois ensuite modifier mon contrôleur pour tenir compte de ces changements :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
public function postCreate()
{
$val
=
new ValidationLivre();
$validate
=
$val
->
validate(Input::
all());
if($validate
===
true)
{
$livre
=
new Livre;
$livre
->
create(Input::
all());
return '
Livre enregistré
'
;
}
else {
return Redirect::
back()->
withInput(Input::
all())->
withErrors($validate
);
}
}
Dans la méthode postCreate je crée une instance de la classe ValidationLivre, puis j'utilise la méthode validate en transmettant les entrées. Tout ça fonctionne très bien. Mais ce n'est pas très propre. Il serait plus élégant d'utiliser le conteneur de Laravel pour pourvoir injecter la classe ValidationLivre dans le contrôleur plutôt que de créer une instance dans le code. Le conteneur de Laravel est suffisamment intelligent pour résoudre des liaisons même si on ne les a pas déclarées. Je vais donc injecter ma classe dans le contrôleur et voir si ça fonctionne :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
class LivreController extends
BaseController {
protected
$val
;
public
function
__construct
(ValidationLivre $val
)
{
$this
->
val =
$val
;
}
public
function
getIndex()
{
$livres
=
Livre::
paginate(5
);
return
View::
make('hello'
)->
with('livres'
,
$livres
);
}
public
function
getCreate()
{
return
View::
make('saisie'
);
}
public
function
postCreate()
{
$validate
=
$this
->
val->
validate(Input::
all());
if
($validate
===
true
)
{
$livre
=
new
Livre;
$livre
->
create(Input::
all());
return
'Livre enregistré'
;
}
else
{
return
Redirect::
back()->
withInput(Input::
all())->
withErrors($validate
);
}
}
}
J'ai ajouté un constructeur avec un paramètre de type ValidationLivre. Le conteneur de Laravel cherche la classe et la trouve ! En effet les contrôleurs sont toujours résolus dans le conteneur, ce qui permet d'injecter un type. Il me crée une instance et je peux l'utiliser . Mon code devient plus propre et plus facile à tester.
XXXII-E. Les espaces de noms▲
XXXII-E-1. psr-0 kesaco ?▲
Il n'est pas très prudent de créer des classes sans les placer dans des espaces de noms. D'autre part il est conseillé d'utiliser les préconisations du psr-0. Mais c'est quelque chose de finalement assez récent pour les développeurs PHP. C'est une préconisation qui a été établie par les principaux acteurs du PHP pour unifier la présentation des librairies. En gros ça permet de pouvoir assurer le chargement automatique des classes grâce à une convention de nommage et ainsi de constituer facilement une application avec des « briques » issues de différentes provenances.
Le plus important est de comprendre l'organisation des espaces de noms :
\<Nom du Vendor>\(<Espace de noms>\)*<Nom de la Classe>
Si vous regardez le code de Laravel vous allez voir que la norme a été parfaitement appliquée.
XXXII-E-2. Utilisation de psr-0 pour l'exemple▲
Je vais poursuivre mon exemple en utilisant cette possibilité. Je vais créer l'architecture app/lib/validation et je vais indiquer à Composer que je respecte le psr-0 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
"autoload"
:
{
"classmap"
:
[
"app/commands"
,
"app/controllers"
,
"app/models"
,
"app/database/migrations"
,
"app/database/seeds"
,
"app/tests/TestCase.php"
],
"psr-0"
:
{
"Lib\\"
:
"app"
}
},
Mon espace de noms est donc Lib\Validation. Comme je n'ai pas grand-chose à mettre dedans je n'aurai pas de sous-espace.
Pour faire les choses bien, je commence par créer une interface app/lib/validation/ValidationInterface :
2.
3.
4.
5.
6.
7.
<?php
namespace
Lib\Validation;
interface
ValidationInterface {
public
function
validate(array
$inputs
);
}
Le contrat est établi. Je veux une méthode de validation. Je crée ma classe abstraite app/lib/validation/ValidationBase comme précédemment mais évidemment modifiée :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
<?php
namespace
Lib\Validation;
use
Illuminate\Validation\Factory as
Validator;
abstract
class
ValidationBase implements
ValidationInterface
{
protected
$rules
;
protected
$validator
;
public
function
__construct
(Validator $validator
)
{
$this
->
validator =
$validator
;
}
public
function
validate(array
$inputs
)
{
$v
=
$this
->
validator->
make($inputs
,
$this
->
rules);
if
($v
->
passes()) {
return
true
;
}
else
{
return
$v
->
messages();
}
}
}
Cette fois j'injecte le validateur de Laravel dans le constructeur. Je crée ensuite ma classe de validation concrète app/lib/validation/ValidationLivre pour les livres :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
<?php
namespace
Lib\Validation;
class
ValidationLivre extends
ValidationBase
{
protected
$rules
=
array
(
'titre'
=>
'required|alpha|unique:livres'
,
'auteur'
=>
'required|alpha'
,
'editeur'
=>
'required|alpha'
);
}
Je me retrouve donc avec cette architecture :
Et évidemment dans le contrôleur je dois tenir compte de l'espace de noms :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
<?php
use
Lib\Validation\ValidationLivre;
class
LivreController extends
BaseController {
protected
$val
;
public
function
__construct
(ValidationLivre $val
)
{
$this
->
val =
$val
;
}
public
function
getIndex()
{
$livres
=
Livre::
paginate(5
);
return
View::
make('hello'
)->
with('livres'
,
$livres
);
}
public
function
getCreate()
{
return
View::
make('saisie'
);
}
public
function
postCreate()
{
$validate
=
$this
->
val->
validate(Input::
all());
if
($validate
===
true
)
{
$livre
=
new
Livre;
$livre
->
create(Input::
all());
return
'Livre enregistré'
;
}
else
{
return
Redirect::
back()->
withInput(Input::
all())->
withErrors($validate
);
}
}
}
Comme j'injecte une classe concrète, c'est résolu automatiquement. Bon, ça commence à avoir un peu plus d'allure comme ça .
XXXII-F. Délocaliser la gestion du modèle▲
On peut pousser un peu plus la réflexion à partir de notre exemple. Un contrôleur sert à quoi ? Essentiellement à récupérer les informations de l'utilisateur et à générer une réponse. Tout le traitement intermédiaire ne devrait pas le concerner. Nous avons déjà extrait la validation. Par contre on voit que l'action sur le modèle est encore inclus dans le contrôleur, ce qui ne devrait pas être sa tâche. Je ne devrais pas avoir à créer une instance du modèle Livre ici. Dans l'idéal je veux un contrôleur comme ça :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
<?php
use
Lib\Validation\ValidationLivre;
use
Lib\Gestion\GestionLivre;
class
LivreController extends
BaseController {
protected
$val
;
protected
$gestion
;
public
function
__construct
(ValidationLivre $val
,
GestionLivre $gestion
)
{
$this
->
val =
$val
;
$this
->
gestion =
$gestion
;
}
public
function
getIndex()
{
$livres
=
$this
->
gestion->
affiche(5
);
return
View::
make('hello'
)->
with('livres'
,
$livres
);
}
public
function
getCreate()
{
return
View::
make('saisie'
);
}
public
function
postCreate()
{
$validate
=
$this
->
val->
validate(Input::
all());
if
($validate
===
true
)
{
$this
->
gestion->
create(Input::
all());
return
'Livre enregistré'
;
}
else
{
return
Redirect::
back()->
withInput(Input::
all())->
withErrors($validate
);
}
}
}
Vous remarquez que j'ai ajouté l'espace de nom Lib\Gestion\GestionLivre. C'est ici que je vais assurer toute l'intendance du modèle. Je me contente ensuite d'une injection dans le contrôleur :
public function __construct(ValidationLivre $val
,
GestionLivre $gestion
)
Pour afficher les livres, je fais simplement appel à la méthode affiche :
$livres
=
$this
->
gestion->
affiche(5
);
Et ensuite pour créer le livre, j'appelle la méthode create et c'est bouclé :
$this
->
gestion->
create(Input::
all());
C'est propre, efficace, et beaucoup plus simple à tester ! Mais bon, pour que ça marche je dois créer quelques classes. D'abord j'enrichis mon espace de noms avec Lib\Gestion\GestionLivre. Comme je veux faire quelque chose de propre je commence par une interface app/lib/gestion/GestionInterface :
2.
3.
4.
5.
6.
7.
8.
<?php
namespace
Lib\Gestion;
interface
GestionInterface {
public
function
affiche($pages
);
public
function
create(array
$inputs
);
}
Ensuite je continue avec une classe abstraite app/lib/gestion/GestionBase qui sera commune à tous les modèles :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<?php
namespace
Lib\Gestion;
abstract
class
GestionBase implements
GestionInterface
{
protected
$model
;
public
function
affiche($pages
)
{
return
$this
->
model->
paginate($pages
);
}
public
function
create(array
$inputs
)
{
$this
->
model->
create($inputs
);
}
}
Et enfin j'établis ma classe spécifique aux livres app/lib/gestion/GestionLivre :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
<?php
namespace
Lib\Gestion;
class
GestionLivre extends
GestionBase
{
public
function
__construct
(\Livre $livre
)
{
$this
->
model =
$livre
;
}
}
Je me retrouve donc maintenant avec cette architecture :
Mon contrôleur est propre, et fait uniquement ce qu'il doit faire sans se soucier de l'intendance intermédiaire. Le code est bien organisé et facile à comprendre et à modifier. En plus il permet une extension facile. Si je dois créer un autre formulaire avec un autre modèle, j'ai déjà tout ce qu'il faut pour le faire .
XXXII-G. Un service de validation▲
Faisons un pas de plus dans l'organisation du code. Regardons le constructeur de notre contrôleur :
public function __construct(ValidationLivre $val
,
GestionLivre $gestion
)
On se rend compte qu'on injecte des classes concrètes. Elle sont résolues automatiquement mais posons-nous une question : puisque nous avons une interface il serait sans doute plus judicieux d'injecter celle-ci. Quel intérêt ? Une interface représente un contrat, c'est une façon de dire au contrôleur : tu as la possibilité d'effectuer une validation de telle façon. Dans notre cas très simple on lui dit juste qu'il existe une méthode validate. Il n'a pas à savoir comment la validation est réalisée. On pourrait imaginer une tout autre organisation du code : si on injecte l'interface, le contrôleur fonctionnera toujours. Voici le contrôleur modifié en conséquence :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
use Lib\Validation\ValidationInterface;
use Lib\Gestion\GestionLivre;
class LivreController extends
BaseController {
protected
$val
;
protected
$gestion
;
public
function
__construct
(ValidationInterface $val
,
GestionLivre $gestion
)
{
$this
->
val =
$val
;
$this
->
gestion =
$gestion
;
}
public
function
getIndex()
{
$livres
=
$this
->
gestion->
affiche(5
);
return
View::
make('hello'
)->
with('livres'
,
$livres
);
}
public
function
getCreate()
{
return
View::
make('saisie'
);
}
public
function
postCreate()
{
$validate
=
$this
->
val->
validate(Input::
all());
if
($validate
===
true
)
{
$this
->
gestion->
create(Input::
all());
return
'Livre enregistré'
;
}
else
{
return
Redirect::
back()->
withInput(Input::
all())->
withErrors($validate
);
}
}
}
On injecte l'interface ValidationInterface. Si on teste le code on va recevoir une erreur, parce que le conteneur ne sait pas quelle classe concrète correspond à cette interface. Il faut donc l'en informer. Pour cela il faut créer un ServiceProvider :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<?php
namespace
Lib\Validation;
use
Illuminate\Support\ServiceProvider;
class
ValidationServiceProvider extends
ServiceProvider {
public
function
register()
{
$this
->
app->
bind('Lib\Validation\ValidationInterface'
,
function
($app
)
{
return
new
ValidationLivre($app
->
make('Illuminate\Validation\Factory'
));
}
);
}
}
Ici on crée un lien entre l'interface ValidationInterface et la classe concrète ValidationLivre. Il faut aussi renseigner le fichier app/config/app.php :
'
Illuminate\Workbench\WorkbenchServiceProvider
'
,
'
Lib\Validation\ValidationServiceProvider
'
),
On fait un dumpautoload pour que le lien soit enregistré, et ça devrait fonctionner.