English Documentation
`gohari/repository-pattern` helps Laravel applications keep data access predictable by generating repositories, interfaces, optional service layers, DTOs, and service-provider bindings.
This package was created by Mohamadreza Gohari from Gandom Software Group.
Installation
Install the package with Composer:
composer require gohari/repository-pattern
Laravel package discovery registers the provider automatically:
Gohari\RepositoryPattern\RepositoryPatternServiceProvider::class
If package discovery is disabled, register it manually in config/app.php:
'providers' => [
Gohari\RepositoryPattern\RepositoryPatternServiceProvider::class,
],
Publish configuration and stubs when you want to customize defaults:
php artisan vendor:publish --tag=repository-pattern-config
php artisan vendor:publish --tag=repository-pattern-stubs
Published stubs are stored in resources/stubs/vendor/repository-pattern.
Command Usage
php artisan repository:make {name} --model={model}
| Option | Description |
|---|---|
name |
Repository name. User and UserRepository both generate UserRepository. |
--model |
Model class name or FQCN. Defaults to the repository name. |
--path |
Repository output path. Defaults to config paths.repositories. |
--interface-path |
Repository interface output path. Defaults to config paths.interfaces. |
--force |
Overwrite existing generated files. |
--bind |
Register interface-to-implementation bindings in App\Providers\RepositoryServiceProvider. |
--service |
Generate repository, repository interface, service, service interface, DTO, and config. |
--service-path |
Service output path. Defaults to config paths.services. |
--dto |
Generate only the DTO alongside the repository. |
--dto-path |
DTO output path. Defaults to config paths.dtos. |
--config |
Copy the package config file into the application. |
Examples
php artisan repository:make User --model=User
php artisan repository:make UserRepository --model=User
php artisan repository:make Product --model="App\Models\Product"
php artisan repository:make Admin/User --model=User
php artisan repository:make User --model=User --service
php artisan repository:make User --model=User --service --bind
php artisan repository:make Product --model=Product --path=app/Domain/Repositories
php artisan repository:make Product --model=Product --force
Generated Repository
A basic command creates the repository and its contract:
php artisan repository:make User --model=User
app/Repositories/UserRepository.php
app/Repositories/Contracts/UserRepositoryInterface.php
Repository output
<?php
namespace App\Repositories;
use App\Models\User;
use App\Repositories\Contracts\UserRepositoryInterface;
use Gohari\RepositoryPattern\BaseRepository;
class UserRepository extends BaseRepository implements UserRepositoryInterface
{
public function __construct(User $user)
{
parent::__construct($user);
}
}
Contract output
<?php
namespace App\Repositories\Contracts;
use Gohari\RepositoryPattern\BaseRepositoryInterface;
interface UserRepositoryInterface extends BaseRepositoryInterface
{
//
}
Base Repository Methods
$repository->query();
$repository->getAll();
$repository->paginate(perPage: 15, page: 2);
$repository->bigDataPaginate(perPage: 100, cursor: $cursor);
$repository->findById($id);
$repository->findOrFail($id);
$repository->firstWhere('email', 'user@example.com');
$repository->findBy('email', 'user@example.com');
$repository->exists($id);
$repository->count();
$repository->create($data);
$repository->updateOrCreate(['email' => $email], $data);
$repository->update($id, $data);
$repository->delete($id);
$repository->deleteMany([$firstId, $secondId]);
$repository->search('email', 'user@example.com', '=');
$repository->searchByColumn('name', 'john');
$repository->with(['roles', 'profile'])->paginate();
$repository->withRelations(['roles', 'profile'])->get();
$repository->sortBy('created_at', 'desc')->get();
$repository->withTrashed()->getAll();
$repository->onlyTrashed()->count();
$repository->restore($id);
$repository->forceDelete($id);
Legacy aliases are still available: insertData, updateItem, and deleteData.
Service Layer, DTO, and Binding
Generate all layers together:
php artisan repository:make User --model=User --service --bind
app/Repositories/UserRepository.php
app/Repositories/Contracts/UserRepositoryInterface.php
app/Services/UserService.php
app/Services/UserServiceInterface.php
app/DTOs/UserData.php
config/repository-pattern.php
The generated service only forwards arguments. Query construction, relationships, sorting, and database access stay in the repository:
$users->paginate(['roles'], 'created_at', 'desc', 20, 2);
$users->bigDataPaginate(['roles'], 'id', 'asc', 100, $cursor);
$users->find($id, ['profile']);
$users->create(UserData::fromArray($data));
$users->restore($id);
$users->forceDelete($id);
Million-row pagination
bigDataPaginate uses cursor pagination for efficient next/previous navigation on large indexed tables. Pass the response's next_cursor or prev_cursor into the next request. It intentionally does not calculate a total count or expose page numbers. Use chunkById or lazyById for background processing of an entire table, not UI pagination.
Model-aware DTO generation
When a model defines $fillable, the generator creates matching public readonly DTO fields and uses primitive model casts for safe PHP types. Unknown input fields are discarded, explicitly provided null values remain in toArray(), and fields without a safe cast use mixed. If the model has no fillable fields, the original generic array DTO is generated unchanged.
When binding is enabled, the command creates or updates app/Providers/RepositoryServiceProvider.php:
public function register(): void
{
$this->app->bind(
\App\Repositories\Contracts\UserRepositoryInterface::class,
\App\Repositories\UserRepository::class
);
$this->app->bind(
\App\Services\UserServiceInterface::class,
\App\Services\UserService::class
);
}
The provider is registered in bootstrap/providers.php for modern Laravel apps, or in config/app.php when that file is available.
Configuration
Publish the config file:
php artisan vendor:publish --tag=repository-pattern-config
<?php
return [
'paths' => [
'repositories' => app_path('Repositories'),
'interfaces' => app_path('Repositories/Contracts'),
'services' => app_path('Services'),
'dtos' => app_path('DTOs'),
'stubs' => resource_path('stubs/vendor/repository-pattern'),
],
'namespaces' => [
'repositories' => 'App\\Repositories',
'interfaces' => 'App\\Repositories\\Contracts',
'services' => 'App\\Services',
'dtos' => 'App\\DTOs',
],
'auto_bind' => true,
'generate_service' => false,
'generate_model_if_missing' => true,
];
Publish and edit stubs when you need custom class templates:
php artisan vendor:publish --tag=repository-pattern-stubs
Testing, Analysis, Formatting, and Security
Install dependencies and run the quality checks:
composer install
composer test
composer analyse
composer format:test
Format code with Pint:
composer format
Security vulnerabilities should not be reported in public issues. See SECURITY.md for the reporting address and supported versions.
The package is released under the MIT License.
مستندات فارسی
پکیج gohari/repository-pattern برای پروژههای Laravel ساخته شده تا ساخت Repository، Contract، Service Layer، DTO،
binding و config قابل انتشار را سریعتر و تمیزتر کند.
سازنده این پکیج محمدرضا گوهری از تیم Gandom Software Group است.
نصب
پکیج را با Composer نصب کنید:
composer require gohari/repository-pattern
Laravel به صورت خودکار Service Provider پکیج را پیدا و ثبت میکند:
Gohari\RepositoryPattern\RepositoryPatternServiceProvider::class
اگر package discovery غیرفعال است، provider را دستی داخل config/app.php ثبت کنید:
'providers' => [
Gohari\RepositoryPattern\RepositoryPatternServiceProvider::class,
],
برای شخصیسازی config و stubها، آنها را publish کنید:
php artisan vendor:publish --tag=repository-pattern-config
php artisan vendor:publish --tag=repository-pattern-stubs
Stubهای publish شده در مسیر resources/stubs/vendor/repository-pattern قرار میگیرند.
دستورها
php artisan repository:make {name} --model={model}
| گزینه | توضیح |
|---|---|
name |
نام Repository. هر دو مقدار User و UserRepository خروجی UserRepository میسازند. |
--model |
نام مدل یا FQCN مدل. اگر وارد نشود از نام Repository استفاده میشود. |
--path |
مسیر خروجی Repository. پیشفرض از config بخش paths.repositories خوانده میشود. |
--interface-path |
مسیر خروجی Interface. پیشفرض app/Repositories/Contracts است. |
--force |
فایلهای موجود را overwrite میکند. |
--bind |
Binding بین Interface و Implementation را داخل App\Providers\RepositoryServiceProvider ثبت میکند. |
--service |
Repository، Interface، Service، ServiceInterface، DTO و config را با هم تولید میکند. |
--service-path |
مسیر خروجی Service. پیشفرض از config بخش paths.services خوانده میشود. |
--dto |
فقط DTO را کنار Repository تولید میکند. |
--dto-path |
مسیر خروجی DTO. پیشفرض از config بخش paths.dtos خوانده میشود. |
--config |
فایل config پکیج را داخل پروژه کپی میکند. |
مثالها
php artisan repository:make User --model=User
php artisan repository:make UserRepository --model=User
php artisan repository:make Product --model="App\Models\Product"
php artisan repository:make Admin/User --model=User
php artisan repository:make User --model=User --service
php artisan repository:make User --model=User --service --bind
php artisan repository:make Product --model=Product --path=app/Domain/Repositories
php artisan repository:make Product --model=Product --force
Repository تولید شده
این دستور:
php artisan repository:make User --model=User
این فایلها را میسازد:
app/Repositories/UserRepository.php
app/Repositories/Contracts/UserRepositoryInterface.php
خروجی Repository
<?php
namespace App\Repositories;
use App\Models\User;
use App\Repositories\Contracts\UserRepositoryInterface;
use Gohari\RepositoryPattern\BaseRepository;
class UserRepository extends BaseRepository implements UserRepositoryInterface
{
public function __construct(User $user)
{
parent::__construct($user);
}
}
خروجی Interface
<?php
namespace App\Repositories\Contracts;
use Gohari\RepositoryPattern\BaseRepositoryInterface;
interface UserRepositoryInterface extends BaseRepositoryInterface
{
//
}
متدهای BaseRepository
$repository->query();
$repository->getAll();
$repository->paginate(perPage: 15, page: 2);
$repository->bigDataPaginate(perPage: 100, cursor: $cursor);
$repository->findById($id);
$repository->findOrFail($id);
$repository->firstWhere('email', 'user@example.com');
$repository->findBy('email', 'user@example.com');
$repository->exists($id);
$repository->count();
$repository->create($data);
$repository->updateOrCreate(['email' => $email], $data);
$repository->update($id, $data);
$repository->delete($id);
$repository->deleteMany([$firstId, $secondId]);
$repository->search('email', 'user@example.com', '=');
$repository->searchByColumn('name', 'john');
$repository->with(['roles', 'profile'])->paginate();
$repository->withRelations(['roles', 'profile'])->get();
$repository->sortBy('created_at', 'desc')->get();
$repository->withTrashed()->getAll();
$repository->onlyTrashed()->count();
$repository->restore($id);
$repository->forceDelete($id);
Aliasهای قدیمی insertData، updateItem و deleteData همچنان پشتیبانی میشوند.
Service Layer، DTO و Binding
برای تولید همه لایهها با هم:
php artisan repository:make User --model=User --service --bind
app/Repositories/UserRepository.php
app/Repositories/Contracts/UserRepositoryInterface.php
app/Services/UserService.php
app/Services/UserServiceInterface.php
app/DTOs/UserData.php
config/repository-pattern.php
Service تولیدشده فقط ورودیها را پاس میدهد؛ ساخت query، relationship، sorting و دسترسی دیتابیس داخل Repository انجام میشود:
$users->paginate(['roles'], 'created_at', 'desc', 20, 2);
$users->bigDataPaginate(['roles'], 'id', 'asc', 100, $cursor);
$users->find($id, ['profile']);
$users->create(UserData::fromArray($data));
$users->restore($id);
$users->forceDelete($id);
Pagination برای جدولهای میلیونی
bigDataPaginate از cursor pagination برای حرکت به صفحه بعد و قبل روی جدولهای بزرگ و indexشده استفاده میکند. مقدار next_cursor یا prev_cursor پاسخ را در درخواست بعدی بفرستید. این روش عمداً total count و شماره صفحه تولید نمیکند. برای پردازش background کل جدول از chunkById یا lazyById استفاده کنید، نه برای pagination رابط کاربری.
ساخت DTO از روی Model
اگر Model دارای $fillable باشد، generator برای همان فیلدها propertyهای public readonly میسازد و برای castهای ساده type مناسب قرار میدهد. ورودیهای ناشناس حذف میشوند، nullهای ارسالشده در toArray() حفظ میشوند و castهای نامطمئن از نوع mixed خواهند بود. اگر Model هیچ fillable نداشته باشد، DTO عمومی آرایهای قبلی بدون تغییر ساخته میشود.
وقتی binding فعال باشد، فایل app/Providers/RepositoryServiceProvider.php ساخته یا بهروزرسانی میشود:
public function register(): void
{
$this->app->bind(
\App\Repositories\Contracts\UserRepositoryInterface::class,
\App\Repositories\UserRepository::class
);
$this->app->bind(
\App\Services\UserServiceInterface::class,
\App\Services\UserService::class
);
}
این provider در Laravelهای جدید داخل bootstrap/providers.php ثبت میشود و در پروژههای قدیمیتر در config/app.php.
تنظیمات و Stubها
برای انتشار config:
php artisan vendor:publish --tag=repository-pattern-config
<?php
return [
'paths' => [
'repositories' => app_path('Repositories'),
'interfaces' => app_path('Repositories/Contracts'),
'services' => app_path('Services'),
'dtos' => app_path('DTOs'),
'stubs' => resource_path('stubs/vendor/repository-pattern'),
],
'namespaces' => [
'repositories' => 'App\\Repositories',
'interfaces' => 'App\\Repositories\\Contracts',
'services' => 'App\\Services',
'dtos' => 'App\\DTOs',
],
'auto_bind' => true,
'generate_service' => false,
'generate_model_if_missing' => true,
];
برای انتشار stubها:
php artisan vendor:publish --tag=repository-pattern-stubs
تست، تحلیل ایستا، فرمت و امنیت
برای توسعه پکیج:
composer install
composer test
composer analyse
composer format:test
برای فرمت کردن کد با Pint:
composer format
آسیبپذیریهای امنیتی را در issue عمومی GitHub ثبت نکنید. روش گزارش در فایل SECURITY.md نوشته شده است.
این پکیج با لایسنس MIT منتشر شده است.