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 Upsert Laravel Package

staudenmeir/laravel-upsert

Extends Laravel's upsert capabilities with support for custom unique indexes, ignoring duplicate inserts, and bulk updates across MySQL, PostgreSQL, SQLite, and SQL Server. Adds insertIgnore/insertOrIgnore helpers for dependable insert-or-update queries without extra boilerplate.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit: staudenmeir/laravel-upsert is a thin, non-invasive extension to Laravel's query builder and Eloquent grammar layer. It adds upsert(), insertIgnore(), and insertGetId() with ignore support, backporting native behavior to older Laravel lines and extending it with custom uniqueBy conflict handling across MySQL, PostgreSQL, SQLite, and SQL Server. There is no service provider with runtime side effects worth modeling, no schema change, no config publishing, and no daemon. The fit is strongest in idempotent, bulk-write workloads — CSV imports, nightly syncs, third-party reconciliation, entitlement/config refresh, webhook replay dedup, and catalog updates — where collapsing read-then-write logic into one statement is a direct product win. The fit is weakest where the team is already on Laravel 8.10+, because native upsert()/insertOrIgnore() already exists; in that case the correct framing is a redundancy decision, not an adoption decision, and the TPM's first job is to classify the request as "capability gap" versus "developer preference."

Integration feasibility: High mechanically (Composer require, auto-discovery, no migrations), but the real feasibility question is behavioral. Replacing select-then-insert-or-update with a single upsert changes concurrency semantics, error semantics, affected-row counts, trigger/audit behavior, and CDC/outbox timing. Those differences are invisible in happy-path tests and visible in production under load. Feasibility must be signed off by whoever owns the affected tables — not only the feature team.

Technical risk: Moderate, driven mostly by lifecycle and driver semantics rather than code quality. Key risks:

  • Lifecycle: last release 2022-01-14, effectively dormant, and only cites PHP 8.0 support. For a package that compiles SQL against four evolving engines and a fast-moving framework, dormancy is a compounding risk, not a static one.
  • Redundancy: overlapping native functionality forks the code path (package-emitted SQL vs. framework-emitted SQL), adding ongoing cognitive and upgrade tax.
  • Driver semantics: INSERT ... ON CONFLICT, ON DUPLICATE KEY UPDATE, and MERGE are not equivalent. SQL Server MERGE has documented concurrency hazards (duplicate-key violations under parallel load); MySQL INSERT IGNORE silently downgrades all errors — including truncation and FK violations — to warnings; MySQL 8.0.20+ deprecated the VALUES(col) reference form that older upsert implementations emit. Any of these is a potential data-integrity incident, not just a bug ticket.
  • Signal weakness: 315 stars and 0 listed dependents indicate low industry validation. Verify adoption against Packagist download trends rather than GitHub metadata.

Key questions:

  • Which Laravel version is in production, and does native upsert()/insertOrIgnore() already cover the requirement, including custom conflict targets and per-column update lists?
  • Which database engines and versions are in scope, and are all of them exercised in CI, or only the primary one?
  • Does the generated SQL still behave correctly on MySQL 8.0.20+ and on the team's SQL Server version?
  • What is the accepted policy for silent-error suppression in INSERT IGNORE paths, and is there compensating validation?
  • Is there a named internal owner and an exit plan (migrate to native) if upstream stays dormant?
  • What are the bind-parameter and batch-size ceilings per driver, and who owns the batching helper?
  • Has the maintainer been responsive historically, and is a fork/vendoring contingency acceptable?

Integration Approach

Stack fit: Best fit is an older Laravel application (roughly 5.5–8.9) that needs bulk upsert or insertIgnore() where the framework does not yet provide it, or a team that specifically needs the package's richer conflict/update-value signatures on a driver where native support is thin or missing (notably SQL Server in some versions). For Laravel 8.10+ on MySQL or PostgreSQL, the default recommendation is native framework methods first, with this package adopted only for a documented gap.

Migration path: Inventory existing write paths that do select-then-insert-or-update, or catch-duplicate-key-then-update. Convert one table and one job at a time behind an existing repository/service boundary — never inline in controllers. Keep the legacy path available behind a feature flag until the new path has completed at least one full data cycle (an import run, a sync window, or a nightly job) under production-like volume and concurrency. No schema migration is normally required, which keeps rollback cheap; preserve that property by avoiding package-specific column or table conventions.

Compatibility: Pin the package explicitly in composer.json and verify constraints against the exact PHP and Laravel minor in production (PHP 8.0 was the last documented validation; 8.1/8.2/8.3 is presumed, not proven). Confirm there is a real unique index on the conflict target — without it, PostgreSQL raises no unique or exclusion constraint matching the ON CONFLICT specification and the whole feature silently breaks. Run the CI matrix against every driver used in production, not just the developer's local engine. Check for conflicts with any other package that extends the query builder or overrides grammar classes — that is the realistic breakage vector for this kind of extension.

Sequencing: (1) Confirm the requirement cannot be met natively. (2) Document the specific gap that justifies the dependency. (3) Pin in a branch. (4) Add driver-matrix tests for the exact statements being used, including the unique-key prerequisite. (5) Convert one write path behind the service boundary. (6) Validate in staging with production-like data volume and concurrency. (7) Canary by job or tenant. (8) Retire the legacy path. (9) Record the decision in the architecture log with an explicit "replace with native when X" trigger. Keep framework/runtime upgrades and package adoption as separate workstreams so a failed adoption never blocks a PHP upgrade.

Operational Impact

Maintenance: Low ongoing effort but non-zero ownership. Lockfile discipline, monitoring the repository for releases or archival, and re-evaluating the dependency at every Laravel major upgrade — that is the moment a dormant query-builder extension becomes a blocker. Budget a small annual review rather than assuming "it just works."

Support: Community-only, single-maintainer, no commercial backing, no SLA. Name an internal owner who can read the generated SQL and, if needed, fork and patch. The realistic contingency is vendoring a fork; MIT licensing permits it, so confirm the team is comfortable maintaining a few hundred lines of grammar code in the worst case.

Scaling: Generally positive — collapsing N round trips into one statement cuts latency and connection pressure on bulk jobs. Watch three ceilings: driver bind-parameter limits (batch size must be computed from column count and driver, not hardcoded); lock contention when many workers upsert overlapping key ranges (order batches by key to reduce deadlocks); and replication lag where downstream readers still expect old read-after-write timing.

Failure modes: Each needs a named detection signal, not just a rollback note.

  • Silent data loss or truncation via INSERT IGNORE on MySQL.
  • Duplicate-key errors under concurrency on SQL Server MERGE.
  • Deadlocks between concurrent bulk upserts on overlapping key sets.
  • Behavior drift after a database minor-version upgrade (e.g., MySQL VALUES() deprecation).
  • Framework upgrade breaking builder/grammar extension points.
  • Subtler: audit triggers, updated_at handling, and CDC/outbox consumers behaving differently when many rows are touched in a single statement instead of individually.

Ramp-up: Low for any Laravel developer — the API reads like the framework's own. Moderate for the operational side, because most engineers will not intuitively reason about MERGE concurrency or INSERT IGNORE error suppression. A single internal page covering (a) when to use the package versus native, (b) batch-size guidance per driver, (c) the accepted error-suppression policy with compensating validation, and (d) how to verify a bulk upsert actually landed the expected rows will prevent the majority of foreseeable incidents.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core