4 Commits

Author SHA1 Message Date
Clément
cfb482ddf5 comment form 2025-05-27 14:41:22 +02:00
fe87272b43 errors handled in the correct log file 2025-05-27 14:41:22 +02:00
Clément
f4aa84a82d handled errors properly 2025-05-12 16:42:41 +02:00
Clément
338e803c5f Categories (NN) 2025-04-17 15:04:13 +02:00
34 changed files with 533 additions and 11 deletions

View File

@@ -10,3 +10,31 @@
- `storage` => logs, cache
- `tests` => tests de l'app (automatisés)
- `vendor` => le dossier de composer (on n'y touche pas)
# Commandes PHP Artisan (Docker)
- Créer une migration => `php artisan make:migration create_nom_table`
- migration => `php artisan migrate:fresh`
- seeder => `php artisan db:seed --class=NomSeeder`
php artisan make:model Categorie --migration -a
php artisan make:model billet_categorie -m
```sql
use laravel_db;
INSERT INTO billet_categorie (id, billet_id, categorie_id, created_at, updated_at)
VALUES
(1, 1, 1, NULL, NULL),
(2, 1, 2, NULL, NULL),
(3, 1, 10, NULL, NULL),
(4, 2, 3, NULL, NULL),
(5, 3, 9, NULL, NULL),
(6, 4, 1, NULL, NULL),
(7, 4, 4, NULL, NULL),
(8, 5, 5, NULL, NULL),
(9, 6, 7, NULL, NULL),
(10, 7, 1, NULL, NULL),
(11, 8, 10, NULL, NULL),
(12, 9, 6, NULL, NULL),
(13, 10, 8, NULL, NULL);
```

View File

@@ -6,6 +6,9 @@ use App\Http\Requests\StoreBilletRequest;
use App\Http\Requests\UpdateBilletRequest;
use App\Models\Billet;
use Illuminate\Support\Facades\Log;
use Illuminate\Database\QueryException;
class BilletController extends Controller
{
/**
@@ -13,7 +16,12 @@ class BilletController extends Controller
*/
public function index()
{
try {
$billets = Billet::all();
} catch (QueryException $e) {
Log::channel('projectError')->error('Erreur d\'accès à la base de données');
return view('errors.dberror');
}
return view('index', compact('billets'));
}
@@ -39,7 +47,11 @@ class BilletController extends Controller
public function show(Billet $billet)
{
//
try {
$commentaires = $billet->commentaires;
} catch (QueryException $e) {
return view('errors.dberror');
}
return view('vBillet', compact('billet', 'commentaires'));
}

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreCategorieRequest;
use App\Http\Requests\UpdateCategorieRequest;
use App\Models\Categorie;
class CategorieController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreCategorieRequest $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(Categorie $categorie)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Categorie $categorie)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateCategorieRequest $request, Categorie $categorie)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Categorie $categorie)
{
//
}
}

View File

@@ -5,6 +5,8 @@ namespace App\Http\Controllers;
use App\Http\Requests\StoreCommentaireRequest;
use App\Http\Requests\UpdateCommentaireRequest;
use App\Models\Commentaire;
use App\Models\Billet;
use Illuminate\Support\Facades\Log;
class CommentaireController extends Controller
{
@@ -19,9 +21,20 @@ class CommentaireController extends Controller
/**
* Show the form for creating a new resource.
*/
public function create()
public function create($idBillet)
{
//
try {
$billet = Billet::findOrFail($idBillet);
}
catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
Log::channel('projectError')->error('Commentaire : Billet non trouvé');
return view('errors.unavailable');
}
catch (\Illuminate\Database\QueryException $e) {
Log::channel('projectError')->error('Erreur accès base de données');
return view('errors.dberror');
}
return view('vCommenter', compact('idBillet', 'billet'));
}
/**
@@ -29,7 +42,15 @@ class CommentaireController extends Controller
*/
public function store(StoreCommentaireRequest $request)
{
//
try {
Commentaire::create($request->all());
}
catch (\Illuminate\Database\QueryException $e) {
Log::channel('projectError')->error('Insertion en base de données impossible');
return view('errors.dberror');
}
Log::channel('projectInfo')->info('Commentaire ajouté par : '.$request->ip());
return view('vConfirmStore');
}
/**

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreCategorieRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
];
}
}

View File

@@ -11,7 +11,7 @@ class StoreCommentaireRequest extends FormRequest
*/
public function authorize(): bool
{
return false;
return true;
}
/**
@@ -22,7 +22,24 @@ class StoreCommentaireRequest extends FormRequest
public function rules(): array
{
return [
//
'COM_AUTEUR' => ['required', 'alpha', 'max:100'],
'COM_CONTENU' => ['required', 'string', 'max:200'],
];
}
/**
* Get the error messages for the defined validation rules.
*
* @return array
*/
public function messages() {
return [
'COM_AUTEUR.required' => 'Le nom de l\'auteur est requis.',
'COM_AUTEUR.alpha' => 'Le nom de l\'auteur ne doit contenir que des lettres.',
'COM_AUTEUR.max' => 'Le nom de l\'auteur ne doit pas dépasser 100 caractères.',
'COM_CONTENU.required' => 'Le contenu du commentaire est requis.',
'COM_CONTENU.string' => 'Le contenu du commentaire doit être une chaîne de caractères.',
'COM_CONTENU.max' => 'Le contenu du commentaire ne doit pas dépasser 200 caractères.',
];
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateCategorieRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
];
}
}

View File

@@ -22,4 +22,9 @@ class Billet extends Model
{
return $this->hasMany(Commentaire::class);
}
public function categories()
{
return $this->belongsToMany(Categorie::class);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Categorie extends Model
{
/** @use HasFactory<\Database\Factories\CategorieFactory> */
use HasFactory;
public function billets()
{
return $this->belongsToMany(Billet::class);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class billet_categorie extends Model
{
/** @use HasFactory<\Database\Factories\BilletCategorieFactory> */
use HasFactory;
}

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\Categorie;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class CategoriePolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return false;
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Categorie $categorie): bool
{
return false;
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return false;
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Categorie $categorie): bool
{
return false;
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Categorie $categorie): bool
{
return false;
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, Categorie $categorie): bool
{
return false;
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Categorie $categorie): bool
{
return false;
}
}

View File

@@ -127,6 +127,18 @@ return [
'path' => storage_path('logs/laravel.log'),
],
"projectError" => [
'driver' => 'single',
'path' => storage_path('logs/project.log'),
'level' => 'error',
],
"projectInfo" => [
'driver' => 'single',
'path' => storage_path('logs/project.log'),
'level' => 'info',
],
],
];

View File

@@ -0,0 +1,23 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\billet_categorie>
*/
class BilletCategorieFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Categorie>
*/
class CategorieFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
'CAT_NOM' => fake()->text(10),
];
}
}

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->text('CAT_NOM');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('categories');
}
};

View File

@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('billet_categorie', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('billet_id');
$table->foreign('billet_id')
->references('id')
->on('billets')
->onDelete('cascade')
->onUpdate('cascade');
$table->unsignedBigInteger('categorie_id');
$table->foreign('categorie_id')
->references('id')
->on('categories')
->onDelete('cascade')
->onUpdate('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('billet_categories');
}
};

View File

@@ -0,0 +1,19 @@
<?php
namespace Database\Seeders;
use App\Models\Categorie;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class CategorieSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
Categorie::factory(10)->create();
}
}

View File

@@ -0,0 +1,4 @@
@extends('layout')
@section('contenu')
<h2>La page demandée n'est pas disponible</h2>
@endsection

View File

@@ -0,0 +1,5 @@
@extends('layout')
@section('contenu')
<h2>Une erreur est survenue. Veuillez réessayer ultérieurement</h2>
@endsection

View File

@@ -0,0 +1,4 @@
@extends('layout')
@section('contenu')
<h2>La ressource demandée n'est pas disponible</h2>
@endsection

View File

@@ -6,8 +6,23 @@
<a href="{{ route('billets.show', $billet->id) }}"><h1 class="titreBillet">{{ $billet->BIL_TITRE }}</h1></a>
<time>{{ $billet->BIL_DATE }}</time>
</header>
<p>
Dans :
@php
$counter = 0;
@endphp
@foreach($billet->categories as $categorie)
{{ $categorie->CAT_NOM }}
@php
$counter++;
@endphp
@if(count($billet->categories) > $counter)
/
@endif
@endforeach
</p>
<p>{{ $billet->BIL_CONTENU }}</p>
<a href="{{ route('commenter', ['id'=>$billet->id]) }}"><h3 class="titre-billet">Ecrire un commentaire</h3></a>
</article>
<hr />
@endforeach
@endsection

View File

@@ -0,0 +1,40 @@
@extends('layout')
@section('contenu')
@php
$today = date('Y-m-d');
@endphp
<h2>Commenter le billet : {{ $billet->BIL_TITRE }}</h2>
<form action="{{ route('commentaires.store') }}" method="POST">
@csrf
<p><i>Complétez le formulaire. Les champs marqué par </i><em>*</em> sont <em>obligatoires</em></p>
<fieldset>
<legend>Entrez votre commentaire :</legend>
<div>
<label for="COM_AUTEUR">Nom : <em>*</em></label>
<input type="text" @error('COM_AUTEUR') is-invalid @enderror name="COM_AUTEUR" placeholder="Entrez votre nom"></input>
@error('COM_AUTEUR')
<div><em>{{ $message }}</em></div>
@enderror
</div>
<br>
<div>
<label for="COM_CONTENU">Message : <em>*</em></label>
<textarea name="COM_CONTENU" @error('COM_CONTENU') is-invalid @enderror name="COM_CONTENU" placeholder="Votre commentaire :"></textarea>
@error('COM_CONTENU')
<div><em>{{ $message }}</em></div>
@enderror
</div>
<div>
<input type="hidden" name="COM_DATE" value="{{ $today }}"></input>
</div>
<div>
<input type="hidden" name="created_at" value="{{ $today }}"></input>
</div>
<div>
<input type="hidden" name="billet_id" value="{{ $idBillet }}"></input>
</div>
<br>
<button type="submit">Envoyer !</button>
</fieldset>
</form>
@endsection

View File

@@ -0,0 +1,5 @@
@extends('layout')
@section('contenu')
<h2>Merci ! Nous avons bien enregistré votre commentaire.</h2>
@endsection

View File

@@ -2,9 +2,13 @@
use App\Http\Controllers\BilletController;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\CommentaireController;
Route::get('/', function () {
return view('welcome');
});
Route::get('/', fn() => view('welcome'));
Route::resource('billets', BilletController::class);
Route::resource('commentaires', CommentaireController::class);
// Route to create a comment
Route::get('commenter/{id}', [CommentaireController::class, 'create'])->name('commenter');

0
laravel/storage/app/.gitignore vendored Normal file → Executable file
View File

0
laravel/storage/app/private/.gitignore vendored Normal file → Executable file
View File

0
laravel/storage/app/public/.gitignore vendored Normal file → Executable file
View File

0
laravel/storage/framework/.gitignore vendored Normal file → Executable file
View File

0
laravel/storage/framework/cache/.gitignore vendored Normal file → Executable file
View File

0
laravel/storage/framework/cache/data/.gitignore vendored Normal file → Executable file
View File

0
laravel/storage/framework/sessions/.gitignore vendored Normal file → Executable file
View File

0
laravel/storage/framework/testing/.gitignore vendored Normal file → Executable file
View File

0
laravel/storage/framework/views/.gitignore vendored Normal file → Executable file
View File

0
laravel/storage/logs/.gitignore vendored Normal file → Executable file
View File