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

Laravel Repository Laravel Package

prettus/laravel-repository

A Laravel repository pattern package that decouples your application logic from Eloquent. It provides base repository and interface classes, reusable query criteria, caching support, and service container bindings for cleaner, testable, and more maintainable code.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package

    composer require prettus/laravel-repository
    

    On Laravel 5.5+ the service provider (Prettus\Repository\Providers\RepositoryServiceProvider) is auto-discovered. On older Laravel versions you must register it manually in config/app.php under providers.

  2. Scaffold a repository

    php artisan make:repository UserRepository
    

    You'll get app/Repositories/UserRepository.php and, if you pass --interface, a matching interface. The generated class extends Prettus\Repository\Eloquent\BaseRepository.

  3. Wire up the model

    In UserRepository set the property or override model():

    protected $model;
    
    public function model()
    {
        return \App\Models\User::class;
    }
    
  4. Bind the interface

    // app/Providers/RepositoryServiceProvider.php
    $this->app->bind(UserRepositoryInterface::class, UserRepository::class);
    
  5. First use case — inject the interface into a controller and swap a raw Eloquent call for $this->users->all() or $this->users->find($id).

Start by reading Prettus\Repository\Eloquent\BaseRepository — every method you'll lean on (all, find, findByField, findWhere, create, update, delete, paginate, with, scopeQuery) lives there. Then read BaseCriteria and BasePresenter once, because almost everything else in the package hinges on those two.

Implementation Patterns

Inject interfaces, not repositories. The main day-to-day value is testability. Controllers should type-hint UserRepositoryInterface, not UserRepository. Mock the interface in tests instead of hitting the database.

Use criteria for reusable query logic. Anything you'd otherwise copy-paste across endpoints — active-only filters, soft-delete handling, tenant scoping — belongs in a criteria class:

class OnlyActiveCriteria extends BaseCriteria
{
    public function apply($model, RepositoryInterface $repository)
    {
        return $model->where('active', true);
    }
}

Apply globally in the repository constructor via pushCriteria() for things like tenant isolation, and per-call via ->pushCriteria(new OnlyActiveCriteria) for optional filters.

The RequestCriteria pattern. The package ships Prettus\Repository\Criteria\RequestCriteria, which reads ?search=, ?filter=, ?order= and ?with= off the request and applies them automatically. Push it once in the repository constructor and most list endpoints stop needing custom code:

public function boot()
{
    $this->pushCriteria(app(RequestCriteria::class));
}

Presenters for output shaping. If you're returning data to an API, define a BasePresenter implementation (usually wrapping a Fractal transformer) and call ->setPresenter(new UserPresenter()) on the repository before the query, or set it once in the constructor. This keeps JSON response formatting out of controllers and out of models.

Validation via ValidatorTrait. Compose Prettus\Repository\Traits\ValidatorTrait into the repository and define rules() / rulesUpdate(). create() and update() then validate automatically and throw Prettus\Validator\Exceptions\ValidatorException on failure — catch it in a middleware or exception handler to return 422s uniformly.

Custom domain methods. Don't force everything through generic methods. Add named methods that express intent — getActiveAdmins(), findByExternalId() — and internally use $this->scopeQuery(fn($q) => ...)->first().

Ad-hoc scoping. scopeQuery() lets you drop to the query builder for one call without permanently altering state:

$this->users->scopeQuery(function ($query) {
    return $query->where('role', 'admin')->orderBy('name');
})->all();

Decorator for caching. Wrap the repository in a caching decorator that implements the same interface. This is the cleanest extension point the package offers because consumers never see the change.

Gotchas and Tips

  • $model must be set or model() overridden. A null model produces confusing ReflectionException or "class does not exist" errors deep inside BaseRepository::makeModel().
  • Criteria mutate state. pushCriteria() on a singleton-bound repository persists across the request. Laravel resolves per-request in normal HTTP flow, but in queued jobs or long-running processes (Octane, Swoole) you must call resetCriteria() afterwards or you'll leak filters between jobs.
  • skipCriteria() disables all criteria, not just one — including the global ones you may be relying on for tenancy or security. Prefer popCriteria(new SomeCriteria()) to remove a specific instance.
  • Criteria execution order matters. Criteria are applied in the order pushed. If you have an OrderBy criteria and a Where criteria that conflicts with it, you'll get surprising SQL.
  • find() throws RepositoryException when nothing matches; first() does not. Pick the method that matches your error handling — throwing-by-default is the opposite of Eloquent's find() and a common surprise.
  • paginate() returns a LengthAwarePaginator, all() returns a Collection, find() returns a model. Presenters can change these types entirely (e.g. Fractal may return an array), so don't mix presenting with logic that expects Eloquent objects.
  • Eager loading works via with(), but be aware criteria that call select() can drop relationships and cause N+1 without warning. Check DB::listen() output when adding criteria.
  • RequestCriteria is a footgun on public endpoints. It will accept arbitrary filters and with params from the query string. Scope it: either remove RequestCriteria from public-facing repositories or subclass it and whitelist fields via the $fieldSearchable property (and the $searchableFields used by the Searchable trait).
  • Validators only run on create() / update() through the repository. If you call $model->save() yourself elsewhere, you bypass validation entirely.
  • Extension points: makeModel() (swap out how the model is instantiated), applyCriteria() (intercept criteria application), parserResult() (adjust what's returned after a query — used by presenters), and model(). Overriding these is almost always cleaner than forking the package.
  • This is a small, low-velocity package (20 stars). Treat it as a thin abstraction you own rather than a framework dependency. Vendor the classes or pin the version — if the maintainer goes silent, patching BaseRepository in your own project is a realistic option.
  • The repository pattern isn't free. For simple CRUD it's overhead. Use it where you genuinely need swap-out data sources, criteria-based composition, or a place to hang caching/validation/presentation — not for every model.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route