This commit is contained in:
Mark Gregory
2026-07-24 11:53:07 +01:00
parent 979ae2421e
commit 8cef5db6b3
18 changed files with 147 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
<?php
return ['name' => 'Products'];
@@ -0,0 +1,10 @@
<?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('products', function (Blueprint $table): void { $table->id(); $table->string('name'); $table->boolean('is_active')->default(true); $table->timestamps(); }); }
public function down(): void { Schema::dropIfExists('products'); }
};
@@ -0,0 +1,22 @@
<?php
namespace Modules\Products\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
use Modules\Products\Http\Requests\StoreProductRequest;
use Modules\Products\Models\Product;
final class ProductController
{
public function index(): View
{
return view('products::admin.products.index', ['records' => Product::query()->latest()->paginate()]);
}
public function store(StoreProductRequest $request): RedirectResponse
{
Product::query()->create($request->validated());
return back()->with('status', 'Product created.');
}
}
@@ -0,0 +1,11 @@
<?php
namespace Modules\Products\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
final class StoreProductRequest extends FormRequest
{
public function authorize(): bool { return $this->user() !== null; }
public function rules(): array { return ['name' => ['required', 'string', 'max:255'], 'is_active' => ['sometimes', 'boolean']]; }
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Modules\Products\Models;
use Illuminate\Database\Eloquent\Model;
final class Product extends Model
{
protected $guarded = [];
protected function casts(): array
{
return ['is_active' => 'boolean'];
}
}
@@ -0,0 +1,20 @@
<?php
namespace Modules\Products\Providers;
use Illuminate\Support\ServiceProvider;
final class ProductsServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->register(RouteServiceProvider::class);
$this->mergeConfigFrom(module_path('Products', 'Config/config.php'), 'products');
}
public function boot(): void
{
$this->loadMigrationsFrom(module_path('Products', 'Database/Migrations'));
$this->loadViewsFrom(module_path('Products', 'Resources/views'), 'products');
}
}
@@ -0,0 +1,14 @@
<?php
namespace Modules\Products\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
final class RouteServiceProvider extends ServiceProvider
{
public function boot(): void
{
Route::middleware('web')->group(module_path('Products', 'Routes/web.php'));
}
}
@@ -0,0 +1 @@
<form method="POST">@csrf<flux:input name="name" :label="__('Name')" required /><flux:button type="submit" variant="primary">{{ __('Save Product') }}</flux:button></form>
@@ -0,0 +1,3 @@
<x-layouts.app :title="__('Product')">
<div class="space-y-6"><h1 class="text-2xl font-semibold">Product</h1>@foreach ($records as $record)<div>{{ $record->name }}</div>@endforeach{{ $records->links() }}</div>
</x-layouts.app>
+7
View File
@@ -0,0 +1,7 @@
<?php
use Illuminate\Support\Facades\Route;
Route::middleware(['web', 'auth'])->prefix('administration/products')->name('administration.products.')->group(function (): void {
// Register module routes here.
});
@@ -0,0 +1,5 @@
<?php
use Modules\Products\Models\Product;
it('can create a Product', function (): void { expect(Product::query()->create(['name' => 'Example']))->toBeInstanceOf(Product::class); });
@@ -0,0 +1,5 @@
<?php
it('boots the Products module', function (): void {
expect(app()->providerIsLoaded(Modules\Products\Providers\ProductsServiceProvider::class))->toBeTrue();
});
+19
View File
@@ -0,0 +1,19 @@
{
"name": "Products",
"alias": "products",
"version": "1.0.0",
"description": "Products module.",
"keywords": [
"products"
],
"priority": 100,
"providers": [
"Modules\\Products\\Providers\\ProductsServiceProvider"
],
"aliases": {},
"files": [],
"requires": [
"Core",
"Administration"
]
}
+3
View File
@@ -0,0 +1,3 @@
# Corporate Theme
Generated by `php artisan theme:make corporate`.
@@ -0,0 +1 @@
:root { --theme-radius: 0.5rem; }
+6
View File
@@ -0,0 +1,6 @@
{
"name": "Corporate",
"slug": "corporate",
"version": "1.0.0",
"parent": "common"
}
@@ -0,0 +1 @@
<x-layouts.app><main><h1>{{ config('app.name') }}</h1></main></x-layouts.app>
@@ -0,0 +1 @@
<!DOCTYPE html><html lang="{{ str_replace('_', '-', app()->getLocale()) }}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>{{ $title ?? config('app.name') }}</title>@vite(['resources/css/app.css', 'resources/js/app.js'])</head><body>{{ $slot }}</body></html>