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

Duplicate Toolkit Laravel Package

gabrielesbaiz/duplicate-toolkit

Laravel package for duplicating Eloquent models and their relationships. Clone records with customizable column overrides, relationship handling, and events via a simple API, making it easy to create reusable copies of templates, content, or configuration records.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Ship “Duplicate” as a cross-cutting capability, not a one-off feature. This package targets a recurring Laravel problem: cloning an Eloquent model plus its relations without hand-writing relation-graph logic per model. That unlocks duplicate actions for quotes, invoices, projects, orders, templates, onboarding configurations, CMS pages, and workflow definitions.
  • Build vs. buy: treat this as a candidate buy/borrow accelerator for generic duplication mechanics. Keep domain-specific copy rules (pricing recalculation, ownership, status transitions, audit requirements) in-house. The decision is not “adopt a platform” but “avoid rebuilding a generic relation-copy utility.” 2.0 strengthens the “borrow” case: it is a genuine rewrite with its own documentation, upgrade guide, and test suite rather than a lightly-maintained fork.
  • Roadmap leverage: a single duplication primitive can support multiple roadmap themes — “save as template,” “clone tenant setup,” “duplicate campaign/product variant,” recurring-order creation, sandbox/demo data generation, and internal admin productivity.
  • Per-relation product semantics are now expressible. 2.0 adds three strategies per relation — copy, reference, skip — settable from the model, from the call site, or via dot notation several levels down. That directly encodes product rules like “duplicate the invoice but reference (not copy) the customer,” or “copy line items but skip audit notes.” Previously this required custom code; now it is configuration, which lowers the cost of refining duplication behavior after launch.
  • Predictable cost is now a product feature. Depth is configurable with cycle detection, max_depth defaults to 1 (matching 1.x behavior, deliberately conservative), and preview() / dry run show the row cost before committing. A PM can scope “duplicate this account” without fear of silently exploding a 43-row copy into thousands — an operational risk that previously argued for restricting the feature to internal tools.
  • Async and large-copy UX: queued and batchable jobs plus four events mean duplication can become a background operation with progress/notification, rather than a request-blocking action. That widens viable use cases to larger relation graphs and bulk admin operations.
  • Downstream traceability: an old-key → new-key map for every created record, plus a provenance column, supports analytics (“how many copies came from templates?”), support tooling, and linking follow-on records without matching by name. Worth designing into the feature spec early.
  • Architecture decision: still wrap the package behind a thin internal DuplicationService or interface. 2.0 is a full rewrite that shares no code with 1.x — a concrete demonstration that the package’s internals can move. The wrapper is what made (and makes) that survivable.
  • Upgrade cost is a real line item. 2.0 is breaking: Traits\HasDuplicates → Concerns\HasDuplicates, Options\DuplicateOptions → DuplicateOptions, saveAsDuplicate() → duplicate(), getDuplicateOptions() → duplicateOptions(), disableDeepDuplication() → shallow(), saveQuietly() → quietly(), and DuplicateOptions is now immutable and fully typed with __get() removed. Old names still work but are deprecated. A migration command exists, but two cases need human review — notably, if a request duplicated two different model classes under 1.x, treat the result as suspect (1.x leaked relations between model classes via an unreset static cache). Any product that already shipped 1.x duplication should budget a verification pass, not just a dependency bump.
  • Requirements gate: PHP 8.3+ and Laravel 12 or 13. This is a hard filter on the portfolio — apps on Laravel 10/11 or PHP 8.1/8.2 cannot adopt 2.x without a platform upgrade, which changes the calculus from “cheap accelerator” to “coupled to an upgrade initiative.”
  • Risk-adjusted product call: the risk profile improved materially — a real service provider, PHPStan at max with no baseline or ignores, Pint/Rector, 105 tests against a real Testbench app covering every relation type, soft deletes, UUID keys and cycles, and a CI matrix across PHP 8.3/8.4 × Laravel 12/13 × prefer-lowest/prefer-stable. It is no longer “unvalidated code with a test suite that registers a nonexistent class.” It is still a low-adoption package, so keep it for internal tools, admin workflows, MVPs, and secondary “duplicate” conveniences rather than the foundation of regulated or high-volume core data flows — but the gap between those two tiers has narrowed.

When to Consider This Package

Consider it when:

  • The product is a Laravel 12/13 application on PHP 8.3+ using Eloquent, and duplication is a repeated but non-core need across several models. (Older stacks: this is a blocker, not a caveat.)
  • The team wants to avoid bespoke clone logic for common relation types and values the newly added coverage: morphToMany no longer fatals, media-library rows are handled by a dedicated pass rather than the relation walk, and unique-column suffixing is resolved in a single query excluding the source row.
  • The feature is an internal admin action, template workflow, onboarding clone, demo-data generator, or “save as new” convenience where occasional edge-case gaps are tolerable.
  • The product needs per-relation control (copy vs. reference vs. skip) or post-copy renaming — 2.0 applies attribute overrides (suffix, prefix, replace, withAttributes, mutate) before insert, so a rename is one write and does not fire the events quietly() suppressed.
  • Engineering wants to de-risk the evaluation cheaply: the relations Artisan command draws a model’s relation tree, preview()/dry run reports and rolls back, and duplicate runs interactively. The spike is now hours, not days — use it to test two or three real models before committing.
  • You can pin the version, review the source, and wrap it behind an internal abstraction so the product is not directly coupled to its API. 2.0’s renames are the proof this matters.
  • You are already on 1.x and duplication is low-risk: run php artisan duplicate-toolkit:upgrade, then manually verify any request that duplicated two different model classes. If 1.x duplication touched important data, treat those copies as suspect until checked.
  • The team accepts that low stars/dependents and a single-maintainer profile remain maturity risks. The engineering hygiene signals are now strong; the operational history is still short, and a rewrite means 2.x has effectively no production track record yet.

Look elsewhere or build in-house when:

  • Duplication is a core differentiator or carries complex domain rules per model — pricing, permissions, tenancy boundaries, approvals, or compliance.
  • You need audit trails, transactional guarantees, idempotency, or SLA-backed support. Events and a provenance column help, but they are not an audit system.
  • The stack is below PHP 8.3 or Laravel 12, or a platform upgrade is not funded.
  • Relation graphs are highly complex in ways the package does not cover, or you depend on custom pivot/versioning behavior that a single spike cannot validate.
  • The team cannot afford an evaluation spike or lacks test coverage to validate duplication correctness — in that case, a small in-house service with explicit rules is safer.
  • You cannot absorb a breaking-change migration — 2.0 renamed public API surface and changed the max_depth default; if you need long-term API stability guarantees, pin 1.x behavior deliberately or wrap it and own the interface.

How to Pitch It (Stakeholders)

For executives: This is a low-cost, MIT-licensed accelerator for a common product capability — duplicating records and their related data. It can help ship “clone,” “save as template,” and tenant-onboarding features faster across multiple parts of the product. The 2.0 release is a full rewrite with real engineering discipline behind it: static analysis at maximum strictness, 105 automated tests, a CI matrix across supported PHP and Laravel versions, published documentation, and an upgrade guide. It also adds the things that make the capability safe to put in front of users: per-relation copy/reference/skip rules, a dry run that previews how many rows a duplication will create, background job support, and traceability from originals to copies. Two caveats to fund accordingly. First, it requires PHP 8.3+ and Laravel 12/13, so it only applies to apps on a current platform. Second, adoption is still near zero, so there is no production track record — and 2.0 is a rewrite, meaning 1.x experience does not transfer. Recommendation: approve the evaluation spike (now genuinely cheap) plus a thin internal wrapper, ship on low-risk workflows first, and treat any upgrade of existing 1.x duplication as a small migration project with data verification, not a version bump.

For engineering: Treat 2.0 as a new dependency, not an upgrade — it shares no code with 1.x. Read UPGRADE.md first: a 1.x app will still boot after updating, and php artisan duplicate-toolkit:upgrade rewrites most call sites, but two cases need a human. Check them explicitly, especially any request that duplicated two different model classes under 1.x (the static relation cache leaked between model classes; those results are suspect). API renames: Traits\HasDuplicates → Concerns\HasDuplicates, Options\DuplicateOptions → DuplicateOptions, saveAsDuplicate() → duplicate(), getDuplicateOptions() → duplicateOptions(), disableDeepDuplication() → shallow(), saveQuietly() → quietly(). Old names still resolve but are deprecated, so do not let them linger. DuplicateOptions is now immutable and fully typed, and the __get() accessor is gone — code that read options dynamically will break loudly, which is preferable to silent drift.

Operationally, max_depth now defaults to 1, matching 1.x behavior. This is the single most important thing to understand before enabling deeper recursion: a previously “43-row” copy can become thousands if you raise depth on a real schema. Use preview() / the dry run to measure cost on a staging dataset before shipping any deep-copy configuration. Prefer shallow() explicitly at call sites where intent is ambiguous. Relation strategies (copy/reference/skip) should be declared on the model where they are domain truth, and overridden at the call site only for genuinely contextual cases; dot notation is available for multi-level exceptions but should be used sparingly, since it makes the copy graph harder to reason about. Use quietly() deliberately when you do not want observer side effects, and apply attribute overrides (suffix, prefix, replace, withAttributes, mutate) before insert so renames are one write and do not trigger events you just suppressed. For larger graphs, move duplication to the queued/batchable job and consume the four events for progress and notification rather than blocking a request.

Finally, wrap the package behind an internal service or interface from day one. 2.0 proves the internals can be replaced wholesale; the wrapper is what makes that a refactor instead of a regression. Pin the version, keep the dependency out of domain models where practical, and add your own tests around the duplication rules that matter to the business — the package’s 105 tests validate the toolkit, not your product’s copy semantics.

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.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log