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

lapaliv/laravel-bulk-upsert

Laravel Bulk Upsert saves collections of Eloquent models with bulk queries while still firing creating, updating, saving, deleting, and restoring events. It auto-aligns uneven fields and returns the inserted rows. Supports MySQL, PostgreSQL, and SQLite.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Build vs. buy for a bulk-write layer. Every team that hits Eloquent write ceilings eventually builds something: a chunking job, a raw insert() helper, a batch of "we'll fire the events manually later" code. This package is a candidate to buy (MIT, ~zero cost) instead of building — the decision is whether a thin third-party dependency beats an internally owned helper we'd have to maintain forever.
  • Feature enablement for ingestion-heavy surfaces. CSV/ERP imports, CRM sync, webhook fan-in, bulk admin edits, nightly reconciliation jobs, marketplace feed ingestion. These features are usually gated on write throughput ("10k rows takes 40 minutes"), not on UI work. This package directly moves that constraint.
  • Roadmap sequencing: performance work vs. new features. If bulk paths are currently a foreach ($rows as $row) { $row->save(); } loop, we're paying N round-trips per batch. Adopting this collapses that to "a few queries per chunk," which can remove a whole performance epic from the roadmap and free the quarter for feature work.
  • Compliance and audit-trail integrity. This is the under-appreciated decision driver. Laravel's native upsert() / insert() do not fire Eloquent events, so any bulk path silently bypasses observers, audit logs, updated_by stamping, derived fields, and cache invalidation. Teams often don't notice until an audit or a "why is this field null" incident. Choosing an events-preserving bulk writer is a correctness/governance decision, not just a perf one.
  • Data-integrity decisions around return values. createAndReturn / upsertAndReturn give back hydrated models with IDs, which determines whether downstream work (child records, search indexing, notifications) can happen in the same job — or whether we need a second read query / extra round-trip architecture.
  • Streaming vs. batch architecture. The chunk() + createOrAccumulate() accumulator pattern lets a queue worker hold constant memory while draining a large feed. That influences whether we size jobs for "load whole file" or "stream rows," which affects worker memory and infra cost.
  • Backfill / migration strategy. Bulk upsert with returned rows is the natural tool for data backfills and re-keying exercises; whether we have it shapes how we scope one-off migration projects.

When to Consider This Package

Adopt-candidate when most of these are true:

  • Write volume is genuinely large — roughly 1k+ rows per operation, or sustained throughput where per-model save() query counts are the measured bottleneck.
  • Our models carry observer/event-driven logic that must run (auditing, slugs, denormalized counters, cache busting, notifications). This is the package's core differentiator.
  • We need the inserted/updated rows back — especially when uniqueness is a composite key, where hand-written "select them back" SQL gets ugly fast.
  • We're on PHP 8.0+, Laravel 8+, MySQL 5.7+/Postgres 9.6+/SQLite 3.32+.
  • Input rows are ragged (missing keys across rows). The auto field-alignment removes a class of bug-prone prep code.
  • We want soft-delete / force-delete and restore in bulk with events, not just create/update.
  • We're comfortable wrapping the dependency behind an internal adapter so it's swappable.

Look elsewhere when:

  • Batches are small (hundreds of rows). Native save() loops or insert() are simpler, and the dependency isn't earning its keep.
  • No events/observers are involved in the write path. Use Laravel's native upsert() / insertOrIgnore and skip the abstraction entirely.
  • Maintenance risk is unacceptable. 15 stars, 0 dependents, and effectively a single maintainer means we'd be one of the only production users. If this path is business-critical and long-lived, treat it as a vendored/forked dependency we accept ownership of — or don't take it.
  • We need strict, documented parity with Eloquent save() semantics (mutators, casts, timestamps, transaction boundaries, model-state consistency). Divergences here are silent correctness bugs, and low adoption means fewer people have found them for us.
  • The problem is actually architectural: cross-system sync belongs in an ETL/CDC tool, not application code.
  • We need multi-table atomicity with strong guarantees — this optimizes per-model writes, not distributed transactions.

Non-negotiable validation before committing: a timeboxed spike (1–2 days) proving our existing observers fire exactly once per model, created_at/updated_at and casts behave, rollback semantics are understood, and query counts and p95 latency drop materially on our real schema.

How to Pitch It (Stakeholders)

To executives: We have a class of features — imports, syncs, bulk edits — that are slow and expensive because we write one database row at a time. Adopting a small MIT-licensed library lets us write those in batches while still running all our existing business logic (audit trails, derived fields, notifications) that the fast path normally skips. Expected outcome: large cuts in ingestion time and database load for the same feature set, and it removes a "make imports faster" project from the roadmap. Cost is effectively zero in license fees; the real investment is a short evaluation spike and a thin internal wrapper so we're not locked in. The one honest caveat: it's a young package with minimal community adoption, so we'd be an early production user — we mitigate that by wrapping it, testing it against our own observer contracts, and keeping a fallback.

To engineering: This gives us Eloquent's event lifecycle (creating/created, saving/saved, deleting/deleted, restoring/restored, plus collection-level *Many events) on top of chunked bulk writes — semantically "a foreach of save(), but a few queries per chunk." It handles ragged field sets without manual alignment, returns hydrated collections, supports soft/force delete and restore in bulk, and exposes BulkRows (original payload + model + unique attributes) so observers can chain related writes — e.g., upsert users, then upsert their comments from the same payload in savedMany. Requirements: PHP 8.0+, Laravel 8+, MySQL 5.7+/Postgres 9.6+/SQLite 3.32+, and the Bulkable trait on participating models. The ask: a spike plus a thin BulkWriter interface so the rest of the codebase depends on our contract, not on Lapaliv\BulkUpsert directly — that keeps this a reversible decision and lets us swap in native upsert() or a fork if the package stalls.

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.
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
noith/settings
kda/laravel-package