Prenons l'exemple d'une liste de produit qui doit correspondre à la catégorie sélectionnée dans une liste déroulante.
Il serait impensable que le contrôleur passe à la vue la liste de toutes les catégories avec, pour chacune, la liste de tous les produits correspondants. Beaucoup trop de données seraient transférées inutilement.
Les performances seront plus intéressantes si on réussit à aller chercher seulement les produits correspondant à la catégorie sélectionnée. Cependant, la sélection a lieu du côté client, ce qui ne permet pas d'effectuer directement une nouvelle requête à la base de données.
La solution consiste à effectuer un appel AJAX lorsqu'une catégorie est sélectionnée afin d'aller chercher la liste des produits correspondants.
La première étape consiste à définir les routes.
Route::get('produits', [
'as' => 'produits.index',
'uses' => 'ProduitsController@index',
]);
// route pour l'appel AJAX
Route::post('produits/retrouver', [
'as' => 'produits.retrouverProduits',
'uses' => 'ProduitsController@retrouverProduits',
]);
Il faut ensuite créer la vue qui contiendra la liste déroulante et qui accueillera éventuellement la liste des produits.
Remarquez que la liste déroulante est ici mise en forme à l'aide de classes Bootstrap.
...
{{-- le formulaire utilise la route qui mène à la méthode d'action qui sera appelée par AJAX --}}
<form method="post" action="{{ route('produits.retrouverProduits') }}">
@csrf
<div class="form-group row selecthautpage">
<label for="categorie_id" class="col-form-label col-sm-2">Catégorie : </label>
<div class="col-sm-4">
<select class="form-control" id="categorie_id" name="categorie_id">
<option value="">Veuillez choisir...</option>
@foreach($categories as $categorie)
<option value="{{ $categorie->id }}">{{ $categorie->description }}</option>
@endforeach
</select>
</div>
</div>
</form>
<div id="donnees"></div>
...
Avec la technique utilisée ici, l'appel AJAX se chargera d'appeler une méthode d'action qui aura deux rôles :
Pour effectuer cette deuxième tâche, il est intéressant de créer une vue qui représente ce qui devra être affiché. Plutôt que d'afficher cette vue avec une instruction return View(), le contôleur ne fera que récupérer les balises HTML correspondant à la vue avec la méthode render(). L'appel AJAX prendra ce code HTML et l'ajoutera dans la division « donnees », sous la liste déroulante.
Puisqu'il s'agit d'une vue partielle, elle sera placée dans le dossier partials. On ajoutera un sous-dossier qui représente le modèle.
<div class="produits">
@if ($produits->count() > 0)
<table class="table">
<thead>
<tr>
<th>Code</th>
<th>Description</th>
</tr>
</thead>
<tbody>
@foreach($produits as $produit)
<tr>
<th>{{ $produit->code }}</th>
<th>{{ $produit->description }}</th>
</tr>
@endforeach
</tbody>
</table>
@else
<p>Il n'y a présentement aucun produit pour cette catégorie.</p>
@endif
</div>
Une première méthode d'action permettra de retrouver les informations de la liste déroulante et d'afficher la vue correspondante.
/**
* Affiche la liste déroulante des catégories. La listes des produits sera fournie par AJAX.
*
* @return \Illuminate\View\View
*/
public function index() : View
{
...
$categories = Categorie::orderBy('description')->get(); // retrouve les catégories de la liste déroulante
return View('produits.index', compact('categories'));
}
Tous les éléments sont maintenant en place pour définir la méthode d'action qui sera appelée par AJAX.
/**
* Génère les balises HTML des produits à afficher pour la catégorie sélectionnée.
* Méthode appelée via AJAX
*
* @return \Illuminate\Http\JsonResponse Chaîne JSON avec variables valide et contenuHTML
*/
public function retrouverProduits() : JsonResponse
{
...
$valide = true;
$contenuHTML = '';
// retrouve l'information passée par AJAX
$id = $_POST['categorie_id'];
// génère les balises HTML avec la liste des produits
try {
$produits = Produit::where('categorie_id', $id)->orderBy('code')->get(); // sera une collection vide si le select est sur "Veuillez choisir..."
$contenuHTML = View('partials.produits.liste', compact('produits'))->render();
...
}
catch(\Throwable $e) {
\Log::error("Erreur inattendue. ", [$e]);
$valide = false;
}
return response->json(compact('valide','contenuHTML'));
}
Finalement, on écrira l'appel AJAX.
$(function () {
$('#categorie_id').change(function (event) {
event.preventDefault(); // peu d'utilité ici mais ce serait nécessaire si l'appel AJAX était généré par un clic sur un bouton
// retrouver les données du formulaire
var $formulaire = $(this).parents("form:first"); // formulaire dans lequel la balise qui a généré l'appel AJAX était placée
var donneesFormulaire = $formulaire.serialize(); // donc le jeton anti-CSRF est inclus.
var actionFormulaire = $formulaire.attr('action'); // l'URL pour l'appel AJAX doit être absolu.
// appel AJAX
$.ajax({
type: "POST",
url: actionFormulaire,
dataType: "json",
data: donneesFormulaire
})
.done(function (response, textStatus, jqXHR) {
if (response.valide) {
...
// l'utilisation de render() a placé dans la variable contenuHTML tout le code HTML de la vue
$('#donnees').html(response.contenuHTML);
}
else {
...
}
})
.fail(function (jqXHR, textStatus, errorThrown) {
...
});
});
});
▼Publicité