phase 4
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
return ['name' => 'Comments'];
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema;
|
||||
return new class extends Migration { public function up(): void { Schema::create('comments', function(Blueprint $table): void { $table->id(); $table->nullableMorphs('commentable'); $table->foreignId('parent_id')->nullable()->constrained('comments')->cascadeOnDelete(); $table->foreignId('user_id')->nullable()->constrained()->nullOnDelete(); $table->string('author_name'); $table->string('author_email'); $table->text('body'); $table->string('status')->default('pending')->index(); $table->string('ip_address',45)->nullable(); $table->text('moderation_notes')->nullable(); $table->timestamp('approved_at')->nullable(); $table->timestamps(); }); } public function down(): void { Schema::dropIfExists('comments'); } };
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
namespace Modules\Comments\Http\Controllers; use App\Http\Controllers\Controller; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; use Modules\Comments\Models\Comment;
|
||||
final class CommentController extends Controller { public function index(Request $r): View { $q=Comment::latest(); if($r->filled('status')) $q->where('status',$r->string('status')); return view('comments::admin.index',['records'=>$q->paginate(30)]); } public function update(Request $r, Comment $comment): RedirectResponse { $data=$r->validate(['status'=>['required','in:pending,approved,spam,rejected'],'moderation_notes'=>['nullable','string']]); $data['approved_at']=$data['status']==='approved' ? now() : null; $comment->update($data); return back()->with('status','Comment moderated.'); } public function destroy(Comment $comment): RedirectResponse { $comment->delete(); return back()->with('status','Comment deleted.'); } }
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
namespace Modules\Comments\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
final class Comment extends Model { protected $fillable=['commentable_type','commentable_id','parent_id','user_id','author_name','author_email','body','status','ip_address','moderation_notes','approved_at']; protected function casts(): array { return ['approved_at'=>'datetime']; } public function commentable(): MorphTo { return $this->morphTo(); } }
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace Modules\Comments\Providers;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
final class CommentsServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->register(RouteServiceProvider::class);
|
||||
$this->mergeConfigFrom(module_path('Comments', 'Config/config.php'), 'comments');
|
||||
}
|
||||
public function boot(): void
|
||||
{
|
||||
$this->loadMigrationsFrom(module_path('Comments', 'Database/Migrations'));
|
||||
$this->loadViewsFrom(module_path('Comments', 'Resources/views'), 'comments');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace Modules\Comments\Providers;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
final class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
Route::middleware('web')->group(module_path('Comments', 'Routes/web.php'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<x-layouts.app :title="__('Comments')"><h1 class="mb-6 text-2xl font-semibold">Comment moderation</h1>@if(session('status'))<div class="mb-4 rounded border border-green-300 bg-green-50 p-3">{{ session('status') }}</div>@endif<div class="space-y-4">@forelse($records as $comment)<div class="rounded-xl border bg-white p-5"><div class="flex justify-between"><strong>{{ $comment->author_name }}</strong><span>{{ $comment->status }}</span></div><p class="my-3">{{ $comment->body }}</p><form method="POST" action="{{ route('administration.comments.update',$comment) }}" class="flex gap-3">@csrf @method('PUT')<select name="status" class="rounded border"><option>pending</option><option @selected($comment->status==='approved')>approved</option><option @selected($comment->status==='spam')>spam</option><option @selected($comment->status==='rejected')>rejected</option></select><input name="moderation_notes" value="{{ $comment->moderation_notes }}" class="flex-1 rounded border" placeholder="Moderation notes"><flux:button type="submit">Update</flux:button></form></div>@empty<p>No comments awaiting moderation.</p>@endforelse</div><div class="mt-4">{{ $records->links() }}</div></x-layouts.app>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
use Illuminate\Support\Facades\Route; use Modules\Comments\Http\Controllers\CommentController;
|
||||
Route::middleware(['auth','verified'])->prefix('administration/comments')->name('administration.comments.')->group(function (): void { Route::get('/',[CommentController::class,'index'])->name('index'); Route::put('/{comment}',[CommentController::class,'update'])->name('update'); Route::delete('/{comment}',[CommentController::class,'destroy'])->name('destroy'); });
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace Modules\Comments\Tests\Feature;
|
||||
use Tests\TestCase;
|
||||
final class ModuleBootTest extends TestCase
|
||||
{
|
||||
public function test_module_configuration_is_available(): void
|
||||
{
|
||||
$this->assertSame('Comments', config('comments.name'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "Comments",
|
||||
"alias": "comments",
|
||||
"version": "1.0.0",
|
||||
"description": "Comments publishing module.",
|
||||
"keywords": ["publishing", "comments"],
|
||||
"priority": 30,
|
||||
"providers": ["Modules\\Comments\\Providers\\CommentsServiceProvider"],
|
||||
"aliases": {},
|
||||
"files": [],
|
||||
"requires": ["Core", "Administration"]
|
||||
}
|
||||
Reference in New Issue
Block a user