puncoz / laravel-repository
A basic light weight package to implement Repository Design Pattern in Laravel.
Fund package maintenance!
JoBinsJP
Requires
- php: ^8.0
- illuminate/database: ^9.0|^10.0|^11.0|^12.0
- illuminate/support: ^9.0|^10.0|^11.0|^12.0
- league/fractal: ^0.20.1
Requires (Dev)
- larastan/larastan: ^2.9
- orchestra/testbench: ^7.49
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^1.2
- phpstan/phpstan-phpunit: ^1.4
This package is auto-updated.
Last update: 2025-05-23 17:27:29 UTC
README
A lightweight and flexible implementation of the Repository Pattern for Laravel applications. This package provides a clean and consistent way to handle data access layers in your Laravel applications.
Features
- Clean and simple Repository Pattern implementation
- Built-in filtering system with criteria pattern
- Data transformation using Fractal
- Scope query support
- Soft delete handling
- Eloquent model integration
- Flexible and extensible architecture
Installation
You can install the package via composer:
composer require puncoz/laravel-repository
You can publish the config file with:
php artisan vendor:publish --provider="JoBins\LaravelRepository\LaravelRepositoryServiceProvider"
Basic Usage
- Create a repository interface:
use JoBins\LaravelRepository\Contracts\RepositoryInterface; interface UserRepository extends RepositoryInterface { // Add custom methods if needed }
- Create a repository implementation:
use JoBins\LaravelRepository\LaravelRepository; use App\Models\User; class UserEloquentRepository extends LaravelRepository implements UserRepository { public function model(): string { return User::class; } }
- Register the repositories in the service provider:
use JoBins\LaravelRepository\LaravelRepositoryServiceProvider; class RepositoryServiceProvider extends ServiceProvider { public function register() { // ideally (for larger apps) recommended to maintain these bindings in config file (bindings/repositories.php) // bindings/repositories.php // return [ // UserRepository::class => UserEloquentRepository::class // ]; collect(config('bindings.repositories'))->each(function (string $implementation, string $contract) { $this->app->bind($contract, $implementation); }); } }
In Laravel 11 and 12, manually register your service provider in bootstrap/providers.php:
return [ // Other service providers... App\Providers\RepositoryServiceProvider::class, ];
- Use the repository in your application:
class UserController extends Controller { public function __construct( private UserRepository $repository ) {} public function index() { $users = $this->repository->all(); return view('users.index', compact('users')); } }
✅ Ready to rock!
Now, Laravel will automatically resolve the correct repository implementation via the interface wherever it's type-hinted using constructor injection or service container resolution.
⚙️ New Artisan Commands
This package now includes three powerful Artisan commands to help scaffold Repositories, Transformers, and Filters with minimal effort.
🔧 Configuration You can customize the default paths via the repository file located at: config/repository.php
return [ 'repository_path' => env('REPOSITORY_PATH', 'app/Repositories'), 'interface_subfolder' => env('REPOSITORY_INTERFACE_SUBFOLDER', true), 'repository_suffix' => env('REPOSITORY_SUFFIX', 'EloquentRepository'), 'interface_suffix' => env('REPOSITORY_INTERFACE_SUFFIX', 'Repository'), 'transformer_path' => env('TRANSFORMER_PATH', 'app/Transformers'), 'filter_path' => env('FILTER_PATH', 'app/Filters'), ];
📦 make:repository
-
interface_subfolder:
-
If true, the interface will be placed in an Interfaces/ subfolder.
-
If false, it will be placed in the same folder as the repository.
Generate Repository and Interface as same time with:
php artisan make:repository {name}
Example
php artisan make:repository product/stock
This will generate:
-
app/Repositories/Product/StockRepository.php
-
app/Repositories/Product/Interfaces/StockRepositoryInterface.php (if interface_subfolder is true)
-
Or app/Repositories/Product/StockRepositoryInterface.php (if false)
It automatically handles:
-
Folder creation
-
Proper naming conventions
-
Namespace and use statements
🔄 make:transformer
Generate a Transformer class.
php artisan make:transformer {name}
Example
php artisan make:transformer product/stock
✅ This will generate:
- app/Transformers/Product/StockTransformer.php
🔍 make:filter
Generate a Filter class for Eloquent query filtering.
php artisan make:filter {name}
Example
php artisan make:filter product/stock
✅ This will generate:
- app/Filters/Product/StockFilter.php
🧠 💡 Notes
-
Slashes (/) in names are used to create subdirectories automatically.
-
All files are created with proper namespaces and boilerplate code to get you started instantly.
-
You can override default paths using the config file or environment variables.
Available Methods
Basic Operations
all(array $columns = ['*'])
: Get all recordsfind(int|string $modelId, array $columns = ['*'])
: Find by IDfindByField(string $field, mixed $value, array $columns = ['*'])
: Find by fieldgetByField(string $field, mixed $value, array $columns = ['*'])
: Get multiple (list/collection) by fieldcreate(array $data)
: Create new recordupdate(array $data, int|string $modelId)
: Update recorddelete(int|string $modelId)
: Delete recorddeleteWhere(array $where)
: Delete by conditionsupdateOrCreate(array $queries, array $values = [])
: Update or create record
Filtering and Scopes
filter(FilterCriteria $filter)
: Apply filter criteriaskipFilters(bool $status = true)
: Skip filtersresetFilters()
: Reset all filtersscopeQuery(Closure $scopeQuery)
: Add query scope
Data Transformation
setTransformer(TransformerAbstract|string $transformer)
: Set transformerskipTransformer(bool $skip = true)
: Skip transformationpresent(Collection|AbstractPaginator|Model $data)
: Present data using transformer
Relations
with(string|array $relations)
: Eager load relationssetIncludes(array|string $includes)
: Set transformer includes
Creating Custom Filters
- Create a filter class:
use Illuminate\Database\Eloquent\Builder; use JoBins\LaravelRepository\Filters\Filterable; class UserFilter extends Filterable { /** * Hook that runs before any filter methods * [optional method] */ public function preHook(Builder $query, array $filters): Builder { return $query; } /** * Filter method for 'search' query/filter parameter * Method name must be in camelCase with 'Filter' suffix * Second parameter will be the value from queries/filters array (coming in the constructor) */ public function searchFilter(Builder $query, ?string $search): Builder { return $query->where(function (Builder $query) use ($search) { $query->orWhere('name', 'ilike', "%{$search}%") ->orWhere('email', 'ilike', "%{$search}%"); }); } /** * Filter method for 'status' query/filter parameter */ public function statusFilter(Builder $query, ?string $status): Builder { return $query->where('status', $status); } /** * Filter method for 'created_at' query/filter parameter * Example of how query/filter parameter names are converted to method names: * 'created_at' => 'createdAtFilter' */ public function createdAtFilter(Builder $query, ?string $date): Builder { return $query->whereDate('created_at', $date); } /** * Hook that runs after all filter methods */ public function postHook(Builder $query, array $filters): Builder { return $query; } }
- Apply the filter with queries:
// In your controller or service $queries = [ 'search' => 'john', // Will call searchFilter() 'status' => 'active', // Will call statusFilter() 'created_at' => '2025-04-14', // Will call createdAtFilter() ]; // or, $queries = $request->all(); $users = $repository ->filter(new UserFilter($queries)) ->all();
How Filters Work
-
Query Parameter to Method Mapping:
- queries/filters array is passed to the constructor
- Query/filter parameters (keys in the array passed to the constructor) are automatically mapped to filter methods
- Method names must be in camelCase and end with 'Filter' suffix
- Examples:
search
=>searchFilter()
user_status
=>userStatusFilter()
created_at
=>createdAtFilter()
-
Filter Method Signature:
public function someKeyFilter(Builder $query, mixed $value): Builder
- First parameter: Eloquent Query builder instance
- Second parameter: Value from the queries array
- Must return: Eloquent Query Builder instance
-
Filter Lifecycle:
preHook -> filterMethods -> postHook
preHook
: Runs before any filter methods- Filter methods: Run in the order they are defined
postHook
: Runs after all filter methods
-
Optional Methods:
- All filter methods are optional
preHook
andpostHook
are optional- Only methods matching query parameters will be called
Data Transformation
The package uses Fractal Transformers for data transformation. Here's how to implement transformers:
- Create a transformer class:
use League\Fractal\TransformerAbstract; use App\Models\User; class UserTransformer extends TransformerAbstract { protected array $availableIncludes = [ 'posts', 'comments' ]; public function transform(User $user): array { return [ 'id' => $user->id, 'name' => $user->name, 'email' => $user->email, 'created_at' => $user->created_at->toISOString(), 'updated_at' => $user->updated_at->toISOString(), ]; } public function includePosts(User $user) { return $this->collection($user->posts, new PostTransformer()); } public function includeComments(User $user) { return $this->collection($user->comments, new CommentTransformer()); } }
- Use transformation in your application:
// Get transformed data $users = $repository->all(); // Returns non-transformed data (collection) // Set transformer to the repository // All subsequent queries (all, find, findByField, getByField etc.) will use this transformer and return transformed data $users = $repository->setTransformer(UserTransformer::class)->all(); // Include relations in transformation $users = $repository ->setTransformer(UserTransformer::class) ->setIncludes(['posts', 'comments']) ->all(); // Skip transformer for specific query $rawUsers = $repository ->skipTransformer() ->all(); // Returns raw data
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.