Skip to content

feat: configurable field-accessor prefixes (SchemaFactory::stripFieldPrefixes) - #827

Merged
oojacoboo merged 2 commits into
thecodingmachine:masterfrom
oojacoboo:feat/strip-field-prefixes
Aug 20, 2026
Merged

feat: configurable field-accessor prefixes (SchemaFactory::stripFieldPrefixes)#827
oojacoboo merged 2 commits into
thecodingmachine:masterfrom
oojacoboo:feat/strip-field-prefixes

Conversation

@oojacoboo

Copy link
Copy Markdown
Collaborator

What

Adds SchemaFactory::stripFieldPrefixes(getters:, setters:) to configure the method-name prefixes GraphQLite strips to derive field names and matches when resolving property accessors:

$factory->stripFieldPrefixes(getters: ['get', 'is', 'has'], setters: ['set']);

This generalizes the hasser support proposed in #766 (thanks @adynemo). Adding has to the getters exposes hassers such as hasStock() via #[SourceField], #[Field], etc. The defaults (getters: ['get', 'is'], setters: ['set']) preserve the historical behavior, so the feature is opt-in.

Exact-case boundary rule

Prefixes are now stripped only when they sit on a real camelCase boundary (the character right after the prefix is uppercase). So isEnabled() becomes enabled and hasHKey() becomes hKey, but ordinary words such as issue(), hashKey(), gettext(), settings() and setup() are left untouched.

This replaces the previous length-only check (str_starts_with($m, 'is') && strlen($m) > 2), which mis-stripped any word starting with a prefix (issue() gave a field named sue, settings() gave tings). See the discussion in #766.

Breaking change

The exact-case rule changes field-name derivation for method names where a prefix is followed by a lowercase letter. The change is always in the "less stripping" direction and only affects names that were almost certainly being mis-stripped:

method old field new field
getName() name name (unchanged)
isEnabled() enabled enabled (unchanged)
issue() sue issue
gettext() text gettext
settings() tings settings

Migration: if a #[Field]/#[SourceField] method relied on the old behavior, set an explicit name with #[Field(name:)] or the source field's name. Documented in CHANGELOG under 8.4.0.

Design notes

  • A small immutable FieldAccessorPrefixes value object owns the two prefix lists and the boundary rule, so NamingStrategy (naming) and PropertyAccessor (lookup) share one source of truth.
  • NamingStrategyInterface is unchanged; prefixes are a construction-time detail of the default NamingStrategy.
  • All new parameters are optional and defaulted (PropertyAccessor::findGetter/findSetter, the FieldsBuilder and NamingStrategy constructors), so existing callers are unaffected.
  • Runtime PropertyAccessor::getValue/setValue intentionally keep the default prefixes: they back property #[Field] reads/writes against an existing property, whereas the hasser/#[SourceField] path resolves the method at build time and invokes it through SourceMethodResolver.

Tests

  • Unit: FieldAccessorPrefixesTest (strip logic, hasSetterPrefix, edge cases), plus additions to NamingStrategyTest and PropertyAccessorTest.
  • End-to-end (StripFieldPrefixesTest): a #[SourceField] hasser (getter side) and a custom-prefixed setter input field (setter side) each resolve and hydrate only when the prefix is configured.
  • Full suite green, PHPStan level 8 clean, phpcs clean.

…Prefixes)

Generalizes the hasser support proposed in thecodingmachine#766 into a configurable set of accessor prefixes. SchemaFactory::stripFieldPrefixes(getters:, setters:) sets the method-name prefixes stripped to derive field names and matched when resolving property accessors such as #[SourceField]. Adding "has" to the getters exposes hassers like hasStock().

Prefixes are stripped only on a camelCase boundary (the character after the prefix must be uppercase), so genuine accessors like isEnabled() become enabled while ordinary words such as issue() or hashKey() are left untouched.

Defaults (getters get/is, setters set) preserve the historical behavior, so the change is backward compatible.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.79%. Comparing base (53f9d49) to head (9d941af).
⚠️ Report is 171 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #827      +/-   ##
============================================
- Coverage     95.72%   91.79%   -3.94%     
- Complexity     1773     2101     +328     
============================================
  Files           154      203      +49     
  Lines          4586     5630    +1044     
============================================
+ Hits           4390     5168     +778     
- Misses          196      462     +266     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@oojacoboo
oojacoboo merged commit f7f3eaa into thecodingmachine:master Aug 20, 2026
12 checks passed
@oojacoboo
oojacoboo deleted the feat/strip-field-prefixes branch August 20, 2026 03:34
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.

2 participants