Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Database Repository Laravel Package

eghamat24/database-repository

A Laravel package that adds a database repository layer for wrapping Eloquent queries in reusable repository classes. It helps keep models and controllers clean, reduces duplicated query logic, and makes data access more organized, consistent, and testable for common database operations.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package:
composer require eghamat24/database-repository

Laravel will auto-discover the service provider. On Lumen you must register the provider manually in bootstrap/app.php:

$app->register(Eghamat24\DatabaseRepository\...ServiceProvider::class);
  1. Create your first repository by extending the package's base class and pointing it at a model. Check vendor/eghamat24/database-repository/src first — the base class namespace and the exact contract name (BaseRepositoryInterface, CriteriaInterface) are the two things you will reference constantly:
namespace App\Repositories;

use App\Models\User;
use Eghamat24\DatabaseRepository\BaseRepository; // verify namespace in vendor/

class UserRepository extends BaseRepository
{
    protected $model = User::class;
}
  1. Bind it to an interface so controllers and services never depend on the concrete class:
// AppServiceProvider::register()
$this->app->bind(
    \App\Repositories\Contracts\UserRepositoryInterface::class,
    \App\Repositories\UserRepository::class
);
  1. First use case — thin controller, injected repository:
public function index(UserRepositoryInterface $users)
{
    return UserResource::collection($users->paginate(15));
}

If the base class supports criteria (most builds of this package do), your second step should be writing one filter criteria and applying it, because that is the feature that actually earns the dependency. Everything else is a thin wrapper over Eloquent that you could write in 20 lines.

Implementation Patterns

Repository + service layer. Keep the repository purely about persistence and put business rules in a service. The repository's job is to hide query construction; do not let validation, authorization, or mail sending leak into it.

class UserRepository extends BaseRepository implements UserRepositoryInterface
{
    protected $model = User::class;

    public function findVerifiedByEmail(string $email): ?User
    {
        return $this->model->newQuery()
            ->where('email', $email)
            ->whereNotNull('email_verified_at')
            ->first();
    }
}

Criteria for composable filters. Instead of ten boolean flags in a query method, one criteria class per filter and push them from the caller:

class ActiveUsersCriteria implements CriteriaInterface
{
    public function apply($query)   // confirm the method signature in the contract
    {
        return $query->where('active', true);
    }
}

// caller
$users = $repo->pushCriteria(new ActiveUsersCriteria())->all();

This composes well for admin screens: pushCriteria(new ActiveUsersCriteria())->pushCriteria(new SearchCriteria($term))->paginate().

Reset criteria between calls. Pushed criteria are stored on the instance. In a long-lived service, a controller with multiple repository calls, or an Octane/queue worker, criteria leak into the next query. Either reset after each use or resolve a fresh repository per operation:

$users = $repo->pushCriteria(new ActiveUsersCriteria())->all();
$repo->resetCriteria(); // or popCriteria(...) for a single one

Eager loading via criteria, not controllers. Add a WithRelationsCriteria (or a with() argument on your own wrapper methods) so every listing endpoint has a consistent answer to N+1:

class WithUserRelationsCriteria implements CriteriaInterface
{
    public function apply($query)
    {
        return $query->with(['roles', 'profile']);
    }
}

Caching decorator. Rather than modifying the base class, wrap the repository in a decorator that implements the same interface and caches find()/findWhere() while passing writes through to the inner repository (and invalidating on write). This keeps the third-party class untouched and upgradeable.

Transactions at the service boundary. The repository should not open transactions. Call DB::transaction(fn () => $service->createUser($data)) and let the repository methods participate in the ambient transaction.

Testing. Because controllers depend on the interface, tests can bind a fake repository or use $this->mock(UserRepositoryInterface::class). For repository tests, use RefreshDatabase and assert against real Eloquent — the wrapper is thin enough that mocking it tests nothing.

Extend the base class once, project-wide. Create App\Repositories\BaseRepository that extends the package class and adds your own helpers (findOrFail, updateOrCreate shortcuts, a with() passthrough). Then all repositories extend yours. When the package changes or you outgrow it, you have a single migration point.

Gotchas and Tips

  • Register with bind, not singleton. Criteria and any internal state live on the instance; a singleton will accumulate filters across requests and produce baffling "why is this query filtered" bugs. If you must use singleton, reset criteria religiously.
  • Criteria mutate the builder in place in some builds. Check whether apply() returns the query or mutates it, and follow the contract exactly — returning null from a criteria is a common cause of "call to member function on null" deep in the base class.
  • Global scopes and soft deletes still apply. The base class uses normal Eloquent queries, so SoftDeletes, ActiveScope, etc. are active. When you need the raw set, add an withoutGlobalScopes() criteria instead of bypassing the repository — keep one escape hatch, not many.
  • find() return type. Verify whether the base returns null, throws, or returns an empty model. Do not assume it mirrors findOrFail; wrap it in your own base class if the semantics do not match your app.
  • Mass assignment. create() and update() go through fill(), so $fillable/$guarded on the model are the real gate. If inserts silently drop fields, that is the first place to look, not the repository.
  • Debugging queries. Grab the underlying builder before execution:
$query = $repo->getModel()->newQuery()->where(...);
dump($query->toSql(), $query->getBindings());

Or add a global DB::listen() in a service provider during development to log every query the repository produces.

  • Config file. If vendor:publish exposes a config (default criteria, table prefix, pagination size), read the package's own config file before overriding anything — silently applied default criteria are a frequent source of "extra WHERE clauses" in production.
  • Low bus factor. With ~19 stars and a single maintainer, treat this as a small convenience layer, not infrastructure. Pin the version in composer.json, read the source (it is small), and keep your project-level BaseRepository as the abstraction boundary so you can swap the package for hand-rolled Eloquent or a fork without touching feature code.
  • Extension points to prefer: your own base class, a criteria base class, and a caching decorator. Avoid editing vendor/ — the package is small enough that a fork or an inline replacement is cleaner if you need behavior changes.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console