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

Zrepository Laravel Package

zhaohehe/zrepository

zhaohehe/zrepository is a Laravel repository-pattern package that wraps Eloquent queries behind clean, testable interfaces. It helps organize reusable query logic and data access so controllers and models stay lean and applications remain easier to maintain.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm the shape of the API before writing anything.

composer require zhaohehe/zrepository

The package is small and thinly documented, so the first ten minutes should be spent in vendor/zhaohehe/zrepository/src/ rather than the README. You are looking for three things:

  1. The base repository class (usually something like Zhaohehe\ZRepository\Repository or a BaseRepository).
  2. Any traits it ships (RepositoryTrait, Cacheable, Criteria-style helpers) — these determine whether you extend a class or compose traits.
  3. Whether an artisan generator exists (php artisan list | grep -i repo). Many Laravel repository packages ship a make:repository command; if this one does, use it so namespace and stub conventions match the package.

If the package publishes config:

php artisan vendor:publish --provider="Zhaohehe\ZRepository\RepositoryServiceProvider"

First use case. Don't refactor an existing controller. Pick one read-heavy endpoint, create a repository around its model, and inject it:

namespace App\Repositories;

use App\Models\Order;
use Zhaohehe\ZRepository\Repository; // verify exact FQCN in src/

class OrderRepository extends Repository
{
    public function model(): string
    {
        return Order::class;
    }
}

Then bind and inject:

// AppServiceProvider::register()
$this->app->bind(OrderRepository::class, fn () => new OrderRepository());
public function show(OrderRepository $orders, int $id)
{
    return $orders->find($id);
}

The value proposition is modest but real: it gives you a seam for testing and a single place to hang query scopes, filters, and caching. Treat it as a thin convention layer, not an ORM replacement.

Implementation Patterns

Interface + implementation binding. The pattern only pays off when consumers depend on an interface, not the concrete class. Define App\Contracts\OrderRepositoryInterface, implement it with the package base class, and bind in a service provider. This is what makes swapping the implementation (Eloquent, API client, in-memory fake) possible later.

Custom query methods live on the repository, not in controllers. Keep the base class for CRUD and add domain-specific finders:

class OrderRepository extends Repository
{
    public function model(): string { return Order::class; }

    public function openForCustomer(int $customerId, int $perPage = 25)
    {
        return $this->model
            ->where('customer_id', $customerId)
            ->where('status', 'open')
            ->with(['lines', 'shipment'])
            ->latest()
            ->paginate($perPage);
    }

    public function markShipped(int $id, string $tracking): bool
    {
        return (bool) $this->model->whereKey($id)->update([
            'status'      => 'shipped',
            'tracking_no' => $tracking,
            'shipped_at'  => now(),
        ]);
    }
}

Expose $this->model (or whatever the base class calls its model instance — check src/) only inside the repository. Never return a Builder from a repository method; return collections, paginators, or DTOs. Leaking the builder defeats the abstraction and re-couples callers to Eloquent.

Filter objects instead of method explosion. Rather than findActive(), findActiveByRegion(), findActiveByRegionSorted(), accept a filter object or an array and build the query once:

public function search(OrderFilter $filter)
{
    return $this->model
        ->when($filter->status, fn ($q, $s) => $q->where('status', $s))
        ->when($filter->region, fn ($q, $r) => $q->where('region', $r))
        ->when($filter->from, fn ($q, $d) => $q->whereDate('created_at', '>=', $d))
        ->paginate($filter->perPage ?? 25);
}

If the package ships a criteria/pipe mechanism, use it for cross-cutting concerns (soft-delete scoping, tenant scoping) and keep business filters as explicit methods. Don't put tenant isolation in a magical global criteria — it's the kind of thing that silently disappears during a refactor.

Wrap multi-step writes in transactions at the service layer, not the repository. A repository method should do one thing. Orchestration belongs in a service or action class that calls two repository methods inside DB::transaction().

Testing. With constructor injection and an interface, you can swap in a fake:

$this->app->instance(OrderRepositoryInterface::class, new InMemoryOrderRepository());

For integration tests, hit the real repository against SQLite in-memory and assert on returned collections rather than on generated SQL. If you find yourself asserting SQL strings, you've coupled the test to the package internals.

Gotchas and Tips

Maturity risk is the headline. 88 stars, zero dependents, and a low opportunity score means this is a single-maintainer package with essentially no ecosystem pressure testing it. Before adopting: check the last commit date, whether it supports your Laravel version's constraints in composer.json, and whether the test suite actually runs. The abstraction it provides is ~100 lines of code you could write yourself in an afternoon — the only thing you're buying is convention. If the package looks stale, fork it or inline the base class into App\Repositories\BaseRepository and move on.

Method name collisions. Base repository classes frequently define all(), find(), create(), update(), delete(), paginate() with signatures that differ subtly from Eloquent's (argument order, return types, whether find throws or returns null). Check each one you call. The classic bug is a base update(array $data) that uses $this->model->whereKey($id)->update() under the hood and therefore skips model events, casts, and updated_at handling depending on implementation. If you rely on observers or booted() hooks, verify they fire.

$this->model may be a fresh instance per call or a shared one. If the base class lazily instantiates and caches the model, state can leak between calls in long-running workers (Octane, queues). Reset explicitly if the package exposes a reset method, or construct the query from app($this->model()) each time.

Eager loading is your job. The base find() almost certainly does not eager-load. N+1s will appear the moment you serialize a repository result in a resource. Either add ->with() in every domain method or override the base find in your repository with a $with property default.

Config quirks. If a config file exists, check for a cache or connection key — packages like this sometimes default to caching all()/find() results with an aggressive TTL and no invalidation hooks. That's a silent correctness bug in any write path. Publish the config and audit every key before going to production.

Don't let the repository become a god object. The failure mode of repository packages is a 600-line UserRepository with 40 methods, half of them used once. Split by read/write concern or by aggregate when it grows.

Extension points to prefer: subclass the base class, override the model-resolution method, and add traits for cross-cutting behavior (caching, logging). Avoid editing vendor files — if you need a behavior change in the base class, copy it into your own base and drop the dependency. Given the maintenance profile, keeping the dependency surface small is the real win.

Where to debug first: when something behaves unexpectedly, dd(get_class($this->model)) inside a repository method. Half of all "why isn't my scope applying" issues with packages like this come down to the base class operating on a different model instance or a query builder that was cloned before your when() ran.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
boomerangui/chat