sazl / laravel-repokit
Repository Pattern implementation for Laravel
Requires
- php: ^8.1
- illuminate/console: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Generate Laravel repositories, services, and their interfaces with Artisan. Create both layers in one command, choose Query Builder or Eloquent, and customize the generated code through published stubs.
Controller → Service → Repository → Database
Requirements
PHP ^8.1 and Laravel 10.x, 11.x, or 12.x, with a PHP version supported by your Laravel release.
Installation
composer require sazl/laravel-repokit
Laravel discovers Sazl\LaravelRepokit\Providers\RepokitProvider automatically through Composer.
Usage
Use a base name such as User or user_profile. Names are converted to StudlyCase, then given the appropriate repository, service, or interface suffix.
Generate a complete module
php artisan make:module User
Creates a repository, a service that injects its repository interface, and both interfaces. It also records the bindings in config/repository.php and config/service.php.
The repository uses Query Builder by default. To inject an Eloquent model instead:
php artisan make:module User --model=User
The model resolves to App\Models\User. The command generates the repository and service files; create the model separately. See Configuration and bindings for repository container registration.
Generate a repository
php artisan make:repository User
Creates app/Repositories/Contracts/UserRepositoryInterface.php and app/Repositories/Databases/UserRepository.php.
The default implementation uses the DB facade, the mysql connection, and a plural snake_case table name such as users. Adjust the generated connection and table properties for your application.
To use an Eloquent model:
php artisan make:repository User --model=User
--model also accepts a full namespace:
php artisan make:repository User --model='App\Custom\User'
The short option is -M. The same model options work with make:module.
Generate a service
php artisan make:service User
Creates app/Services/Contracts/UserServiceInterface.php and app/Services/UserService.php. By default, the service injects App\Repositories\Contracts\UserRepositoryInterface.
To inject a different repository interface:
php artisan make:service Order --repository=User
The short option is -R. Pass the repository's base name. This command creates only the service and its interface, so generate the repository separately or use make:module to create both layers.
Generate an empty service
php artisan make:service User --empty
The short option is -e. The bundled empty template removes the pre-built methods from the interface and service, but keeps the constructor and repository injection. You can combine --empty with --repository.
Replace existing files
All three generators stop when they encounter an existing target file unless --force is supplied:
php artisan make:module User --model=User --force php artisan make:repository User --force php artisan make:service User --empty --force
These are separate examples. Rerun with the options you want in the replacement files. --force overwrites every existing file generated by that command, discarding manual changes. For a module, this includes all four files. Existing config bindings remain unchanged, even with --force.
Without --force, an existing file produces this message:
File already exists: /project/app/Repositories/Contracts/UserRepositoryInterface.php
Choose a different name, or rerun with --force to overwrite existing files. Manual changes will be lost.
Files are written in order. If a later file conflicts, files written earlier in that run remain on disk.
Generated file structure
For make:module User:
app/
├── Repositories/
│ ├── Contracts/
│ │ └── UserRepositoryInterface.php
│ └── Databases/
│ └── UserRepository.php
└── Services/
├── Contracts/
│ └── UserServiceInterface.php
└── UserService.php
config/
├── repository.php
└── service.php
The bundled templates provide these methods:
| Repository | Service | Behavior |
|---|---|---|
all() |
getAll() |
Fetch all records. |
find($id) |
getById($id) |
Find a record by ID. |
create(array $data) |
create(array $data) |
Insert a record. |
update($id, array $data) |
update($id, array $data) |
Update a record by ID. |
delete($id) |
delete($id) |
Delete a record by ID. |
Service methods delegate to the injected repository. Query Builder create() returns an inserted ID, while Eloquent create() returns a model. The Eloquent template returns false when updating or deleting a missing record.
Configuration and bindings
Generators publish the relevant config file automatically if it is missing. You can also publish the files directly:
php artisan vendor:publish --tag=repository-config php artisan vendor:publish --tag=service-config
Repository mappings are written to config/repository.php:
<?php return [ 'bindings' => [ \App\Repositories\Contracts\UserRepositoryInterface::class => \App\Repositories\Databases\UserRepository::class, ], ];
Service mappings are written to config/service.php:
<?php return [ 'bindings' => [ \App\Services\Contracts\UserServiceInterface::class => \App\Services\UserService::class, ], ];
An existing mapping for an interface is kept as-is. Adding a new mapping rewrites the config file as a bindings array, so keep unrelated settings and comments elsewhere.
The current package provider loads service.bindings into Laravel's container when the interface and implementation exist. It does not currently load repository.bindings. To resolve a generated service's repository dependency, add this binding to your application's AppServiceProvider::register():
$this->app->bind( \App\Repositories\Contracts\UserRepositoryInterface::class, \App\Repositories\Databases\UserRepository::class, );
Repeat the binding for each generated repository. The generators write config mappings; they do not edit AppServiceProvider.
Console output
For php artisan make:module User --model=User, with config files already published and no existing generated files:
Generating module [User].
Repository model: App\Models\User
Created: /project/app/Repositories/Contracts/UserRepositoryInterface.php
Created: /project/app/Repositories/Databases/UserRepository.php
Created: /project/app/Services/Contracts/UserServiceInterface.php
Created: /project/app/Services/UserService.php
Registered binding in config/repository.php: App\Repositories\Contracts\UserRepositoryInterface => App\Repositories\Databases\UserRepository.
Registered binding in config/service.php: App\Services\Contracts\UserServiceInterface => App\Services\UserService.
Paths are absolute and depend on your project location. Each successful write reports Created: or Overwritten:. A new file still reports Created: when --force is supplied.
Repository and module commands show the selected model or Repository type: Query Builder. Service commands show the repository interface and identify an empty template when selected.
Existing bindings report already exists and left unchanged. Missing or unwritable config files produce an error message after file generation.
Customize generated code
Publish the stubs into your application:
php artisan vendor:publish --tag=repokit-stubs
Edit the files under resources/stubs/vendor/repokit/:
| Stub | Used for |
|---|---|
repositories/contract.stub |
Repository interfaces. |
repositories/implementation.stub |
Query Builder repositories. |
repositories/implementation.model.stub |
Repositories generated with --model. |
services/contract.stub |
Standard service interfaces. |
services/implementation.stub |
Standard services, including those generated by make:module. |
services/contract.empty.stub |
Service interfaces generated with --empty. |
services/implementation.empty.stub |
Services generated with --empty. |
Keep the existing {{ ... }} placeholders where you want the generators to insert names and model details. Published stubs take precedence over bundled stubs. If a published file is missing, the generator falls back to the corresponding bundled file.
Stub edits affect future generation. Regenerating existing files requires --force and replaces their contents with the newly rendered stubs.
Command reference
| Command | Options | Result |
|---|---|---|
make:module {name} |
--model / -M, --force |
Repository, service, both interfaces, and both config mappings. |
make:repository {name} |
--model / -M, --force |
Repository, interface, and repository config mapping. |
make:service {name} |
--repository / -R, --empty / -e, --force |
Service, interface, and service config mapping. |
Local development
Clone the package and install its dependencies:
git clone https://github.com/sazl/laravel-repokit.git
cd laravel-repokit
composer install
The test suite uses PHPUnit and Orchestra Testbench. Run it from the package directory:
php vendor/bin/phpunit
To use your local checkout in a Laravel application, add a path repository to that application's composer.json:
{
"repositories": [
{
"type": "path",
"url": "../laravel-repokit",
"options": {
"symlink": true
}
}
]
}
Adjust url relative to the Laravel application's directory. Then run this command from that application:
composer require sazl/laravel-repokit:@dev
The path repository requests a symlink so local package edits are reflected in the application. Laravel discovers the package provider through its Composer metadata.
License
MIT