Skip to content

feat: add per-type API transformer sparse fieldsets#10285

Open
michalsn wants to merge 2 commits into
codeigniter4:4.8from
michalsn:feat/api-transformer-sparse-fieldsets
Open

feat: add per-type API transformer sparse fieldsets#10285
michalsn wants to merge 2 commits into
codeigniter4:4.8from
michalsn:feat/api-transformer-sparse-fieldsets

Conversation

@michalsn

@michalsn michalsn commented Jun 7, 2026

Copy link
Copy Markdown
Member

Description
This PR adds per-type sparse fieldsets for API transformers via fields[<type>].

Transformers can opt in by defining $resourceType, allowing nested resources to apply their own field filters without inheriting the root fields parameter.

Ref: #10278 (comment)

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

@michalsn michalsn added enhancement PRs that improve existing functionalities 4.8 PRs that target the `4.8` branch. labels Jun 7, 2026
protected mixed $resource = null;

public function __construct(
private ?IncomingRequest $request = null,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To allow reusing the same transformer for multiple relations (like posts and comments) with independent fieldsets, we should add an $overrideResourceType parameter to the constructor.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d rather not add this. The idea is, $resourceType is the resource type, not the relation name. If someone needs the same transformer logic for two resource types, they can make small subclasses with different $resourceType values.

Comment on lines +102 to +132
/**
* Resolves the requested field list for this transformer from the request.
*
* Supports both the flat `?fields=a,b` form and the per-type sparse
* fieldset form `?fields[<type>]=a,b`. The flat form is only honored when
* $allowFlat is true (i.e. for the root transformer); a type-specific
* fieldset is matched against this transformer's $resourceType at any
* nesting level.
*
* @return list<string>|null
*/
private function resolveFields(bool $allowFlat): ?array
{
$fields = $this->request->getGet('fields');

// Sparse fieldsets: ?fields[posts]=id,slug -> ['posts' => 'id,slug']
if (is_array($fields)) {
$scoped = ($this->resourceType !== null && is_string($fields[$this->resourceType] ?? null))
? $fields[$this->resourceType]
: null;

return $scoped !== null ? $this->splitList($scoped) : null;
}

// Flat fieldset: ?fields=id,slug (applies to the root only)
if ($allowFlat && is_string($fields)) {
return $this->splitList($fields);
}

return null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In resolveFields(), mixing fields[x]=... and fields=... in the URL causes PHP to natively overwrite the array and break the filters,we need to manually parse the QUERY_STRING to prevent this bug.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should manually parse QUERY_STRING for this. If clients need separate fieldsets for root and nested resources, they can use the bracketed form for both, e.g. fields[users]=id,name&fields[posts]=id,slug. This is already covered by tests.

I added an explanation for this behavior to the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.8 PRs that target the `4.8` branch. enhancement PRs that improve existing functionalities

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants