Skip to content

feat: Add withCount() support for relationship counting - #10

Open
tabennett wants to merge 3 commits into
masterfrom
feature/add-with-count
Open

tabennett wants to merge 3 commits into
masterfrom
feature/add-with-count

Conversation

@tabennett

Copy link
Copy Markdown
Contributor

Implements withCount() method to count related records without loading them, providing significant performance improvements when only counts are needed.

Changes:

  • Add withCount() method to Builder with support for constraints and aliasing
  • Implement toCountQuery() interface method across all relationship types
  • Add getBaseQueryWithScopes() helper to BaseRelationship for scope application
  • Update Custom relationship to accept optional count closure parameter
  • Add 23 comprehensive integration tests covering all relationship types
  • Document withCount() in eager-loading.md, query-building.md, and relationship-cheatsheet.md

Features:

  • Count single or multiple relationships in one query
  • Apply constraints to count queries via closures
  • Custom aliases using 'as' syntax
  • Automatic snake_case conversion for count attributes
  • Global scopes (SoftDeletes, etc.) automatically applied
  • Support for HasMany, HasOne, BelongsTo, BelongsToMany relationships
  • Custom relationships can opt-in via count closure

Technical Details:

  • Delegates count logic to relationships via polymorphic toCountQuery() method
  • Wraps constraint closures in Holloway Builder for full query API access
  • Preserves relationship-level scopes during counting
  • Count columns added as subqueries on main query

Tests: All 23 tests passing
Breaking Changes: None (Custom relationships without count closure throw helpful exception)

@tabennett
tabennett force-pushed the feature/add-with-count branch from 4a4b983 to 2e545e5 Compare May 6, 2026 17:34
Implements withCount() method to count related records without loading them,
providing significant performance improvements when only counts are needed.

Changes:
- Add withCount() method to Builder with support for constraints and aliasing
- Implement toCountQuery() interface method across all relationship types
- Add getBaseQueryWithScopes() helper to BaseRelationship for scope application
- Update Custom relationship to accept optional count closure parameter
- Add 23 comprehensive integration tests covering all relationship types
- Document withCount() in eager-loading.md, query-building.md, and relationship-cheatsheet.md

Features:
- Count single or multiple relationships in one query
- Apply constraints to count queries via closures
- Custom aliases using 'as' syntax
- Automatic snake_case conversion for count attributes
- Global scopes (SoftDeletes, etc.) automatically applied
- Support for HasMany, HasOne, BelongsTo, BelongsToMany relationships
- Custom relationships can opt-in via count closure

Technical Details:
- Delegates count logic to relationships via polymorphic toCountQuery() method
- Wraps constraint closures in Holloway Builder for full query API access
- Preserves relationship-level scopes during counting
- Count columns added as subqueries on main query

Tests: All 23 tests passing
Breaking Changes: None (Custom relationships without count closure throw helpful exception)
@tabennett
tabennett force-pushed the feature/add-with-count branch from 2e545e5 to 4065545 Compare October 3, 2026 21:44
…eys, and or constraints

- Correlate has-one/has-many/belongs-to-many counts on the relationship's own
  local key, as load() does, instead of the mapper's primary key.
- Alias the related table when it is also the parent table, as Eloquent does
  for self relations, so a self-referential count no longer correlates the
  table with itself. Add Builder::aliasTable() for this.
- Build count queries on the related mapper's query builder: relationships
  apply the constraints and then the global scopes once, last, so constraints
  can use withoutGlobalScope() and scopes are no longer applied twice. This
  adds an optional constraints argument to Relationship::toCountQuery().
- Apply constraints as a scope (Builder::callScope() is now public, as in
  Eloquent) so an or in a constraint stays inside the correlation instead of
  matching rows of other parents.
- Accept an uppercase AS when aliasing a count, and point the custom
  relationship's missing count closure error at Mapper::custom().
- Add a self-referential category fixture and tests for local keys,
  self-referential counts, or constraints, and custom relationship counts.
- Explain that the count is a column on the record passed to hydrate() and
  must be left out of dehydrate().
- Document constraint behavior (unqualified columns, scopes applied after,
  withoutGlobalScope) and same-table relationships.
- Fix the cheatsheet example, which used typographic quotes in PHP.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant