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

Postgresql Distinct On Laravel Package

datajoe/postgresql-distinct-on

Adds PostgreSQL DISTINCT ON support to Laravel's query builder and Eloquent, so you can fetch the first row per group with a clean, chainable API. Ideal for latest-per-user or per-category queries without raw SQL or subqueries.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package sits on the query-builder seam of Laravel — extending Illuminate\Database\Query\Builder and/or PostgresGrammar to emit SELECT DISTINCT ON (cols) .... That is a low-coupling insertion point: no service provider bootstrap cost, no schema changes, no runtime service container resolution in the hot path. It only applies to applications whose primary datastore is PostgreSQL, and only the read path. Fit is therefore narrow but clean — it does not perturb the app architecture, but it also does not compound: the capability it adds is roughly one selectRaw() call.

Integration feasibility. High in principle, low in practice due to provenance. The repository lives on Bitbucket, reports no stars, no dependents, and no score. There is no evidence in the supplied metadata of Packagist publication, tagged releases, CI, or a license file. Composer can consume it via a vcs repository and a pinned commit, which is technically sufficient but operationally fragile. The claimed support matrix — Laravel 5 through 8 — is the decisive finding: every version it supports is end-of-life. Laravel 9, 10, 11, and 12 are outside the declared range. Adoption today means adopting an unmaintained artifact for a framework generation it was never tested against.

Technical risk.

  • Correctness risk is low; value risk is high. DISTINCT ON is a well-understood PostgreSQL extension with stable semantics. The wrapper itself is thin. The question is not "will it work" but "is it worth a dependency."
  • Injection surface. Any builder macro that accepts a column list and interpolates it into SQL without routing through Grammar::wrap() / wrapSegments() is an injection vector if the column names ever derive from request input. This must be verified by reading the source before any pilot.
  • Grammar override collisions. If the package swaps the connection's query grammar rather than registering macros, it will conflict with any other package doing the same (common with tenant, audit, or soft-delete extensions). Macro registration is the safer mechanism and should be preferred if we reimplement.
  • Aggregate semantics. DISTINCT ON interacts badly with pagination: Laravel's paginator issues a count(*) over the same builder. Count queries over a DISTINCT ON select require a subquery wrapper, and a naive implementation will produce wrong totals or invalid SQL. This is the most likely production defect class.
  • Cross-engine test matrix. Any test suite backed by SQLite (Laravel's default) or MySQL will fail on generated SQL. This forces a PostgreSQL-only test path for the affected features.

Key questions.

  1. Is there a tagged release on Packagist, or is this a dev-master/commit pin?
  2. What is the license, and has legal cleared Bitbucket-hosted source?
  3. Does it wrap identifiers through the grammar, or concatenate raw strings?
  4. Grammar swap or builder macro?
  5. Does it correctly handle count(), paginate(), exists(), and union() composed with DISTINCT ON?
  6. Has it been exercised against Laravel 10/11/12 and PHP 8.2/8.3?

Integration Approach

Stack fit. Appropriate only for a Laravel service on PostgreSQL 12+ where "greatest-n-per-group" reads are a recurring access pattern — latest event per entity, most recent price per SKU, current assignment per user. If the codebase already contains scattered selectRaw('DISTINCT ON ...') fragments or correlated whereIn/row_number() workarounds, the fit is real and the consolidation benefit is concrete.

Migration path. Do not take the dependency. Treat the repository as prior art and a reference implementation, and internalize the capability:

  1. Read the source; extract the SQL generation logic and the identifier-wrapping behavior.
  2. Reimplement as a Builder::macro('distinctOn', ...) registered in an application service provider, delegating to $this->grammar->wrap() for the column list and requiring an explicit orderBy alongside it.
  3. Add a guard that throws if the driver is not pgsql, so failures are loud rather than silent.
  4. Vendor the original under a private path repository only if there is a hard requirement to match its exact output for compatibility with existing serialized queries.

This yields the same capability with a ~40-line internal surface, no third-party availability risk, and a clear owner.

Compatibility. Constrain to the internal macro's behavior, not the package's version range. Verify against the actual PostgreSQL major versions in the fleet, since DISTINCT ON planning behavior and index selection have shifted across 12–17. Confirm the Laravel version's compileColumns path — the method's handling of the distinct flag has changed across major versions, and any external package reaching into it is the primary break vector.

Sequencing.

  1. Inventory: grep for distinct on, row_number() over, and correlated whereIn subqueries used for latest-per-group.
  2. Decide per call site: macro vs. selectRaw vs. LATERAL join.
  3. Implement and unit-test the macro against a containerized PostgreSQL.
  4. Migrate low-traffic call sites first; validate query plans with EXPLAIN (ANALYZE, BUFFERS).
  5. Only then refactor high-traffic reads.

Operational Impact

Maintenance. Assume zero upstream velocity. There is no signal of recent commits, issue triage, or security response. Budget for the package as fork-and-own from day one, not as a maintained dependency. The internal macro route eliminates this cost entirely and is the recommended posture.

Support. No vendor, no SLA, no security contact. Any incident affecting a DISTINCT ON read becomes an in-house debugging exercise against unfamiliar third-party code, on a Bitbucket repository that could disappear. Note Bitbucket's history of free-tier policy changes as an availability risk for the source itself — mirror the code internally at adoption time if we proceed.

Scaling. DISTINCT ON forces a sort over the driving set. Performance is entirely contingent on a btree index whose leading columns match the DISTINCT ON expressions and whose trailing columns support the ORDER BY direction. Without it, the planner falls back to a full sort spilling to temp files — a query that looks fine at 10k rows and degrades sharply at 10M. For queries pulling one row per group from a small group set, a LATERAL join is frequently faster. For deep pagination over DISTINCT ON results, expect the count query to be the bottleneck because it cannot use the index in the same way. All of this requires per-query EXPLAIN review, not a blanket adoption.

Failure modes.

  • Silent incorrectness on paginate()/count() — wrong total counts, off-by-page boundaries. Highest severity because it is invisible.
  • Wrong rows returned when ORDER BY is omitted or when the leading ORDER BY terms do not match the DISTINCT ON expressions — PostgreSQL requires this and will error, but a macro that auto-injects ORDER BY incorrectly will silently pick the wrong "first" row.
  • Hard failure in CI on SQLite/MySQL test environments.
  • Breakage on Laravel major upgrade if the package patches grammar internals.
  • SQL injection if column names flow from user input and are not wrapped.

Ramp-up. Effectively zero for a competent Laravel engineer — the concept is a single SQL clause and the wrapper is small. Half a day to read, reimplement, and test. The real cost is not learning the tool; it is the review discipline required to ensure every call site has a supporting index and a validated count path. Budget engineering time for query-plan review, not for onboarding.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin