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

L5 Repository Laravel Package

sevenymedia/l5-repository

View on GitHub
Deep Wiki
Context7

Getting Started

This is a maintained fork of prettus/l5-repository (the repository/ presenter/ validator pattern package). It keeps the Prettus\Repository\* namespace, so existing use statements and docs from the upstream package still apply — only the Composer package name differs. Release 1.3.0 is a compatibility bump: it adds Laravel 5.8 support (which means a PHP >= 7.1.3 constraint in practice). No API changes, so this assessment describes the package as a whole.

Install and wire up

composer require sevenymedia/l5-repository
# do NOT also require prettus/l5-repository - same namespace, conflicting classes

On Laravel 5.5+ the provider is auto-discovered; otherwise add to config/app.php:

Prettus\Repository\Providers\RepositoryServiceProvider::class,

Publish the config (this is the first file you should read):

php artisan vendor:publish --provider="Prettus\Repository\Providers\RepositoryServiceProvider"
# -> config/repository.php

First use case — scaffold an entity

php artisan make:entity Post
# creates app/Repositories/PostRepository.php + PostRepositoryEloquent.php
# optionally Presenter, Transformer, Validator, Model, Controller, migration, seeder

Then bind interface → implementation (an AppServiceProvider or a dedicated RepositoryServiceProvider):

$this->app->bind(PostRepositoryInterface::class, PostRepositoryEloquent::class);

And inject the interface into your controller:

public function __construct(PostRepositoryInterface $repository)
{
    $this->repository = $repository;
    $this->repository->pushCriteria(app(RequestCriteria::class));
}

Order of files to read: the generated PostRepositoryEloquent (it's just a BaseRepository subclass with a model() method and $fieldSearchable), then config/repository.php, then Prettus\Repository\Criteria\RequestCriteria, which is what most day-to-day behaviour (search/filter/sort/pagination) flows through.

Implementation Patterns

Thin interface, thin Eloquent implementation. Keep the interface as the contract your controllers/services/commands type-hint, and let the Eloquent class stay declarative:

class PostRepositoryEloquent extends BaseRepository implements PostRepository
{
    protected $fieldSearchable = [
        'title'        => 'like',   // operator matters
        'status'       => '=',
        'author.name'  => 'like',   // dot notation traverses relations
    ];

    public function model(): string
    {
        return Post::class;
    }
}

Search/filter/sort for free via RequestCriteria. Pushing it in the controller constructor gives you ?search=, ?filter[status]=published, ?orderBy=created_at&sortedBy=desc, ?limit=50 with no extra code. The accepted parameter names are configurable in config/repository.php. That is the single highest-leverage pattern in the package — one line gives a list endpoint real querying.

Custom criteria for anything non-trivial or reused. Criteria are the right extension point instead of fat repository methods:

class PublishedOnly implements CriteriaInterface
{
    public function apply($model, RepositoryInterface $repository)
    {
        return $model->where('status', 'published');
    }
}

$posts = $this->repository->pushCriteria(new PublishedOnly())->paginate(20);

Criteria execute in push order, are introspectable (getCriteria()), and removable by class (popCriteria(PublishedOnly::class)) or wholesale (skipCriteria()), which makes them composable per-endpoint and testable in isolation.

Presenters/transformers for API shaping. Extend FractalPresenter, implement transform() returning a PostTransformer, set it on the repository, and transform() at call time. Use this when response shape must be stable and decoupled from the schema; use skipPresenter() when you want raw Eloquent models (e.g. for a Blade view or a form).

Validation at the repository boundary. Give the repository a Prettus\Validator\LaravelValidator subclass with create/update rule sets; create() and update() will validate and throw Prettus\Validator\Exceptions\ValidatorException. Catch it in the controller and redirect()->withErrors($e->getMessageBag()). Useful for admin/console write paths that bypass FormRequests.

Caching when reads dominate. Implement CacheableInterface and use the CacheableRepository trait, set $cacheMinutes, and configure repository.cache.enabled in the config. Cache keys incorporate the method plus arguments plus serialized criteria, so distinct criteria combinations are cached separately — but see the caveats below.

Repository events for side effects. The Eloquent base class fires RepositoryEntityCreated / Updated / Deleted, which is a clean hook for search-index syncs, audit logs, and cache flushing without touching the repository itself.

Testing. Bind the interface to a fake or an in-memory implementation in a test service provider; because controllers depend on the interface, the controller tests never touch the repository implementation at all. For integration tests, disable caching (config(['repository.cache.enabled' => false])) and skipCriteria() so no request state leaks in.

Gotchas and Tips

  • RequestCriteria is request-coupled. It reads the current HTTP request; in queued jobs, console commands, or long-running workers a stale request can supply bogus filter/search params. Call skipCriteria() or popCriteria(RequestCriteria::class) in non-HTTP entry points.
  • Presenters change return types. With a presenter set, all()/paginate() return plain arrays and find()/first() return arrays or null — code that assumed an Eloquent model ($post->save(), $post->relation) breaks. Reach for skipPresenter() explicitly rather than debugging a confusing null.
  • scopeQuery() persists on the instance. If the repository is bound as a singleton or resolved once per request, a scopeQuery closure can leak into later calls. resetScope() after use, or prefer criteria (which have explicit pop/skip semantics).
  • lists() is gone — use pluck(). Older tutorials still show lists($value, $key); it will fatal on modern Illuminate versions.
  • Cache invalidation is repository-local. The cacheable trait flushes on writes it performs, not on seeders, direct Model::save(), bulk DB queries, or writes from other repositories. If your data changes outside the repository, you need explicit invalidation (an event listener, or a cache-key versioning scheme). Also decide deliberately what cacheMinutes of zero should mean for you and verify against your driver.
  • Pagination limit comes from ?limit=. If a client can pass limit=1000000, cap it — the package honours the request value and there's no built-in maximum.
  • Dot-notation search only works for declared relations. Relation search via 'author.name' => 'like' requires the relation to actually exist and be queryable; complex relation filters (nested whereHas with constraints) are usually cleaner as a custom criterion than as a $fieldSearchable entry.
  • Operators in $fieldSearchable are meaningful. = requires an exact match; like wraps with %...%. Getting this wrong is the most common cause of "search returns nothing for one field but works for others".
  • Validators are optional and silent. If validator() returns nothing, create/update skip validation entirely rather than failing loudly — easy to think you have validation when you don't.
  • Soft deletes are respected. delete() soft-deletes when the model uses SoftDeletes, and all() excludes trashed rows unless a criterion explicitly includes them.
  • make:entity is interactive. For scripted scaffolding pass the flags (--model, --repository, --presenter, --transformer, --validator, --controller, --migration, --seeder) or use --force; generated namespaces/folders come from config/repository.php's generator section, so adjust that first if your project layout differs.
  • Fork hygiene. Because the namespace is still Prettus\, upstream README examples work verbatim, but the Composer package identity differs: don't install both, and if you're migrating from prettus/l5-repository, remove it in the same composer require step (or add a replace) so Composer doesn't resolve two providers with clashing classes. Pin versions against your Laravel version — this fork's releases track Laravel support (1.3.0 → 5.8).
  • Extension points worth knowing: override parserResult() in a BaseRepository subclass to normalise every result; subclass BaseRepository for shared behaviour across repositories; swap repository.cache.repository for a custom cache class; register additional accepted request params in config/repository.php rather than patching RequestCriteria.
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.
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle