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 Union Paginator Laravel Package

austinw/laravel-union-paginator

Laravel Union Paginator lets you paginate the combined results of multiple Eloquent queries as a single dataset. Merge different models or filtered queries with UNION, keep consistent ordering and page sizes, and return a standard Laravel paginator for Blade or API output.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Roadmap: unify heterogeneous data into one paginated feed. This package powers a single ordered, paginated result set across multiple Eloquent models (e.g. an activity feed mixing Posts, Comments, and Transactions), which is the backbone of dashboards, notifications, search results, and admin audit views. Whether to build that feed, and how much of the roadmap depends on it, is directly informed here.
  • Build vs. buy (build vs. adopt). Rolling your own union paginator is deceptively expensive: stable ordering with tie-breakers, cursor pagination across heterogeneous rows, transformer support, and multi-driver SQL correctness. v3.0.0's CI matrix (SQLite, MySQL 8.4, Postgres 17; PHP 8.2–8.5) and PHPStan level 8 mean the maintenance burden is externalized — a strong signal for adoption over in-house code.
  • Performance trade-offs as product decisions. simplePaginate() (no COUNT over the union) and cursorPaginate() (cursors from raw union rows, compatible with transformers and skipped models) let you choose between accurate page counts and feed latency — a user-facing decision about "Page 1 of N" vs. infinite scroll.
  • Data consistency and UX correctness. type/id tie-breakers guarantee stable pages (no duplicate or missing rows between page loads), and skipped/unloadable models are dropped rather than surfacing as null — a decision about whether feeds can tolerate dangling references.
  • Migration scoping. v3.0.0 is a breaking release gated on Laravel 12/13. That constrains upgrade sequencing for any product still on Laravel 10/11 and should be reflected in platform-upgrade planning.
  • Use cases to greenlight: cross-model activity feeds, unified search, moderation queues, multi-source admin tables, and any read path where "one list, many tables" is the requirement.

When to Consider This Package

Adopt when:

  • You need a single paginated list spanning multiple Eloquent models and want paginate(), simplePaginate(), or cursorPaginate() semantics rather than hand-rolled UNION queries plus manual LengthAwarePaginator assembly.
  • You are on Laravel 12 or 13 (hard requirement in v3.0.0) and PHP 8.2+.
  • Your models share a connection — models across different connections throw in v3, so cross-database unions are out of scope.
  • You can express per-model filtering through applyScope() rather than Eloquent-only constraints. whereHas(), eager-loading-style constraints, and similar Eloquent methods are not forwarded and will throw; only query-builder where*/orderBy* methods are.
  • You need stable pagination (the new type/id tie-breakers) or cursor pagination that survives transformers and skipped rows — historically the hard part of union pagination.
  • You are comfortable with the v3 key contract: the key is selected as id, setSelectedColumns() must not include type (appended last), and models with non-id keys or CREATED_AT = null now load correctly.

Look elsewhere when:

  • You are on Laravel 11 or earlier, or cannot absorb a breaking migration (see MIGRATION.md; removed service provider, public $unionQuery/$scopes/$transformers, prepareUnionQuery(), hasScope(), getScopesFor()).
  • You need Eloquent relationship filtering (whereHas, withCount constraints) inside the union — this is an explicit non-goal; you must refactor to applyScope().
  • Your models span multiple database connections.
  • Your data lives in a search engine (Elasticsearch, Meilisearch, Typesense) or a single denormalized table — a union paginator is the wrong abstraction.
  • You need mutable, order-sensitive filtering applied per model at different stages; v3 applies where*/orderBy* to every model at paginate time in any order, which is a deliberate simplification you must accept.
  • You require exact totals on large unions — simplePaginate() deliberately skips COUNT, and paginate()'s COUNT over a derived table can be costly.

How to Pitch It (Stakeholders)

For executives: This release turns a fragile, in-house pattern into a maintained, production-grade capability. It lets us ship cross-model feeds — activity streams, unified search, moderation queues — as a single paginated list, without each team rebuilding union logic. The v3 release adds cursor pagination and guaranteed stable page ordering, which directly reduces "missing/duplicated row" bugs users notice, and it is CI-tested against three databases and four PHP versions, lowering our operational risk. The cost is a breaking upgrade tied to Laravel 12/13, so it should be sequenced with our framework upgrade rather than treated as an isolated dependency bump.

For engineering: v3.0.0 is a rewrite of the filtering model. where*/orderBy* on the paginator now run against a derived table over the union and apply to every model at paginate time; Eloquent-only methods like whereHas() now throw — port those to applyScope(). The key is fixed to id, type must not appear in setSelectedColumns() (it is appended last), unregistered model types and cross-connection models throw, and unloadable rows are skipped instead of returned as null. Public $unionQuery/$scopes/$transformers, prepareUnionQuery(), hasScope(), and getScopesFor() are gone — audit for subclassing or reflection before upgrading. In exchange we get simplePaginate() and cursorPaginate() (cursor values derived from raw union rows, so transformers and skipped models behave), type/id tie-breakers for stable pages, toBase(), when()/unless(), correct handling of non-id keys and timestamp-less models (including Postgres), and type as a bound parameter (fixes MySQL NO_BACKSLASH_ESCAPES). Quality bar: PHPStan level 8, CI on SQLite/MySQL 8.4/Postgres 17, PHP 8.2–8.5.

Recommendation: Adopt on Laravel 12/13 projects with a cross-model feed requirement; budget the v2→v3 migration (MIGRATION.md) as real work, primarily around scope refactors and removal of public extension points.

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