bishnu / laravel-repository-generator
A Laravel package to generate repositories, interfaces, services, and handling Module-based structure with ease.
Installs: 9
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/bishnu/laravel-repository-generator
Requires
- php: ^7.3|^8.0|^8.1|^8.2
- illuminate/support: ^8.0|^9.0|^10.0|^11.0|^12.0
Suggests
- nwidart/laravel-modules: Required for module-based architecture support (^8.0|^9.0|^10.0|^11.0|^12.0)
README
The Ultimate Tool for Laravel APIs
A powerful, production-ready Laravel package that automatically generates Repositories, Services, Controllers, Requests (with auto-validation rules!), Resources, and Routes.
It strictly follows SOLID Principles and Clean Architecture, and includes built-in support for:
- 🔍 Advanced Filtering & Searching (DataTables ready)
- 🔒 Role-Based Access Control (Row-level security)
- 📄 Automatic Pagination & Limiting
- 📂 Spatie Media Library Integration
- 🏗️ Laravel Modules (nwidart) support
- 🔗 Auto-Relationship Loading
🚀 Features at a Glance
| Feature | Description |
|---|---|
| Instant Scaffolding | Create Repositories, Interfaces, Controllers, Requests, and Resources in 1 command. |
| Smart Validation | Automatically reads your Database Schema to generate validation rules (required, nullable, exists, etc.). |
| Auto-Routing | Automatically injects API Resource routes into api.php. |
| Advanced Filtering | Filter by any column, relation, date range, or generic search with ?filters[...]. |
| Role Scoping | Admins see everything; Users see only their own data (automatically enforced). |
| Media Handling | Upload files, handle replacements, and specialized collections seamlessly. |
| Flexible IDs | Choose between UUIDs or standard IDs; the package auto-detects schema for lookups & relations. |
📦 Installation
1. Require the Package
composer require bishnu/laravel-repository-generator
2. Install & Publish Config
php artisan repo:install
3. Enable API Routes (Laravel 11+)
If you are using Laravel 11 or higher and haven't enabled API routes yet, ensure they are registered in bootstrap/app.php:
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php', // Add this line
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
🛠 Usage & Scenarios
1️⃣ Generating a Repository
The command checks your database table columns to generate validation rules automatically.
# Standard Laravel App
php artisan make:repo Product
# For a Specific Module (e.g., Ecommerce)
php artisan make:repo Product --module=Ecommerce
What gets created?
ProductRequest: With rules likename => required,category_id => exists:categories,id.ProductResource: Maps your model to JSON (CamelCase by default).ProductRepository: Handles DB logic (pagination, filters, auth).ProductController: Standard CRUD methods.api.php: InjectsRoute::apiResource('products', ...)
2️⃣ Advanced Filtering (Deep Dive)
To enable advanced filtering, ensure your model extends BaseModel. It already includes the HasFilters trait and other essentials.
// App/Models/Product.php
use Bishnu\RepositoryGenerator\Base\BaseModel;
class Product extends BaseModel
{
/**
* Exact match filters
* Usage: ?filters[status]=active&filters[category_id]=1
*/
protected $filterable = ['status', 'category_id', 'is_featured'];
/**
* Searchable columns (LIKE %query%)
* Usage: ?filters[search]=macbook
*/
protected $searchable = ['name', 'description', 'sku', 'vendor.name'];
/**
* Relationship filters (Dot notation)
* Usage: ?filters[category.slug]=electronics
*/
protected $filterableRelations = ['category', 'vendor', 'tags'];
}
💡 Filter Query Examples
| Scenario | API Request URL |
|---|---|
| Multiple Values | GET /products?filters[status]=active,pending |
| Range Filter | GET /products?filters[price][operator]=>&filters[price][value]=100 |
| Date Range (Preset) | GET /products?filters[created_at]=this_week (today, yesterday, last_month, etc.) |
| Date Range (Custom) | GET /products?filters[created_at][from]=2023-01-01&filters[created_at][to]=2023-01-31 |
| Sort By Relation | GET /products?sort_by=category.name&sort_direction=desc |
| Global Search | GET /products?filters[search]=apple (Searches all columns in $searchable) |
3️⃣ Eager Loading Relations (?with=)
The package automatically handles eager loading of relationships based on the URL query. This is handled by the HandleRelations trait.
🔹 Usage
You can load relationships (including nested ones) by passing the with parameter in the request.
Example Request:
GET /api/products?with=category,vendor,reviews.user
How it works:
- The
HandleRelationstrait listens to theretrievedevent of the model. - It checks for the
withquery parameter. - It validates that the requested relations exist on the model (to prevent errors).
- It loads them automatically before the data reaches your Resource/Controller.
🔹 Support in Resources
When using the generated Resource, these relations are automatically included in the JSON response if they are loaded.
{
"id": "uuid-1",
"name": "Macbook Pro",
"category": {
"id": "cat-1",
"name": "Laptops"
},
"vendor": {
"id": "ven-1",
"name": "Apple"
}
}
🔹 Customizing the Resource
If you need to add custom fields or conditionally load relations, you can override appendAdditionalRelations or appendedResource in your generated Resource.
// App/Transformers/ProductResource.php
use Bishnu\RepositoryGenerator\Base\BaseJsonResource;
class ProductResource extends BaseJsonResource
{
/**
* Define the data structure
*/
protected function appendedResource($request): array
{
return [
'id' => $this->uuid,
'name' => $this->name,
'price' => $this->price,
'is_low_stock' => $this->stock < 10,
];
}
/**
* Manually add specific relations
*/
protected function appendAdditionalRelations(): array
{
return [
'category' => new CategoryResource($this->whenLoaded('category')),
'tags' => TagResource::collection($this->whenLoaded('tags')),
];
}
}
4️⃣ Custom Filter Logic (filterCustom)
If the standard filters aren't enough, you can define custom logic in your Model.
// App/Models/Product.php
public function filterCustom($query, $key, $value)
{
if ($key === 'min_price') {
$query->where('price', '>=', $value);
return true; // Stop default processing for this key
}
if ($key === 'is_on_sale') {
$query->whereNotNull('sale_price');
return true;
}
return false; // Let the repository handle other keys
}
5️⃣ Role-Based Access Control (RBAC)
The Repository automatically applies security scopes if the user is NOT an admin.
🔹 Scenario: Permission-Based Visibility
- Admin: Sees all orders.
- Manager: Sees orders in their assigned
branch_id. - User: Sees only their own orders.
// App/Models/Order.php
public function scopeForUser($query, $user)
{
// 1. Managers see branch orders
if ($user->hasRole('manager')) {
return $query->where('branch_id', $user->branch_id);
}
// 2. Regular users see their own
return $query->where('user_id', $user->id);
}
How it works:
The repository identifies the user by checking request()->user(). If isAdmin() returns false, it automatically calls forUser($query, $user).
6️⃣ Handling Relationships (Nested Create/Update)
Create or update a Model and its relations in one JSON payload. The BaseModel already includes the logic to handle these relationships.
The Payload:
{
"name": "New Smartphone",
"category_id": "uuid-here",
"relation": {
"tags": ["uuid-1", "uuid-2"],
"specifications": {
"color": "Midnight Black",
"ram": "12GB"
},
"gallery": [
{ "title": "Front View", "sort_order": 1 },
{ "title": "Back View", "sort_order": 2 }
]
}
}
The Model:
use Bishnu\RepositoryGenerator\Base\BaseModel;
class Product extends BaseModel {
protected static $hasRelations = ['tags', 'specifications', 'gallery'];
}
7️⃣ Media & File Management (Global)
Built on top of Spatie Media Library, with advanced features like global deletion and replacement.
🔹 Uploading Files
Send files via multipart/form-data with the key files or your custom collection name.
🔹 Global Media Deletion
To delete specific media items by their UUID (useful for gallery management):
{
"clear_media": ["uuid-of-media-1", "uuid-of-media-2"]
}
This works globally on any update/store request.
🔹 Replace Collection
To clear an entire collection (e.g., avatar) and replace with a new one:
{
"media": {
"clear": true
}
}
8️⃣ "How It Works" - A Step-by-Step Example
Let's say you want to build a Blog API.
- Create Table: Add a
poststable withtitle,content,user_id. - Run Command:
php artisan make:repo Post. - Customize Model:
- Ensure it extends
BaseModel. - Define
$searchable = ['title']; - Add
scopeForUserto restrict post editing to owners.
- Ensure it extends
- Done!: You now have:
GET /api/posts(Filtered, Paginated, Scoped)POST /api/posts(Validated, Auto-UUID generation)PUT /api/posts/{uuid}(Auto-Relation handling)DELETE /api/posts/{uuid}
9️⃣ UUID vs. Standard ID Handling
The package is designed to be identifier-agnostic. You can use standard auto-incrementing IDs, UUIDs, or even a mix of both across different tables.
- Auto-Detection: The package checks if a
uuidcolumn exists in your table. If it does, it uses UUIDs for lookups and relationship resolution. If not, it falls back to standardid. - Automatic Generation: If a
uuidcolumn exists, theBaseModelautomatically generates a UUID upon record creation. - Payload Resolution: When you send a nested relationship payload (e.g.,
category: "some-uuid"), the package automatically resolves it to the correct internal database ID, whether searching by UUID or primary ID. - Resource Output: The generated
Resourceclasses automatically map the JSONidproperty to the model'suuidcolumn if present, otherwise to the standardid.
⚙️ Configuration
The package configuration is located at config/modules.php. You can customize:
path: Where modules are stored.namespace: The base namespace for your app.stubs: Override the default templates.
📄 License
MIT License.