# Complete Module Boilerplate

This guide provides complete, copy-paste ready boilerplate code for creating a new module. Replace `YourEntity` with your actual entity name.

## Quick Setup Commands

```bash
# Create folder structure
mkdir -p app/Domains/YourDomain/YourModule/DTOs
mkdir -p app/Domains/YourDomain/YourModule/Enums
mkdir -p app/Domains/YourDomain/YourModule/Http/Controllers/V1
mkdir -p app/Domains/YourDomain/YourModule/Http/Requests/V1
mkdir -p app/Domains/YourDomain/YourModule/Http/Resources
mkdir -p app/Domains/YourDomain/YourModule/Models
mkdir -p app/Domains/YourDomain/YourModule/Routes
mkdir -p app/Domains/YourDomain/YourModule/Services

# Create migration
php artisan make:migration create_your_entities_table
```

---

## 1. Migration

**File:** `database/migrations/YYYY_MM_DD_HHMMSS_create_your_entities_table.php`

```php
<?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('your_entities', function (Blueprint $table) {
            $table->id();
            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
            $table->foreignId('category_id')->nullable()->constrained()->nullOnDelete();
            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();

            $table->string('name');
            $table->string('code')->nullable();
            $table->text('description')->nullable();
            $table->string('status')->default('draft');
            $table->decimal('amount', 15, 2)->default(0);
            $table->integer('quantity')->default(0);
            $table->date('date')->nullable();
            $table->boolean('is_active')->default(true);
            $table->json('metadata')->nullable();

            $table->timestamps();
            $table->softDeletes();

            $table->index(['company_id', 'status']);
            $table->unique(['company_id', 'code']);
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('your_entities');
    }
};
```

---

## 2. Enum

**File:** `app/Domains/YourDomain/YourModule/Enums/YourEntityStatus.php`

```php
<?php

namespace App\Domains\YourDomain\YourModule\Enums;

enum YourEntityStatus: string
{
    case Draft = 'draft';
    case Pending = 'pending';
    case Active = 'active';
    case Completed = 'completed';
    case Cancelled = 'cancelled';

    public function label(): string
    {
        return match($this) {
            self::Draft => __('Draft'),
            self::Pending => __('Pending'),
            self::Active => __('Active'),
            self::Completed => __('Completed'),
            self::Cancelled => __('Cancelled'),
        };
    }

    public static function values(): array
    {
        return array_column(self::cases(), 'value');
    }
}
```

---

## 3. Model

**File:** `app/Domains/YourDomain/YourModule/Models/YourEntity.php`

```php
<?php

namespace App\Domains\YourDomain\YourModule\Models;

use App\Domains\YourDomain\YourModule\Enums\YourEntityStatus;
use App\Models\BaseModel;
use App\Models\Company;
use App\Domains\Core\User\Models\User;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;

class YourEntity extends BaseModel
{
    use SoftDeletes;

    protected static bool $applyCompanyScope = true;

    protected $table = 'your_entities';

    protected $fillable = [
        'company_id',
        'category_id',
        'created_by',
        'name',
        'code',
        'description',
        'status',
        'amount',
        'quantity',
        'date',
        'is_active',
        'metadata',
    ];

    protected $casts = [
        'amount' => 'decimal:2',
        'quantity' => 'integer',
        'date' => 'date',
        'is_active' => 'boolean',
        'metadata' => 'array',
        'status' => YourEntityStatus::class,
    ];

    public static array $filtersCols = [
        'company_id',
        'status',
        'category_id',
        'is_active',
    ];

    public static array $searchCols = [
        'name',
        'code',
        'description',
    ];

    public static bool $searchDate = true;

    // ==================
    // RELATIONSHIPS
    // ==================

    public function company(): BelongsTo
    {
        return $this->belongsTo(Company::class);
    }

    public function category(): BelongsTo
    {
        return $this->belongsTo(\App\Models\Category::class);
    }

    public function createdBy(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    // ==================
    // SCOPES
    // ==================

    public function scopeActive($query)
    {
        return $query->where('is_active', true);
    }

    public function scopeByStatus($query, string $status)
    {
        return $query->where('status', $status);
    }

    // ==================
    // METHODS
    // ==================

    public function canBeDeleted(): bool
    {
        return !in_array($this->status, [
            YourEntityStatus::Completed,
            YourEntityStatus::Active,
        ]);
    }

    public function canBeEdited(): bool
    {
        return $this->status === YourEntityStatus::Draft;
    }
}
```

---

## 4. DTO

**File:** `app/Domains/YourDomain/YourModule/DTOs/YourEntityDTO.php`

```php
<?php

namespace App\Domains\YourDomain\YourModule\DTOs;

use App\Core\Interfaces\DTOInterface;

readonly final class YourEntityDTO implements DTOInterface
{
    public function __construct(
        public ?int $id = null,
        public ?string $name = null,
        public ?string $code = null,
        public ?string $description = null,
        public ?string $status = null,
        public ?float $amount = null,
        public ?int $quantity = null,
        public ?string $date = null,
        public ?bool $is_active = null,
        public ?int $category_id = null,
        public ?array $metadata = null,
    ) {}

    public static function fromRequest(array $array): self
    {
        return new self(
            id: $array['id'] ?? null,
            name: $array['name'] ?? null,
            code: $array['code'] ?? null,
            description: $array['description'] ?? null,
            status: $array['status'] ?? null,
            amount: isset($array['amount']) ? (float) $array['amount'] : null,
            quantity: isset($array['quantity']) ? (int) $array['quantity'] : null,
            date: $array['date'] ?? null,
            is_active: isset($array['is_active']) ? (bool) $array['is_active'] : null,
            category_id: isset($array['category_id']) ? (int) $array['category_id'] : null,
            metadata: $array['metadata'] ?? null,
        );
    }
}
```

---

## 5. Service

**File:** `app/Domains/YourDomain/YourModule/Services/YourEntityService.php`

```php
<?php

namespace App\Domains\YourDomain\YourModule\Services;

use App\Domains\YourDomain\YourModule\DTOs\YourEntityDTO;
use App\Domains\YourDomain\YourModule\Models\YourEntity;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class YourEntityService
{
    public function index(): LengthAwarePaginator
    {
        return YourEntity::query()
            ->filter()
            ->with(['category', 'createdBy'])
            ->latest('created_at')
            ->paginate(request('perPage', 15));
    }

    public function find(int $id): YourEntity
    {
        return YourEntity::query()
            ->with(['category', 'createdBy'])
            ->findOrFail($id);
    }

    public function store(YourEntityDTO $dto): YourEntity
    {
        return DB::transaction(function () use ($dto) {
            $entity = YourEntity::create([
                'company_id' => company()->id,
                'name' => $dto->name,
                'code' => $dto->code,
                'description' => $dto->description,
                'status' => $dto->status ?? 'draft',
                'amount' => $dto->amount ?? 0,
                'quantity' => $dto->quantity ?? 0,
                'date' => $dto->date,
                'is_active' => $dto->is_active ?? true,
                'category_id' => $dto->category_id,
                'metadata' => $dto->metadata,
                'created_by' => auth()->id(),
            ]);

            Log::info('YourEntity created', ['id' => $entity->id]);

            return $entity->fresh(['category', 'createdBy']);
        });
    }

    public function update(YourEntityDTO $dto, YourEntity $entity): YourEntity
    {
        return DB::transaction(function () use ($dto, $entity) {
            if (!$entity->canBeEdited()) {
                throw new \Exception(__('Entity cannot be edited in current status'));
            }

            $entity->update(array_filter([
                'name' => $dto->name,
                'code' => $dto->code,
                'description' => $dto->description,
                'status' => $dto->status,
                'amount' => $dto->amount,
                'quantity' => $dto->quantity,
                'date' => $dto->date,
                'is_active' => $dto->is_active,
                'category_id' => $dto->category_id,
                'metadata' => $dto->metadata,
            ], fn($v) => $v !== null));

            Log::info('YourEntity updated', ['id' => $entity->id]);

            return $entity->fresh(['category', 'createdBy']);
        });
    }

    public function destroy(YourEntity $entity): bool
    {
        return DB::transaction(function () use ($entity) {
            if (!$entity->canBeDeleted()) {
                throw new \Exception(__('Entity cannot be deleted in current status'));
            }

            $entity->delete();

            Log::info('YourEntity deleted', ['id' => $entity->id]);

            return true;
        });
    }

    public function approve(YourEntity $entity): YourEntity
    {
        return DB::transaction(function () use ($entity) {
            if ($entity->status->value !== 'pending') {
                throw new \Exception(__('Only pending entities can be approved'));
            }

            $entity->update([
                'status' => 'active',
            ]);

            return $entity->fresh();
        });
    }
}
```

---

## 6. Request Validation

**File:** `app/Domains/YourDomain/YourModule/Http/Requests/V1/StoreYourEntityRequest.php`

```php
<?php

namespace App\Domains\YourDomain\YourModule\Http\Requests\V1;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class StoreYourEntityRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name' => 'required|string|max:255',
            'code' => [
                'nullable',
                'string',
                'max:50',
                Rule::unique('your_entities', 'code')
                    ->where('company_id', company()->id),
            ],
            'description' => 'nullable|string|max:1000',
            'status' => 'nullable|string|in:draft,pending',
            'amount' => 'nullable|numeric|min:0',
            'quantity' => 'nullable|integer|min:0',
            'date' => 'nullable|date|date_format:Y-m-d',
            'is_active' => 'nullable|boolean',
            'category_id' => 'nullable|integer|exists:categories,id',
            'metadata' => 'nullable|array',
        ];
    }

    public function messages(): array
    {
        return [
            'name.required' => __('Name is required'),
            'code.unique' => __('This code already exists'),
        ];
    }
}
```

**File:** `app/Domains/YourDomain/YourModule/Http/Requests/V1/UpdateYourEntityRequest.php`

```php
<?php

namespace App\Domains\YourDomain\YourModule\Http\Requests\V1;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UpdateYourEntityRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        $entityId = $this->route('yourEntity')?->id;

        return [
            'name' => 'sometimes|required|string|max:255',
            'code' => [
                'sometimes',
                'nullable',
                'string',
                'max:50',
                Rule::unique('your_entities', 'code')
                    ->where('company_id', company()->id)
                    ->ignore($entityId),
            ],
            'description' => 'nullable|string|max:1000',
            'status' => 'nullable|string|in:draft,pending',
            'amount' => 'nullable|numeric|min:0',
            'quantity' => 'nullable|integer|min:0',
            'date' => 'nullable|date|date_format:Y-m-d',
            'is_active' => 'nullable|boolean',
            'category_id' => 'nullable|integer|exists:categories,id',
            'metadata' => 'nullable|array',
        ];
    }
}
```

---

## 7. Resource

**File:** `app/Domains/YourDomain/YourModule/Http/Resources/YourEntityResource.php`

```php
<?php

namespace App\Domains\YourDomain\YourModule\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class YourEntityResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'code' => $this->code,
            'description' => $this->description,

            'amount' => (float) $this->amount,
            'formatted_amount' => number_format($this->amount, 2),
            'quantity' => (int) $this->quantity,

            'status' => $this->status?->value,
            'status_label' => $this->status?->label(),

            'date' => $this->date?->format('Y-m-d'),
            'is_active' => (bool) $this->is_active,

            'category' => $this->whenLoaded('category', fn() => [
                'id' => $this->category->id,
                'name' => $this->category->name,
            ]),

            'created_by' => $this->whenLoaded('createdBy', fn() => [
                'id' => $this->createdBy->id,
                'name' => $this->createdBy->name,
            ]),

            'metadata' => $this->metadata,

            'created_at' => $this->created_at?->format('Y-m-d H:i:s'),
            'updated_at' => $this->updated_at?->format('Y-m-d H:i:s'),

            // Permissions
            'can_edit' => $this->canBeEdited(),
            'can_delete' => $this->canBeDeleted(),
        ];
    }
}
```

---

## 8. Controller

**File:** `app/Domains/YourDomain/YourModule/Http/Controllers/V1/YourEntityController.php`

```php
<?php

namespace App\Domains\YourDomain\YourModule\Http\Controllers\V1;

use App\Http\Controllers\Controller;
use App\Domains\YourDomain\YourModule\DTOs\YourEntityDTO;
use App\Domains\YourDomain\YourModule\Http\Requests\V1\StoreYourEntityRequest;
use App\Domains\YourDomain\YourModule\Http\Requests\V1\UpdateYourEntityRequest;
use App\Domains\YourDomain\YourModule\Http\Resources\YourEntityResource;
use App\Domains\YourDomain\YourModule\Models\YourEntity;
use App\Domains\YourDomain\YourModule\Services\YourEntityService;
use Illuminate\Http\JsonResponse;

class YourEntityController extends Controller
{
    public function __construct(
        private readonly YourEntityService $service
    ) {
        $this->applyPermissions(
            'your-entities',
            ['index', 'show', 'store', 'update', 'destroy'],
            ['approve' => 'approve']
        );
    }

    public function index(): JsonResponse
    {
        $entities = $this->service->index();

        return $this->sendPaginatedResponse(
            YourEntityResource::collection($entities)
        );
    }

    public function show(YourEntity $yourEntity): JsonResponse
    {
        $entity = $this->service->find($yourEntity->id);

        return $this->sendSuccessResponse(
            new YourEntityResource($entity),
            __('Entity retrieved successfully')
        );
    }

    public function store(StoreYourEntityRequest $request): JsonResponse
    {
        try {
            $dto = YourEntityDTO::fromRequest($request->validated());
            $entity = $this->service->store($dto);

            return $this->sendSuccessResponse(
                new YourEntityResource($entity),
                __('Entity created successfully'),
                201
            );
        } catch (\Exception $e) {
            return $this->sendFailedResponse($e->getMessage());
        }
    }

    public function update(UpdateYourEntityRequest $request, YourEntity $yourEntity): JsonResponse
    {
        try {
            $dto = YourEntityDTO::fromRequest($request->validated());
            $entity = $this->service->update($dto, $yourEntity);

            return $this->sendSuccessResponse(
                new YourEntityResource($entity),
                __('Entity updated successfully')
            );
        } catch (\Exception $e) {
            return $this->sendFailedResponse($e->getMessage(), 422);
        }
    }

    public function destroy(YourEntity $yourEntity): JsonResponse
    {
        try {
            $this->service->destroy($yourEntity);

            return $this->sendSuccessResponse(
                message: __('Entity deleted successfully')
            );
        } catch (\Exception $e) {
            return $this->sendFailedResponse($e->getMessage(), 422);
        }
    }

    public function approve(YourEntity $yourEntity): JsonResponse
    {
        try {
            $entity = $this->service->approve($yourEntity);

            return $this->sendSuccessResponse(
                new YourEntityResource($entity),
                __('Entity approved successfully')
            );
        } catch (\Exception $e) {
            return $this->sendFailedResponse($e->getMessage(), 422);
        }
    }
}
```

---

## 9. Routes

**File:** `app/Domains/YourDomain/YourModule/Routes/api.php`

```php
<?php

use App\Domains\YourDomain\YourModule\Http\Controllers\V1\YourEntityController;
use Illuminate\Support\Facades\Route;

Route::middleware(['auth:sanctum'])->prefix('your-module')->name('your-module.')->group(function () {

    Route::prefix('your-entities')->name('your-entities.')->group(function () {
        Route::get('/', [YourEntityController::class, 'index'])
            ->name('index')
            ->middleware('permission:your-entities.list');

        Route::post('/', [YourEntityController::class, 'store'])
            ->name('store')
            ->middleware('permission:your-entities.create');

        Route::get('/{yourEntity}', [YourEntityController::class, 'show'])
            ->name('show')
            ->middleware('permission:your-entities.show');

        Route::put('/{yourEntity}', [YourEntityController::class, 'update'])
            ->name('update')
            ->middleware('permission:your-entities.edit');

        Route::delete('/{yourEntity}', [YourEntityController::class, 'destroy'])
            ->name('destroy')
            ->middleware('permission:your-entities.delete');

        Route::post('/{yourEntity}/approve', [YourEntityController::class, 'approve'])
            ->name('approve')
            ->middleware('permission:your-entities.approve');
    });
});
```

---

## 10. Permission Seeder

**File:** `database/seeders/YourEntityPermissionsSeeder.php`

```php
<?php

namespace Database\Seeders;

use App\Domains\Core\Permission\Models\Permission;
use App\Domains\Core\Role\Models\Role;
use Illuminate\Database\Seeder;

class YourEntityPermissionsSeeder extends Seeder
{
    public function run(): void
    {
        $permissions = [
            [
                'name' => 'your-entities.list',
                'title' => ['en' => 'List Entities', 'ar' => 'عرض الكيانات'],
                'group_title' => 'your-entities',
                'category' => 'YourDomain',
            ],
            [
                'name' => 'your-entities.show',
                'title' => ['en' => 'View Entity', 'ar' => 'عرض الكيان'],
                'group_title' => 'your-entities',
                'category' => 'YourDomain',
            ],
            [
                'name' => 'your-entities.create',
                'title' => ['en' => 'Create Entity', 'ar' => 'إنشاء كيان'],
                'group_title' => 'your-entities',
                'category' => 'YourDomain',
            ],
            [
                'name' => 'your-entities.edit',
                'title' => ['en' => 'Edit Entity', 'ar' => 'تعديل الكيان'],
                'group_title' => 'your-entities',
                'category' => 'YourDomain',
            ],
            [
                'name' => 'your-entities.delete',
                'title' => ['en' => 'Delete Entity', 'ar' => 'حذف الكيان'],
                'group_title' => 'your-entities',
                'category' => 'YourDomain',
            ],
            [
                'name' => 'your-entities.approve',
                'title' => ['en' => 'Approve Entity', 'ar' => 'الموافقة على الكيان'],
                'group_title' => 'your-entities',
                'category' => 'YourDomain',
            ],
        ];

        foreach ($permissions as $permission) {
            Permission::firstOrCreate(
                ['name' => $permission['name'], 'guard_name' => 'web'],
                [
                    'guard_name' => 'web',
                    'group_title' => $permission['group_title'],
                    'category' => $permission['category'],
                    'title' => json_encode($permission['title']),
                ]
            );
        }

        // Assign to super_admin
        $superAdmin = Role::where('name', 'super_admin')->first();
        if ($superAdmin) {
            $permissionNames = array_column($permissions, 'name');
            $superAdmin->givePermissionTo($permissionNames);
        }

        // Clear permission cache
        app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
    }
}
```

---

## 11. Register Routes

Add to `routes/apis/yourdomain.php`:

```php
<?php

require base_path('app/Domains/YourDomain/YourModule/Routes/api.php');
```

---

## 12. Final Steps

```bash
# Run migration
php artisan migrate

# Run permission seeder
php artisan db:seed --class=YourEntityPermissionsSeeder

# Clear caches
php artisan cache:clear
php artisan route:clear

# Verify routes
php artisan route:list --name=your-entities
```

---

## Checklist

- [ ] Migration created and run
- [ ] Enum created (if needed)
- [ ] Model created with company scope
- [ ] DTO created with fromRequest()
- [ ] Service created with CRUD methods
- [ ] Store and Update requests created
- [ ] Resource created with proper formatting
- [ ] Controller created with all methods
- [ ] Routes defined with permissions
- [ ] Permission seeder created and run
- [ ] Routes registered in domain file
- [ ] Tested all endpoints
