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

Database Laravel Package

nodes/database

Nodes Database is a Laravel package from Nodes that simplifies working with application data, providing a clean layer over schemas, migrations, and queries so you can build database-backed features with less boilerplate while staying close to Laravel's conventions and existing tooling.

View on GitHub
Deep Wiki
Context7

Getting Started

nodes/database is a small utility package that ships base classes for the repository pattern on top of Eloquent. Before you install anything, note the reality check: it is archived, last released in February 2020, and has essentially no downstream users. That means it was built against Laravel 5.x/6.x-era APIs. It will still install on modern Laravel in many cases, but expect to either pin an older framework or fork and patch it.

Minimal steps:

composer require nodes/database

If the package registers a service provider via discovery, it will be picked up automatically; on older Laravel versions you may need to add Nodes\Database\DatabaseServiceProvider::class to config/app.php. Check for a config/nodes.php (or similar) and publish it if present:

php artisan vendor:publish --provider="Nodes\\Database\\DatabaseServiceProvider"

Where to look first: the src/ directory, specifically the base model and base repository classes, plus the README. There is no meaningful documentation ecosystem around this package, so the source is the documentation. Read the base repository's method list before writing anything — that tells you what you get for free (typically all, find, create, update, delete, paginate, and a query-builder passthrough) and what you'll need to add yourself.

First use case: take one existing Eloquent model that has accumulated query logic in a controller, and move that logic behind a repository. A typical first pass looks like:

namespace App\Repositories;

use Nodes\Database\Repository; // verify exact FQCN against src/
use App\Models\Order;

class OrderRepository extends Repository
{
    public function __construct(Order $model)
    {
        parent::__construct($model);
    }

    public function openForCustomer(int $customerId)
    {
        return $this->model->where('customer_id', $customerId)
            ->where('status', 'open')
            ->latest()
            ->get();
    }
}

Bind it in a service provider and inject the repository (not the model) into your controller. That single move is the package's entire value proposition — everything else is convention.

Implementation Patterns

Repository-per-aggregate, interface-per-repository. The pattern that ages best is binding interfaces in a dedicated provider so tests can swap in fakes:

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

Keep the concrete class extending the package base; keep the interface hand-written. Do not try to put the package's base class in the interface signature — it leaks the dependency everywhere.

Thin repositories, fat models is fine; the inverse is also fine. The package's base classes are convenience, not architecture. The failure mode is building a repository for every table and ending up with UserRepository::findByEmail() wrapping User::where('email', $x)->first() — pure indirection with no payoff. Reserve repositories for aggregates with real query complexity: dashboards, search endpoints, reporting, anything with joins or conditional filters.

Query filter objects on top of the base repository. A workflow that scales well is passing a filter DTO into repository methods instead of exploding the method signature:

class OrderRepository extends Repository
{
    public function search(OrderFilter $filter)
    {
        return $this->model->newQuery()
            ->when($filter->status, fn ($q, $s) => $q->where('status', $s))
            ->when($filter->from,   fn ($q, $d) => $q->whereDate('created_at', '>=', $d))
            ->when($filter->customerId, fn ($q, $id) => $q->where('customer_id', $id))
            ->paginate($filter->perPage ?? 25);
    }
}

Transactions belong in the service layer, not the repository. The base repository likely gives you raw model access; resist the urge to open transactions inside repository methods. Wrap multi-repository operations in a service or action class using DB::transaction(...), calling repositories as collaborators. This keeps the repository reusable inside larger units of work.

Eager loading as an explicit contract. Have repository methods accept array $with = [] and apply it, rather than hiding with() calls deep in the method body. Callers then control N+1 behaviour from the controller/API resource layer:

public function findWithRelations(int $id, array $with = []): ?Model
{
    return $this->model->newQuery()->with($with)->find($id);
}

Integration tips for Laravel's ecosystem:

  • Register repositories as singletons only if they are stateless — most should be bind, not singleton, since they hold a model instance.
  • Laravel's route model binding can stay intact; you can accept an Order in the controller for simple reads and still use the repository for complex writes.
  • For APIs, return Eloquent models/collections from the repository and let API Resources handle serialization. Don't have the repository return arrays — you lose lazy loading, pagination metadata, and $model->wasRecentlyCreated.
  • Queue jobs and console commands can resolve repositories from the container the same way controllers do.

Testing. Point your test suite at the interface binding. With SQLite in-memory and RefreshDatabase, repository tests are just integration tests over real queries — which is where most repository bugs live anyway. For unit-level tests of services that consume repositories, bind a mock of the interface.

Gotchas and Tips

The archive status is the headline gotcha. Zero dependents and a 2020 release mean no security patches, no Laravel 9/10/11 compatibility work, and no fixes for PHP 8.x deprecations. Before adopting: run composer require in a scratch project on your target Laravel version and see if it resolves at all. If it does, immediately composer show nodes/database and read the actual source in vendor/ — the signatures in the repo are the truth, and any tutorial or guess about method names is likely wrong.

Fork if you're going to depend on it. With 3 stars and an archived repo, vcs-forking into your own GitHub org is cheap insurance. Pin to your fork with a specific commit, and you retain the ability to patch PHP 8 deprecations (dynamic properties on base models are a likely offender).

Configuration quirks. Older Laravel packages often rely on config/app.php provider registration and a config/ file that isn't publish-tagged with a namespaced key. If vendor:publish silently publishes nothing, check whether the service provider's boot() actually calls publishes() — in archived packages this is frequently missing or uses the old two-argument signature.

Eloquent leakage. The base repository almost certainly exposes its model instance (via a public/protected $model). Any code that reaches in and does $repo->model->where(...) from a controller has defeated the abstraction. Enforce the boundary with an interface and code review; otherwise the repository becomes a thin, unmaintained layer over Eloquent with extra indirection.

Watch for mass-assignment assumptions. Base repository create()/update() implementations often call $this->model->fill($data)->save() or ->create($data) with no filtering. If your model doesn't have $fillable set properly, you may silently expose attributes to mass assignment. Audit $fillable/$guarded on every model you route through the package.

Debugging. Because queries flow through the package's methods, DB::listen() and Laravel Telescope's query tab are your fastest tools — you'll see the real SQL and can catch missing where clauses or accidental select * on wide tables. Add a temporary ->dd() on $this->model->newQuery()->toSql() when a repository method returns nothing.

Extension points. The reliable way to customise is to subclass the base repository and override methods. Global changes (adding a default orderBy, soft-delete handling, caching) are better done in one intermediate BaseRepository of your own that all your repositories extend — that gives you a single file to patch when the upstream package inevitably breaks, and insulates the rest of your codebase from the dependency.

Honest recommendation. The package is a thin convenience wrapper. If you're starting fresh, Laravel's built-in Eloquent scopes, query builder macros, and action classes cover 90% of what this offers with zero dependency risk. Adopt nodes/database only where it already exists in a codebase you're maintaining — and in that case, plan a gradual extraction rather than expanding its footprint.

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/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
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