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

jetbox/laravel-repository

jetbox/laravel-repository provides a lightweight repository pattern for Laravel, helping separate data access from business logic. Define interfaces, bind implementations, and keep Eloquent queries organized, reusable, and easier to test.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and let Laravel's package discovery register it:

composer require jetbox/laravel-repository

Framework compatibility is explicit as of v0.1.6: Laravel 10 and 11 are supported. That release is a support-matrix bump only — it adds no new API — so if you were already on 0.1.5 with Laravel 10/11, upgrading is low-risk; re-run your repository test suite and move on.

Then check two things before writing any code:

  1. The README and src/ directory — this is a low-traffic package (43 stars, no dependents), so the source is the source of truth. Identify the base repository class, any contracts/interfaces, and whether there are Artisan generators (php artisan list | grep -i repo).
  2. Publishable config/providers — run php artisan vendor:publish --provider="Jetbox\..." if the README mentions it. If there is no config, there is nothing to tune; the package is likely just a thin base class.

Your first use case should be the smallest possible: wrap one model (e.g. User) in a repository, bind its interface in a service provider, and inject it into a controller. That validates the whole loop — autoloading, binding, and return types — before you refactor anything real.

// app/Repositories/UserRepository.php
class UserRepository extends BaseRepository
{
    public function model(): string
    {
        return User::class;
    }
}

// app/Providers/RepositoryServiceProvider.php
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);

Confirm the actual base-class name and required method (model(), setModel(), or a constructor argument) against src/ — these packages differ here and guessing wastes an hour.

Implementation Patterns

One repository per aggregate, not per table. Repositories work best when they represent a domain concept with a clear public API (findActiveByEmail, paginateRecent). If you generate a repository for every Eloquent model, you've added a layer that only forwards calls.

Interface + binding, always. The entire value of the pattern is substitutability. Bind interfaces in a dedicated RepositoryServiceProvider rather than scattering bind() calls in AppServiceProvider. This also makes it obvious which repositories exist.

Inject into actions/services, not controllers. Controllers should call a service or action; the service calls one or more repositories. This keeps transactions and multi-repository orchestration in one place.

class RegisterUser
{
    public function __construct(private UserRepositoryInterface $users) {}

    public function handle(array $data): User
    {
        return DB::transaction(fn () => $this->users->create($data));
    }
}

Keep query concerns in the repository. Eager loading, scopes, and filters belong behind the interface method, not at the call site:

public function paginateWithPosts(int $perPage = 15): LengthAwarePaginator
{
    return $this->model->with('posts')->latest()->paginate($perPage);
}

If the package ships a criteria/filter system, use it for cross-cutting filters (published, by tenant, date range) and keep them as small classes. If it doesn't, plain explicit methods are better than inventing a criteria engine on top.

Testing pattern. Because consumers depend on the interface, unit tests mock it; integration tests hit a real repository against SQLite in-memory. Test the repository itself once with a real database — mocking Eloquent's query builder is not worth it.

Run one repository suite under each supported framework version if your app is mid-upgrade between Laravel 10 and 11. v0.1.6 claims both, but with this package's maturity level, verifying in CI is cheaper than debugging BindingResolutionException in staging.

Gotchas and Tips

  • Don't leak Eloquent through the interface. Methods like ->with(), ->where(), or returning Builder defeat the abstraction and couple callers to the ORM. Return Collection, LengthAwarePaginator, models, or DTOs.
  • Be consistent about return types. A repository that returns Collection from all(), Paginator from paginate(), and Builder from query() confuses every caller. Document it in the interface.
  • N+1 creeps in at the boundary. Callers can no longer chain ->with() if the interface forbids it, so eager-loading decisions must be explicit repository methods (findWithRelations). Plan for that up front.
  • Business logic does not belong here. Validation, events, notifications, and authorization stay in actions/services. The repository does persistence.
  • Version support is now bounded, not unknown. v0.1.6 declares support for Laravel 10 and 11 — treat that as the supported window. There's nothing indicating Laravel 9 or 12 support, so pin the exact version in composer.lock, and if you're on Laravel 12+ check composer.json constraints before adopting. Note the 0.1.x version string itself signals pre-1.0 churn: minor bumps can still move the API surface.
  • Binding type matters. Use bind() for stateless repositories. singleton() is tempting but risks stale model state if the repository caches anything internally.
  • Debugging a resolution failure: php artisan tinker → app(UserRepositoryInterface::class). If that throws, the binding is missing or the provider isn't registered in bootstrap/providers.php (Laravel 11) / config/app.php (Laravel 10 and below).
  • Debugging queries: add DB::listen() temporarily or use Telescope. The repository layer hides where a query originates, so log the caller when performance hunting.
  • Extension points are usually just inheritance. Override base methods in your repository rather than forking the package. If you need to change global behavior (e.g. default pagination size), subclass the base once in your app and extend that everywhere.
  • Read the source before relying on it. With this package's maturity level, expect thin documentation. The base class is probably a few hundred lines; skim it once so you know exactly which conveniences (all, find, create, update, delete, paginate) are actually implemented versus assumed — the v0.1.6 notes confirm support scope, not feature scope.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core