richan-fongdasen/eloquent-repository
Installation
composer require richan-fongdasen/eloquent-repository
Publish the config (if needed):
php artisan vendor:publish --provider="RichanFongdasen\EloquentRepository\RepositoryServiceProvider"
Define a Repository
Create a repository class (e.g., app/Repositories/UserRepository.php):
namespace App\Repositories;
use RichanFongdasen\EloquentRepository\EloquentRepository;
use App\Models\User;
class UserRepository extends EloquentRepository
{
public function model()
{
return User::class;
}
}
Register the Repository
Bind the repository in a service provider (e.g., AppServiceProvider):
$this->app->bind(
\App\Repositories\UserRepository::class,
\App\Repositories\UserRepository::class
);
First Use Case Inject the repository into a controller/service and use basic CRUD:
use App\Repositories\UserRepository;
class UserController extends Controller
{
protected $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function index()
{
$users = $this->userRepository->all();
return view('users.index', compact('users'));
}
}
Basic CRUD Operations
// Fetch all records
$users = $userRepository->all();
// Find by ID
$user = $userRepository->find(1);
// Create
$user = $userRepository->create(['name' => 'John']);
// Update
$user = $userRepository->update(1, ['name' => 'Updated John']);
// Delete
$userRepository->delete(1);
Query Scoping Define custom scopes in the repository:
public function activeUsers()
{
return $this->scopeQuery(function ($query) {
return $query->where('active', true);
})->all();
}
Relationship Handling
Use with() for eager loading:
$user = $userRepository->find(1, ['posts']);
Transactions Wrap operations in a transaction:
$userRepository->transaction(function ($repository) {
$user = $repository->create(['name' => 'Alice']);
$user->posts()->create(['title' => 'Hello']);
});
$this->mock(UserRepository::class)->shouldReceive('find')->andReturn($user);
Model Binding
Ensure the model() method returns the fully qualified class name (e.g., App\Models\User).
return 'User'; (fails)return User::class; (works)Query Caching Avoid caching raw queries without a TTL (Time-To-Live) to prevent stale data:
// Bad: No TTL
$this->scopeQuery(function ($query) {
return $query->remember(60); // Unsafe!
});
// Good: With TTL
$this->scopeQuery(function ($query) {
return $query->remember(60 * 5); // 5 minutes
});
Mass Assignment Risks
Use $fillable or $guarded in your model to prevent mass assignment vulnerabilities when using create() or update().
Transaction Rollbacks
Unhandled exceptions in transaction() will silently roll back. Use try-catch:
try {
$userRepository->transaction(...);
} catch (\Exception $e) {
// Log or handle the error
}
\DB::enableQueryLog();
$users = $userRepository->all();
\Log::info(\DB::getQueryLog());
boot() to add listeners:
protected static function boot()
{
static::created(function ($model) {
\Log::info("Created: " . $model->name);
});
}
Custom Methods Extend the repository with domain-specific methods:
public function searchByEmail($email)
{
return $this->scopeQuery(function ($query) use ($email) {
return $query->where('email', 'like', "%{$email}%");
})->first();
}
Repository Interfaces Define interfaces for better testability and loose coupling:
interface UserRepositoryInterface {
public function find(int $id);
public function all();
}
Dynamic Scopes Use traits or mixins to share query logic across repositories:
trait SoftDeletesScope {
public function withTrashed()
{
return $this->scopeQuery(function ($query) {
return $query->withTrashed();
});
}
}
How can I help you explore Laravel packages today?