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

hocvt/l5-repository

Laravel repository package that abstracts the database layer behind reusable repositories. Add criteria, presenters, validators, Fractal transformers and caching without repeating query code, keeping controllers and models clean and testable.

View on GitHub
Deep Wiki
Context7

Getting Started

hocvt/l5-repository is a fork of prettus/l5-repository — the package still uses the Prettus\ namespace, so all configuration, contracts, and generators live under Prettus\Repository\* regardless of the Composer name. Confirm the installed namespace before copy-pasting docs from either repo.

Minimal setup:

composer require hocvt/l5-repository
php artisan vendor:publish --provider="Prettus\Repository\Providers\RepositoryServiceProvider"

This creates config/repository.php (generator paths, cache settings, Fractal serializer, etc.). Laravel 5.5+ auto-discovery registers the service provider.

Generate a repository + interface:

php artisan make:repository UserRepository

This drops app/Repositories/UserRepository.php and UserRepositoryInterface.php. Wire them in a service provider:

// app/Providers/RepositoryServiceProvider.php
public function register(): void
{
    $this->app->bind(
        \App\Repositories\UserRepositoryInterface::class,
        \App\Repositories\UserRepository::class
    );
}

Implement it:

namespace App\Repositories;

use App\Models\User;
use Prettus\Repository\Eloquent\BaseRepository;

class UserRepository extends BaseRepository
{
    public function model(): string
    {
        return User::class;
    }
}

First use case: inject the interface into a controller and use all(), find(), or paginate():

public function index(UserRepositoryInterface $users)
{
    return view('users.index', ['users' => $users->paginate(15)]);
}

Where to look first:

  1. config/repository.php — generator paths, cache defaults.
  2. Prettus\Repository\Contracts\RepositoryInterface — the full method surface (all, paginate, find, findBy, findWhere, create, update, delete, with, scopeQuery, pushCriteria, presenter, skipPresenter, resetCriteria, resetModel).
  3. The generated repository stub — it is intentionally minimal; everything else is opt-in.

Implementation Patterns

Interface-first binding + thin controllers

Always type-hint the contract, never the concrete class. This makes it trivial to swap to a cached or in-memory implementation later, and keeps controllers free of Eloquent calls.

class UserController extends Controller
{
    public function __construct(private UserRepositoryInterface $users) {}

    public function show(int $id)
    {
        return $this->users->find($id);
    }
}

Eager loading and scoped queries

with() sets persistent eager loads; scopeQuery() receives the Eloquent builder for ad-hoc filtering:

$active = $this->users
    ->with(['posts', 'profile'])
    ->scopeQuery(fn ($q) => $q->where('active', true))
    ->paginate(20);

Because with() and scopeQuery() mutate repository state, reset between logical operations:

$this->users->resetModel();     // clears model, with, scopeQuery
$this->users->resetCriteria();  // clears pushed criteria

Criteria for composable filters

Criteria are the package's main lever for keeping query logic out of controllers. Implement CriteriaInterface and push it per-call or globally.

use Prettus\Repository\Contracts\CriteriaInterface;
use Prettus\Repository\Contracts\RepositoryInterface;

class ActiveUsersCriteria implements CriteriaInterface
{
    public function apply($model, RepositoryInterface $repository)
    {
        return $model->where('active', true);
    }
}
$this->users->pushCriteria(new ActiveUsersCriteria())->paginate(20);

Push globally in boot() for cross-cutting scopes (tenant isolation, soft-delete variants):

public function boot(): void
{
    parent::boot(); // REQUIRED
    $this->pushCriteria(new TenantCriteria(auth()->user()->tenant_id));
}

RequestCriteria for index endpoints

The package ships Prettus\Repository\Criteria\RequestCriteria, which reads search, searchFields, filter, orderBy, and sortedBy from the current request. This is a fast way to get a filterable index without hand-writing query parsing.

$this->users->pushCriteria(app(RequestCriteria::class));

Requirements:

  • Override getFieldsSearchable() in the repository (or set $fieldSearchable) — otherwise search does nothing.
  • Be aware it reads the global request; this is a testing and reusability hazard.

Presenters / Fractal transformations

When a repository needs to return a transformed response, register a presenter implementing Prettus\Repository\Presenter\FractalPresenter and call presenter() or set it in the constructor.

public function presenter(): string
{
    return UserPresenter::class;
}

Then find()/paginate() return transformed payloads; use skipPresenter() when you need raw models (e.g., inside a service that will further mutate data).

Caching

Read-heavy repos can implement Prettus\Repository\Contracts\CacheableRepository and extend CacheRepository (or use the cacheable variant of BaseRepository). Configure via config/repository.php (cache.enabled, cache.minutes, cache.repository). Override cacheKey() and cacheMinutes() per repository, and remember the repository invalidates cache on create/update/delete but not on arbitrary scopeQuery/raw DB writes.

Validation

make:entity scaffolds a validator implementing Prettus\Repository\Contracts\ValidatorInterface (Prettus\Validator\LaravelValidator). Assign it via validator(). Rules run on create and update, and throw Prettus\Validator\Exceptions\ValidatorException — catch it in a form request / exception handler and map to 422.

Events

Repositories fire RepositoryEntityCreated, RepositoryEntityUpdated, RepositoryEntityDeleted. Listen for these when you want side effects (search indexing, audit logging) without stuffing them into the repository or model observers.

Generator workflow (make:entity)

php artisan make:entity User produces model, repository, interface, presenter, validator, controller, and migration in one shot. Treat this as scaffolding: inspect every file, delete what you don't need, and fix namespaces before running tests. It will prompt before overwriting existing files — prefer generating individual pieces (make:repository, make:presenter, make:validator) once a project is off the ground.

Gotchas and Tips

  • Namespace mismatch. The Composer name is hocvt/l5-repository, but all classes are Prettus\.... IDE autocomplete and Stack Overflow answers referencing andersao/l5-repository still apply — the fork tracks the original API.
  • parent::boot() is mandatory. Overriding boot() without calling the parent silently skips criteria registration, event wiring, and reset behavior.
  • Criteria persist between calls. Pushing a criterion inside one controller method affects subsequent calls on the same injected instance (Laravel resolves it per-request by default, but long-running workers and Octane will leak). Always resetCriteria() in jobs/queues.
  • with() and scopeQuery() also persist. This is the most common source of "why does this query have stale constraints" bugs. Prefer local variables + resetModel(), or keep repos stateless by always calling resetModel() at the start of a public method.
  • find() throws ModelNotFoundException. Use findWhere([...])->first() or catch it in the exception handler. Do not rely on null returns.
  • getFieldsSearchable() is a whitelist. If search returns everything or nothing, this is the first thing to check. Wildcards (name vs name:*) follow the original package semantics — read the source, not blog posts.
  • Cache + file driver = no tags. The cache repository relies on tags for invalidation; the file and database cache stores don't support them. Use Redis/Memcached in any environment where caching is enabled, or you'll chase phantom stale data.
  • Cache is not transactional. Wrapping repository calls in DB::transaction() does not roll back cache. Invalidate explicitly after commits.
  • Presenter surprises. Once presenter() is set, create()/update() may return presenter-transformed data instead of the model. Use skipPresenter() inside services and tests that assert on raw attributes.
  • Pagination + presenter. paginate() returns a LengthAwarePaginator; the Fractal presenter wraps the underlying collection. Assertions against $result->items() may need $result->toArray()['data'] instead.
  • make:entity migrations are stubs. They generate plausible columns from the entity name only — always edit before migrate.
  • Version pinning. This fork's API surface is stable but its Laravel compatibility matrix lags. Before upgrading Laravel or PHP majors, run the repository test suite (or a smoke test hitting each repository method) — the package's CI is not a guarantee for your version combination.
  • Don't turn repositories into services. Keep them about data access + criteria. Business rules belong in a service or action class that composes one or more repositories. This is what makes the abstraction worth the indirection.
  • Testing. Repositories bind well to an in-memory SQLite connection (:memory: + RefreshDatabase). For unit tests of consumers, mock the interface — the concrete BaseRepository is hard to fake because of its fluent state.
  • Extension point: prefer overriding model(), presenter(), validator(), cacheKey(), and boot() over overriding all(), find(), etc. The fluent methods are chained internally; overriding them tends to break criteria and caching.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
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