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

scolib/l5-repository

A Laravel package for the repository pattern, adding criteria, presenters, validators, and cache support to keep your application's data layer clean, testable, and framework-agnostic.

View on GitHub
Deep Wiki
Context7

Getting Started

v3.0.0 is a small but meaningful release: it adds a findOrNew finder and raises the minimum framework version to Laravel 5.5. That 5.5 floor is not arbitrary — 5.5 introduced package auto-discovery, so the package no longer needs manual service-provider registration in config/app.php. If you are on 5.4 or below, stay on the 2.x line; there is no compatibility shim.

Minimal steps:

  1. composer require <vendor>/<package> ^3.0 — Composer will refuse on Laravel < 5.5, which is your first signal that the upgrade is blocked by framework version rather than PHP.
  2. On 5.5+ the service provider is discovered automatically. Confirm with php artisan package:discover if you have a cached/discovery-disabled setup (e.g. composer.json with "extra": {"laravel": {"dont-discover": [...]}}).
  3. Look first at how the new finder is exposed — whether it is a trait you add to a model, a base model/Repository class you extend, or a query builder macro registered in the provider's boot(). That single detail determines everything downstream; check the package's README section for the v3 upgrade before writing code.
  4. First use case: an update screen where the same controller action handles "edit existing" and "create new" by ID.
public function edit($id)
{
    $post = Post::findOrNew($id);   // existing row, or a fresh instance with the key set
    return view('posts.edit', compact('post'));
}

The contrast with core Eloquent is the whole point:

  • firstOrNew(['slug' => $slug]) — instantiate by arbitrary attributes.
  • findOrNew($id) — instantiate by primary key, so the "new" branch is already keyed and ready for a form action that posts back to the same URL.

Implementation Patterns

Update-or-create controller actions. This is the primary consumer of findOrNew. Pair it with a single store/update route or a PUT that upserts, and branch on $model->exists rather than on a null check:

$post = Post::findOrNew($request->route('id'));
$post->fill($request->validated());

if (! $post->exists) {
    $post->save();          // only persists when it was a newly minted instance
}

return redirect()->route('posts.edit', $post);

Form scaffolding. Because the returned new instance already carries the key, a shared Blade partial can render route('posts.update', $post) without conditional logic, and hidden id fields come out correct for both branches.

Repository / service layer. If the package exposes the finder through a repository abstraction rather than a model trait, standardise on it so callers never mix find() + new Model by hand. Keep the "not found → new" decision in one place; scattering findOrNew across controllers makes the fallback semantics hard to audit later.

Validation adjacency. findOrNew is a read-plus-construct operation, not a guard. If an ID must exist (e.g. editing someone else's record), you still want findOrFail or route-model binding — use findOrNew only where absence is a legitimate code path.

Testing. Assert on exists and wasRecentlyCreated, not just on attributes, and cover both branches explicitly: an ID that resolves to a row and an ID that does not. A test that only exercises the happy path will not catch the insert-with-explicit-key behaviour described below.

Mixed-version codebases. If you maintain packages or downstream apps spanning 5.5 and 5.6+, pin ^3.0 in the app and add the version constraint to your composer.json require rather than relying on transitive resolution, so a future 4.x bump is a deliberate step.

Gotchas and Tips

findOrNew does not write. The name reads like firstOrCreate but behaves like firstOrNew. Nothing is persisted until you call save(). This is the most common v3 migration mistake.

Saving the "new" branch inserts an explicit primary key. The instance comes back with the key attribute already set. On an auto-incrementing model, save() will issue an INSERT carrying that ID. That is usually fine, but it can collide with existing rows, desynchronise the auto-increment counter on MySQL, and surprises people auditing the table. If the ID is client-supplied, decide deliberately whether you want "create with this ID" or "create and let the DB assign" — the latter means unsetting the key before save.

exists and wasRecentlyCreated remain false after construction, and only flip on save(). Do not branch on wasRecentlyCreated before persisting.

Mass assignment is your responsibility. Only the key is populated on the new branch; fill()/create() semantics and your $fillable/$guarded config still govern everything else. A MassAssignmentException here means your model config, not the package.

Upgrade path from 2.x. Run composer why-not <vendor>/<package> 3.0 before bumping — on older projects the blocker is almost always laravel/framework or a dependency pinning it, not the package itself. Then check whether the service provider was previously registered manually in config/app.php; leaving the duplicate registration alongside auto-discovery is harmless in most cases but produces confusing double-registration when macros or bindings are involved.

Discovery quirks. If the method "doesn't exist" after upgrading, check bootstrap/cache/packages.php and run php artisan package:discover — a stale cache after a deploy is the usual culprit. If the package is macro-based, verify the macro is registered in boot() and not register().

Name collisions. Other packages (and your own code) sometimes define a findOrNew macro on the query builder. If behaviour differs from what you expect, Builder::hasMacro('findOrNew') and Model::hasGlobalMacro(...) will tell you who won.

Extension point. If the finder is trait-based, that trait is the seam for custom behaviour — override it in a base model to add scoping (e.g. tenancy or soft-delete-aware lookup) rather than wrapping every call site. Keep such overrides thin and documented; the package's semantics are the contract and deviating from them silently is how the next developer loses an afternoon.

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
cgv/localized-resource