Paillasse/portail/app/Http/Controllers/ClasseController.php

202 lines
7 KiB
PHP
Raw Normal View History

<?php
namespace App\Http\Controllers;
use App\Models\Classe;
use App\Models\DemandeInscription;
use App\Models\Eleve;
use App\Models\Seance;
use App\Services\GenerateurCompteEleve;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\View\View;
class ClasseController extends Controller
{
public function __construct(private readonly GenerateurCompteEleve $generateur) {}
public function index(): View
{
$classes = Auth::guard('web')->user()->classes()->withCount('eleves')->latest()->get();
return view('mes-classes.index', ['classes' => $classes]);
}
public function show(Classe $classe): View
{
$this->verifierProprietaire($classe);
return view('mes-classes.show', [
'classe' => $classe->load('eleves'),
'demandes' => $classe->demandesInscription()->where('statut', DemandeInscription::EN_ATTENTE)->oldest()->get(),
]);
}
public function storeEleve(Request $request, Classe $classe): RedirectResponse
{
$this->verifierProprietaire($classe);
$donnees = $request->validate([
'prenom' => ['required', 'string', 'max:100'],
'nom' => ['nullable', 'string', 'max:100'],
]);
$fiche = $this->creerEleve($classe, $donnees['prenom'], $donnees['nom'] ?? null);
return redirect()->route('mes-classes.show', $classe)
->with('fiches', [$fiche]);
}
public function importerCsv(Request $request, Classe $classe): RedirectResponse
{
$this->verifierProprietaire($classe);
$request->validate([
'fichier' => ['required', 'file', 'mimes:csv,txt'],
]);
$lignes = file($request->file('fichier')->getRealPath(), FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$fiches = [];
foreach ($lignes as $ligne) {
$colonnes = str_getcsv($ligne);
$prenom = trim($colonnes[0] ?? '');
if ($prenom === '' || Str::lower($prenom) === 'prenom') {
continue; // ligne vide ou en-tête
}
$nom = trim($colonnes[1] ?? '') ?: null;
$fiches[] = $this->creerEleve($classe, $prenom, $nom);
}
return redirect()->route('mes-classes.show', $classe)
->with('fiches', $fiches)
->with('nombre_importes', count($fiches));
}
public function regenererCode(Classe $classe, Eleve $eleve): RedirectResponse
{
$this->verifierProprietaire($classe);
abort_if($eleve->classe_id !== $classe->id, 404);
$codeSecret = $this->generateur->genererCodeSecret();
$jetonQr = Str::random(32);
$eleve->forceFill([
'code_secret' => Hash::make($codeSecret),
'jeton_qr' => $jetonQr,
])->save();
return redirect()->route('mes-classes.show', $classe)
->with('fiches', [[
'identifiant' => $eleve->identifiant,
'prenom' => $eleve->prenom,
'nom' => $eleve->nom,
'code_secret' => $codeSecret,
'jeton_qr' => $jetonQr,
]]);
}
/**
* @return array{identifiant: string, prenom: string, nom: ?string, code_secret: string, jeton_qr: string}
*/
private function creerEleve(Classe $classe, string $prenom, ?string $nom): array
{
$fiche = $this->generateur->creerDans($classe, $prenom, $nom);
unset($fiche['eleve']);
return $fiche;
}
public function accepterDemande(Classe $classe, DemandeInscription $demande): RedirectResponse
{
$this->verifierProprietaire($classe);
abort_if($demande->classe_id !== $classe->id, 404);
if ($demande->statut === DemandeInscription::EN_ATTENTE && ! $classe->estArchivee()) {
$fiche = $this->generateur->creerDans($classe, $demande->prenom, $demande->nom);
$demande->forceFill([
'statut' => DemandeInscription::ACCEPTEE,
'eleve_id' => $fiche['eleve']->id,
'code_secret_chiffre' => $fiche['code_secret'],
])->save();
}
return redirect()->route('mes-classes.show', $classe);
}
public function refuserDemande(Classe $classe, DemandeInscription $demande): RedirectResponse
{
$this->verifierProprietaire($classe);
abort_if($demande->classe_id !== $classe->id, 404);
$demande->forceFill(['statut' => DemandeInscription::REFUSEE])->save();
return redirect()->route('mes-classes.show', $classe);
}
public function nombreDemandes(Classe $classe): JsonResponse
{
$this->verifierProprietaire($classe);
return response()->json(['en_attente' => $classe->demandesInscription()->where('statut', DemandeInscription::EN_ATTENTE)->count()]);
}
/**
* Nouvelles fiches (nouveau code, nouveau QR) pour les élèves cochés :
* la fiche n'est visible qu'une fois, ceci permet de la refaire en lot.
*/
public function nouvellesFiches(Request $request, Classe $classe): RedirectResponse
{
$this->verifierProprietaire($classe);
$ids = $request->validate(['eleves' => ['required', 'array'], 'eleves.*' => ['integer']])['eleves'];
$fiches = [];
foreach ($classe->eleves()->whereIn('id', $ids)->orderBy('prenom')->get() as $eleve) {
$codeSecret = $this->generateur->genererCodeSecret();
$jetonQr = Str::random(32);
$eleve->forceFill(['code_secret' => Hash::make($codeSecret), 'jeton_qr' => $jetonQr])->save();
$fiches[] = ['identifiant' => $eleve->identifiant, 'prenom' => $eleve->prenom, 'nom' => $eleve->nom, 'code_secret' => $codeSecret, 'jeton_qr' => $jetonQr];
}
return redirect()->route('mes-classes.show', $classe)->with('fiches', $fiches);
}
/**
* Fin d'année : plus de séance possible, et suppression définitive de la
* classe et des données de ses élèves 90 jours plus tard.
*/
public function archiver(Classe $classe): RedirectResponse
{
$this->verifierProprietaire($classe);
if ($classe->aDesLabsEnCours()) {
return back()->withErrors(['archivage' => 'Des élèves ont encore un lab en cours : termine la séance avant d’archiver.']);
}
$classe->forceFill(['archivee_a' => now()])->save();
$classe->seances()->whereIn('statut', [Seance::PLANIFIEE, Seance::SALLE_ATTENTE, Seance::EN_COURS])
->update(['statut' => Seance::TERMINEE, 'terminee_a' => now()]);
return redirect()->route('mes-classes.show', $classe);
}
public function desarchiver(Classe $classe): RedirectResponse
{
$this->verifierProprietaire($classe);
$classe->forceFill(['archivee_a' => null])->save();
return redirect()->route('mes-classes.show', $classe);
}
private function verifierProprietaire(Classe $classe): void
{
abort_if($classe->user_id !== Auth::guard('web')->id(), 403);
}
}