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 Eloquent Foundation Laravel Package

hatchyu/laravel-eloquent-foundation

hatchyu/laravel-eloquent-foundation offers a reusable base layer for Laravel Eloquent models, bundling common traits and helpers that cut boilerplate and keep model behavior consistent across your application.

View on GitHub
Deep Wiki
Context7

Getting Started

Treat this package as a source-readable utility library, not a framework: with 0 stars, 0 dependents, and a first release dated in the future relative to most lockfiles, the composer README is not a substitute for reading src/. Budget 20 minutes to skim every trait before you composer require it into a production app.

  1. Install and pin.

    composer require hatchyu/laravel-eloquent-foundation
    

    Because there is no adoption signal, pin an explicit version in composer.json ("hatchyu/laravel-eloquent-foundation": "1.0.*") rather than floating. Nothing is worse than an unmaintained package silently changing scope semantics on a patch release.

  2. Confirm the namespace and autoload map. Open composer.json inside the package and check autoload.psr-4. Small packages frequently ship a namespace that doesn't match the vendor name (hatchyu vs. the repo owner rajeshmk), which is a smell worth noting but not a blocker.

  3. Look for a service provider. Check for src/*ServiceProvider.php and whether it's registered via Laravel's package discovery (extra.laravel.providers). If the package only ships traits, there may be no provider at all — in which case there's nothing to php artisan vendor:publish and no config file. Don't waste time looking for one.

  4. Inventory what you actually get. List the traits and scopes by filename. Typical contents of a package like this:

    • A BaseModel or HasFoundation trait (UUID/slug bootstrapping, timestamps, casts)
    • Query scopes: scopeActive, scopeOrdered, scopeRecent, scopeWhereLike, scopeSearch
    • Soft-delete or publishing-state helpers (scopePublished, scopeDraft)
    • Cast or attribute helpers (getInitialsAttribute, boolean flags)
  5. First use case — apply it to exactly one model. Pick a low-risk, read-heavy model (a Category, Tag, or lookup table). Add the trait, run your existing test suite, and check nothing breaks. This is your canary.

Minimal first integration:

use Hatchyu\EloquentFoundation\Concerns\HasFoundation; // verify exact FQCN in src/

class Category extends Model
{
    use HasFoundation;

    protected $fillable = ['name', 'slug', 'is_active'];
}

Then exercise one scope in tinker:

Category::query()->active()->ordered()->get();

If that returns the rows you expect, the package's core contract is intact and you can proceed to broader adoption.

Implementation Patterns

Compose traits on a project base model. The cleanest way to use a foundations package is to inherit it once and never touch it again:

abstract class AppModel extends Model
{
    use HasFoundation;
}

Every concrete model extends AppModel. This means the day you decide to drop or fork the package, you edit one file instead of forty.

Chain scopes rather than writing where() inline. The entire value proposition of a scope package is readability and a single source of truth. Prefer:

Post::query()->published()->recent()->paginate(20);

over scattering ->where('status', 'published')->latest() across controllers, API resources, and Blade views. When the definition of "published" changes (a new state, a soft-delete interaction), you change one method.

Wrap scopes in dedicated query objects for complex features. Scopes compose poorly once you need conditional logic. Use them as building blocks inside a PostQuery / FiltersPosts class rather than calling them directly from controllers:

class PostQuery
{
    public function forListing(?string $term): Builder
    {
        return Post::query()
            ->published()
            ->when($term, fn ($q) => $q->search($term))
            ->ordered();
    }
}

Pair the scopes with API resources and pagination. Scopes returning Builder (not Collection) is the expected contract — verify this, because a package that returns ->get() from a scope will break paginate() and eager loading. Always check the return type before chaining.

Test the scopes, not the package. Write a thin test per scope in your own suite:

it('only returns published posts', function () {
    Post::factory()->create(['status' => 'draft']);
    $published = Post::factory()->create(['status' => 'published']);

    expect(Post::query()->published()->pluck('id'))->toBe([$published->id]);
});

These tests are your regression harness if the package is ever abandoned and you have to vendor the trait into app/Models/Concerns.

Fork-friendly escape hatch. If a scope almost-but-not-quite fits, override it in your model rather than bypassing it:

public function scopeRecent(Builder $query): Builder
{
    return $query->orderByDesc('published_at')->orderByDesc('id');
}

Local overrides shadow the trait method and keep call sites unchanged.

Gotchas and Tips

Trait method collisions are the #1 failure mode. If your model already uses SoftDeletes, HasUuids, or a HasFactory variant that defines bootHasX or the same scope name, PHP will fatal with a collision error. Resolution order is class → traits (last-used wins, silently, which is worse than a fatal). Run php -l isn't enough; run the test suite after adding the trait to any model that already composes several traits.

Watch for boot{TraitName} side effects. Foundation traits frequently hook static::creating() or static::saving() to populate slugs, UUIDs, or timestamps. That means Model::create() and factory()->create() can behave differently than ->make(), and insert() bypasses it entirely. If you see unexpected null slugs or missing UUIDs in seeders, this is why — use create(), not insert(), or populate manually.

is_active-style scopes and soft deletes interact badly. A scopeActive that filters on a status column will still return soft-deleted rows unless the model also uses SoftDeletes. And withoutGlobalScopes() on a model using foundation traits may drop scopes you didn't know were global. Inspect the trait for static::addGlobalScope() calls before assuming your queries are unscoped.

Check whether scopes return Builder or void. A scope that mutates the builder without return works in Model::scope() chains but breaks the moment someone does $q->active()->paginate() inside a variable assignment or a subquery. Read the return statements; if any scope lacks return, fix it in your base model override.

No versioning guarantees. With zero dependents and a 0-star repo, assume the maintainer has no downstream consumers to break. Do not use ^1.0 if you can avoid it, and consider copying the handful of traits you actually use into app/Models/Concerns/ with an attribution comment. For a package of this size, vendoring is often cheaper than the dependency — you get full control over edge cases and lose nothing but future updates you probably weren't going to receive.

Config quirks. If a config file does exist, check whether the package merges defaults (mergeConfigFrom) or expects a published file. Un-published config with config('eloquent-foundation.*') returning null inside a trait is a common silent failure — the trait falls back to a hardcoded default and you spend an hour wondering why your setting is ignored.

Debugging checklist when a scope "does nothing":

  1. ->toSql() and ->getBindings() on the builder — confirm the scope was actually invoked (typos in scope names fail silently in some call paths).
  2. class_uses_recursive(YourModel::class) in tinker — confirm the trait is composed.
  3. Model::getGlobalScopes() — check for hidden global scopes.
  4. DB::listen() — verify the emitted SQL in a request test.

Extension points worth knowing: the traits are almost certainly protected-method-light and rely on properties. If you need different default ordering, override the scope method in your model. If you need different slug behaviour, override the boot hook rather than the trait — you can't partially override a boot{Trait} method without redeclaring it, which reintroduces collisions.

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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource