phase 4
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
return ['name' => 'Authors'];
|
||||
@@ -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('authors', function(Blueprint $table): void { $table->id(); $table->foreignId('user_id')->nullable()->constrained()->nullOnDelete(); $table->string('name'); $table->string('slug')->unique(); $table->string('job_title')->nullable(); $table->text('biography')->nullable(); $table->string('avatar_path')->nullable(); $table->json('social_links')->nullable(); $table->string('meta_title')->nullable(); $table->text('meta_description')->nullable(); $table->boolean('is_active')->default(true); $table->timestamps(); }); } public function down(): void { Schema::dropIfExists('authors'); } };
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
namespace Modules\Authors\Http\Controllers;
|
||||
use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\View\View; use Modules\Authors\Models\Author;
|
||||
final class AuthorController extends Controller { public function index(): View { return view('authors::admin.index',['records'=>Author::with('user')->latest()->paginate(25)]); } public function create(): View { return view('authors::admin.form',['users'=>User::orderBy('name')->get()]); } public function store(Request $r): RedirectResponse { Author::create($this->data($r)); return redirect()->route('administration.authors.index')->with('status','Author created.'); } public function edit(Author $author): View { return view('authors::admin.form',['record'=>$author,'users'=>User::orderBy('name')->get()]); } public function update(Request $r, Author $author): RedirectResponse { $author->update($this->data($r)); return redirect()->route('administration.authors.index')->with('status','Author updated.'); } public function destroy(Author $author): RedirectResponse { $author->delete(); return back()->with('status','Author deleted.'); } private function data(Request $r): array { $d=$r->validate(['user_id'=>['nullable','exists:users,id'],'name'=>['required','string','max:255'],'slug'=>['required','string','max:255'],'job_title'=>['nullable','string','max:255'],'biography'=>['nullable','string'],'avatar_path'=>['nullable','string','max:255'],'meta_title'=>['nullable','string','max:255'],'meta_description'=>['nullable','string'],'is_active'=>['sometimes','boolean']]); $d['is_active']=$r->boolean('is_active'); return $d; } }
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
namespace Modules\Authors\Models; use App\Models\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
final class Author extends Model { protected $fillable=['user_id','name','slug','job_title','biography','avatar_path','social_links','meta_title','meta_description','is_active']; protected function casts(): array { return ['social_links'=>'array','is_active'=>'boolean']; } public function user(): BelongsTo { return $this->belongsTo(User::class); } }
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace Modules\Authors\Providers;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
final class AuthorsServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->register(RouteServiceProvider::class);
|
||||
$this->mergeConfigFrom(module_path('Authors', 'Config/config.php'), 'authors');
|
||||
}
|
||||
public function boot(): void
|
||||
{
|
||||
$this->loadMigrationsFrom(module_path('Authors', 'Database/Migrations'));
|
||||
$this->loadViewsFrom(module_path('Authors', 'Resources/views'), 'authors');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace Modules\Authors\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('Authors', 'Routes/web.php'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<x-layouts.app :title="isset($record) ? 'Edit author' : 'Create author'"><form method="POST" action="{{ isset($record) ? route('administration.authors.update',$record) : route('administration.authors.store') }}" class="max-w-3xl space-y-5">@csrf @isset($record) @method('PUT') @endisset <flux:select name="user_id" label="Linked user"><option value="">None</option>@foreach($users as $user)<option value="{{ $user->id }}" @selected(old('user_id',$record->user_id ?? null)==$user->id)>{{ $user->name }}</option>@endforeach</flux:select><flux:input name="name" label="Display name" value="{{ old('name',$record->name ?? '') }}" required/><flux:input name="slug" label="Slug" value="{{ old('slug',$record->slug ?? '') }}" required/><flux:input name="job_title" label="Job title" value="{{ old('job_title',$record->job_title ?? '') }}"/><flux:textarea name="biography" label="Biography">{{ old('biography',$record->biography ?? '') }}</flux:textarea><flux:input name="avatar_path" label="Avatar path" value="{{ old('avatar_path',$record->avatar_path ?? '') }}"/><flux:checkbox name="is_active" value="1" label="Active" :checked="old('is_active',$record->is_active ?? true)"/><flux:button type="submit" variant="primary">Save</flux:button></form></x-layouts.app>
|
||||
@@ -0,0 +1 @@
|
||||
<x-layouts.app :title="__('Authors')"><div class="flex items-center justify-between mb-6"><div><h1 class="text-2xl font-semibold">Authors</h1><p class="text-sm text-zinc-500">Manage publishing authors.</p></div><flux:button variant="primary" :href="route('administration.authors.create')" wire:navigate>Create</flux:button></div>@if(session('status'))<div class="mb-4 rounded border border-green-300 bg-green-50 p-3 text-green-800">{{ session('status') }}</div>@endif<div class="overflow-hidden rounded-xl border border-zinc-200 bg-white"><table class="w-full text-sm"><thead class="bg-zinc-50"><tr><th class="p-3 text-left">Name / title</th><th class="p-3 text-left">Status</th><th class="p-3 text-right">Actions</th></tr></thead><tbody>@forelse($records as $record)<tr class="border-t"><td class="p-3 font-medium">{{ $record->title ?? $record->name }}</td><td class="p-3">{{ $record->status ?? ($record->is_active ? 'Active' : 'Inactive') }}</td><td class="p-3 text-right"><flux:button size="sm" :href="route('administration.authors.edit', $record)" wire:navigate>Edit</flux:button><form class="inline" method="POST" action="{{ route('administration.authors.destroy', $record) }}">@csrf @method('DELETE')<flux:button size="sm" variant="danger" type="submit">Delete</flux:button></form></td></tr>@empty<tr><td colspan="3" class="p-6 text-center text-zinc-500">No records yet.</td></tr>@endforelse</tbody></table></div><div class="mt-4">{{ $records->links() }}</div></x-layouts.app>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
use Illuminate\Support\Facades\Route; use Modules\Authors\Http\Controllers\AuthorController;
|
||||
Route::middleware(['auth','verified'])->prefix('administration/authors')->name('administration.authors.')->group(function (): void { Route::resource('/', AuthorController::class)->parameters([''=>'author'])->except('show'); });
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace Modules\Authors\Tests\Feature;
|
||||
use Tests\TestCase;
|
||||
final class ModuleBootTest extends TestCase
|
||||
{
|
||||
public function test_module_configuration_is_available(): void
|
||||
{
|
||||
$this->assertSame('Authors', config('authors.name'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "Authors",
|
||||
"alias": "authors",
|
||||
"version": "1.0.0",
|
||||
"description": "Authors publishing module.",
|
||||
"keywords": ["publishing", "authors"],
|
||||
"priority": 30,
|
||||
"providers": ["Modules\\Authors\\Providers\\AuthorsServiceProvider"],
|
||||
"aliases": {},
|
||||
"files": [],
|
||||
"requires": ["Core", "Administration"]
|
||||
}
|
||||
Reference in New Issue
Block a user