Requête récursive

Prenons le cas d’une table familles avec des enregistrements liés parent/enfant sur plusieurs niveaux

id | nom          | id_parent
-----------------------------
1  | grand parent | null
2  | parent       | 1
3  | enfant 1     | 2
4  | enfant 2     | 2
etc...

Si on crée une relation simple dans le model, on obtient uniquement les enfants sur un seul niveau. On va créer une méthode qui s’appelle elle même

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Famille extends Model
{
 
	public function childrens() {
		return $this->hasMany(Famille::class, 'id_parent', 'id');
	}
 
	public function recursiveChildren() {
		return $this->childrens()->with('recursiveChildren');
		// si vous voulez trier par nom
		return $this->childrens()->orderBy('nom')->with('recursiveChildren');
	}
}

La requête eloquent pour sortir un résultat correcte est la suivante

Famille::with('recursiveChildren')->whereNull('id_parent')->get()

On utilise le with() pour forcer l’exécution de la relation pour chaque enregistrement et on filtre sur le id_parent sur null pour commencer uniquement sur le 1er niveau

On obtient ce résultat

=> Illuminate\Database\Eloquent\Collection {
     all: [
       App\Models\Famille {
         id: 1,
         nom: "grand parent",
         id_parent: null,
         recursiveChildren: Illuminate\Database\Eloquent\Collection {
           all: [
             App\Models\Famille {
               id: 2,
               nom: "parent",
               id_parent: 1,
               recursiveChildren: Illuminate\Database\Eloquent\Collection {
                 all: [
                   App\Models\Famille {
                     id: 3,
                     nom: "enfant 1",
                     id_parent: 2,
                     recursiveChildren: Illuminate\Database\Eloquent\Collection {
                       all: [],
                     },
                   },
                   App\Models\Famille {
                     id: 4,
                     nom: "enfant 2",
                     id_parent: 2,
                     recursiveChildren: Illuminate\Database\Eloquent\Collection {
                       all: [],
                     },
                   },
                 ],
               },
             },
           ],
         },
       },
     ],
   }

Maintenant on veut afficher le résultat. Il va falloir créer une sous-vue pour la récursivité.

Controller

public function index(){
	$familles = Famille::with('recursiveChildren')->whereNull('id_parent')->orderBy('nom')->get();
	return view('famille', compact('familles'));
}

Vue: famille.blade.php

<select>
	<option></option>
@php
	$tab = '';
@endphp
@foreach ( $familles as $e )
	@include('famille-item', ['famille'=>$e, 'tab'=>$tab])
@endforeach
</select>

Vue: famille-item.blade.php

	<option>{!! $tab !!}{{ $famille->nom }}</option>
	@if(isset($famille->recursiveChildren) and count($famille->recursiveChildren))
		@php
			// adaptez comme vous le souhaitez
			$tab .= '&sdot;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
		@endphp
		@foreach ($famille->recursiveChildren as $element)
			@include('visu.groupe-item', ['famille'=>$element, 'tab'=>$tab])
		@endforeach
	@endif

Pour un affichage en liste avec ul et li

Vue: famille.blade.php

<ul>
@foreach ( $familles as $e )
	@include('famille-item', ['famille'=>$e])
@endforeach
</ul>

Vue: famille-item.blade.php

	<li>{!! $tab !!}{{ $famille->nom }}</li>
	@if(isset($famille->recursiveChildren) and count($famille->recursiveChildren))
	<ul>
		@foreach ($famille->recursiveChildren as $element)
			@include('visu.groupe-item', ['famille'=>$element])
		@endforeach
	</ul>
	@endif