Skip to content

IBX-8190: Update REST custom media type - #2688

Open
adriendupuis wants to merge 7 commits into
5.0from
update-rest-visitor-resolver
Open

IBX-8190: Update REST custom media type#2688
adriendupuis wants to merge 7 commits into
5.0from
update-rest-visitor-resolver

Conversation

@adriendupuis

@adriendupuis adriendupuis commented Apr 1, 2025

Copy link
Copy Markdown
Contributor
Question Answer
JIRA Ticket IBX-8190
Versions 5.0
Edition All
  • Move from removed ValueObjectVisitorDispatcher to new ValueObjectVisitorResolver.

Preview: Adding custom media type

Related PRs

This PR is the minimal mandatory change. In other PRs, some other optional improvement studies are made.

Checklist

  • Text renders correctly
  • Text has been checked with vale
  • Description metadata is up to date
  • Redirects cover removed/moved pages
  • Code samples are working
  • PHP code samples have been fixed with PHP CS fixer
  • Added link to this PR in relevant JIRA ticket or code PR

Move to ValueObjectVisitorDispatcher to ValueObjectVisitorResolver
@github-actions

github-actions Bot commented Apr 1, 2025

Copy link
Copy Markdown

Property App\Rest\Output\ValueObjectVisitorResolver::$visitors type has no value type specified in iterable type array.

Method App\Rest\Output\ValueObjectVisitorResolver::__construct() has parameter $visitors with no value type specified in iterable type iterable.
@adriendupuis
adriendupuis marked this pull request as ready for review April 2, 2025 06:59
@adriendupuis
adriendupuis requested a review from barw4 April 2, 2025 06:59
@mnocon
mnocon changed the base branch from master to 5.0 May 19, 2025 07:25
@github-actions

Copy link
Copy Markdown

# Conflicts:
#	code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php
#	docs/api/rest_api/extending_rest_api/adding_custom_media_type.md
#	phpstan-baseline.neon
@adriendupuis
adriendupuis force-pushed the update-rest-visitor-resolver branch from 29a8a72 to cf3ee7b Compare August 27, 2026 08:56
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

Summary

Status Count
🔍 Total 750718
🔗 Unique 15054
✅ Successful 6305
⏳ Timeouts 0
🔀 Redirected 0
👻 Excluded 744413
❓ Unknown 0
🚫 Errors 0
⛔ Unsupported 0

Full Github Actions output

@github-actions

Copy link
Copy Markdown

code_samples/ change report

Before (on target branch)After (in current PR)

code_samples/api/rest_api/config/services.yaml

docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@41:``` yaml
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@42:services:
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@43: #…

code_samples/api/rest_api/config/services.yaml

docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@41:``` yaml
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@42:services:
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@43: #…
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@44:[[= include_file('code_samples/api/rest_api/config/services.yaml', 28, 35) =]]
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@44:[[= include_file('code_samples/api/rest_api/config/services.yaml', 33, 41) =]]
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@45:```

001⫶services:
002⫶ #…
003⫶ App\Rest\ValueObjectVisitor\RestLocation:
004⫶ class: App\Rest\ValueObjectVisitor\RestLocation
005⫶ parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor
006⫶ arguments:
007⫶ $urlAliasService: '@ibexa.api.service.url_alias'
008⫶ tags:
009⫶ - { name: app.rest.output.value_object.visitor, type: Ibexa\Rest\Server\Values\RestLocation }

docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@52:``` yaml
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@53:services:
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@54: #…
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@45:```

001⫶services:
002⫶ #…
003⫶ App\Rest\ValueObjectVisitor\RestLocation:
004⫶ class: App\Rest\ValueObjectVisitor\RestLocation
005⫶ parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor
006⫶ arguments:
007⫶ $urlAliasService: '@ibexa.api.service.url_alias'
008⫶ tags:
009⫶ - { name: app.rest.output.value_object.visitor, type: Ibexa\Rest\Server\Values\RestLocation }

docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@52:``` yaml
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@53:services:
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@54: #…
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@55:[[= include_file('code_samples/api/rest_api/config/services.yaml', 22, 27) =]]
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@55:[[= include_file('code_samples/api/rest_api/config/services.yaml', 28, 32) =]]
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@56:```

001⫶services:
002⫶ #…
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@56:```

001⫶services:
002⫶ #…
003⫶    App\Rest\Output\ValueObjectVisitorDispatcher:
004⫶ class: App\Rest\Output\ValueObjectVisitorDispatcher
005⫶ arguments:
006⫶ - !tagged_iterator { tag: 'app.rest.output.value_object.visitor', index_by: 'type' }
007⫶ - '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher'
003⫶    App\Rest\Output\ValueObjectVisitorResolver:
004⫶ arguments:
005⫶ $visitors: !tagged_iterator { tag: 'app.rest.output.value_object.visitor', index_by: 'type' }
006⫶ $resolver: '@Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolver'

docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@67:``` yaml
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@68:parameters:
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@69: #…
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@70:[[= include_file('code_samples/api/rest_api/config/services.yaml', 1, 3) =]]
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@71:services:
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@72: #…

docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@67:``` yaml
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@68:parameters:
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@69: #…
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@70:[[= include_file('code_samples/api/rest_api/config/services.yaml', 1, 3) =]]
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@71:services:
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@72: #…
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@73:[[= include_file('code_samples/api/rest_api/config/services.yaml', 6, 21) =]]
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@73:[[= include_file('code_samples/api/rest_api/config/services.yaml', 6, 27) =]]
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@74:```

001⫶parameters:
002⫶ #…
003⫶ app.rest.output.visitor.xml.regexps: ['(^application/app\.api\.[A-Za-z]+\+xml$)']
004⫶ app.rest.output.visitor.json.regexps: ['(^application/app\.api\.[A-Za-z]+\+json$)']
005⫶
006⫶services:
007⫶ #…
008⫶ app.rest.output.visitor.xml:
009⫶ class: Ibexa\Contracts\Rest\Output\Visitor
010⫶ arguments:
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@74:```

001⫶parameters:
002⫶ #…
003⫶ app.rest.output.visitor.xml.regexps: ['(^application/app\.api\.[A-Za-z]+\+xml$)']
004⫶ app.rest.output.visitor.json.regexps: ['(^application/app\.api\.[A-Za-z]+\+json$)']
005⫶
006⫶services:
007⫶ #…
008⫶ app.rest.output.visitor.xml:
009⫶ class: Ibexa\Contracts\Rest\Output\Visitor
010⫶ arguments:
011⫶            - '@Ibexa\Rest\Output\Generator\Xml'
012⫶ - '@App\Rest\Output\ValueObjectVisitorDispatcher'
013⫶ tags:
014⫶ - { name: ibexa.rest.output.visitor, regexps: app.rest.output.visitor.xml.regexps, priority: 20 }
015⫶
016⫶ app.rest.output.visitor.json:
017⫶ class: Ibexa\Contracts\Rest\Output\Visitor
018⫶ arguments:
019⫶ - '@Ibexa\Rest\Output\Generator\Json'
020⫶ - '@App\Rest\Output\ValueObjectVisitorDispatcher'
021⫶ tags:
022⫶ - { name: ibexa.rest.output.visitor, regexps: app.rest.output.visitor.json.regexps, priority: 20 }
011⫶            $generator: '@Ibexa\Rest\Output\Generator\Xml'
012⫶ $normalizer: '@ibexa.rest.serializer'
013⫶ $encoder: '@ibexa.rest.serializer.encoder.xml'
014⫶ $valueObjectVisitorResolver: '@App\Rest\Output\ValueObjectVisitorResolver'
015⫶ $format: 'xml'
016⫶ tags:
017⫶ - { name: ibexa.rest.output.visitor, regexps: app.rest.output.visitor.xml.regexps, priority: 20 }
018⫶
019⫶ app.rest.output.visitor.json:
020⫶ class: Ibexa\Contracts\Rest\Output\Visitor
021⫶ arguments:
022⫶ $generator: '@Ibexa\Rest\Output\Generator\Json'
023⫶ $normalizer: '@ibexa.rest.serializer'
024⫶ $encoder: '@ibexa.rest.serializer.encoder.json'
025⫶ $valueObjectVisitorResolver: '@App\Rest\Output\ValueObjectVisitorResolver'
026⫶ $format: 'json'
027⫶ tags:
028⫶ - { name: ibexa.rest.output.visitor, regexps: app.rest.output.visitor.json.regexps, priority: 20 }

docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@48:``` yaml
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@49:services:
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@50: #…

docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@48:``` yaml
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@49:services:
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@50: #…
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@51:[[= include_file('code_samples/api/rest_api/config/services.yaml', 36, 42) =]]
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@51:[[= include_file('code_samples/api/rest_api/config/services.yaml', 41, 47) =]]
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@52:```

001⫶services:
002⫶ #…
003⫶ App\Rest\Controller\:
004⫶ resource: '../src/Rest/Controller/'
005⫶ parent: Ibexa\Rest\Server\Controller
006⫶ autowire: true
007⫶ autoconfigure: true
008⫶ tags: [ 'controller.service_arguments' ]

docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@102:``` yaml
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@103:services:
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@104: #…
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@52:```

001⫶services:
002⫶ #…
003⫶ App\Rest\Controller\:
004⫶ resource: '../src/Rest/Controller/'
005⫶ parent: Ibexa\Rest\Server\Controller
006⫶ autowire: true
007⫶ autoconfigure: true
008⫶ tags: [ 'controller.service_arguments' ]

docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@102:``` yaml
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@103:services:
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@104: #…
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@105:[[= include_file('code_samples/api/rest_api/config/services.yaml', 43, 48) =]]
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@105:[[= include_file('code_samples/api/rest_api/config/services.yaml', 48, 53) =]]
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@106:```

001⫶services:
002⫶ #…
003⫶ App\Rest\ValueObjectVisitor\Greeting:
004⫶ parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor
005⫶ tags:
006⫶ - { name: ibexa.rest.output.value_object.visitor, type: App\Rest\Values\Greeting }

docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@124:``` yaml
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@125:services:
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@126: #…
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@106:```

001⫶services:
002⫶ #…
003⫶ App\Rest\ValueObjectVisitor\Greeting:
004⫶ parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor
005⫶ tags:
006⫶ - { name: ibexa.rest.output.value_object.visitor, type: App\Rest\Values\Greeting }

docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@124:``` yaml
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@125:services:
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@126: #…
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@127:[[= include_file('code_samples/api/rest_api/config/services.yaml', 48, 53) =]]
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@127:[[= include_file('code_samples/api/rest_api/config/services.yaml', 53, 58) =]]
docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@128:```

001⫶services:
002⫶ #…
003⫶ App\Rest\InputParser\GreetingInput:
004⫶ parent: Ibexa\Rest\Server\Common\Parser
005⫶ tags:
006⫶ - { name: ibexa.rest.input.parser, mediaType: application/vnd.ibexa.api.GreetingInput }


code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php

docs/api/rest_api/extending_rest_api/creating_new_rest_resource.md@128:```

001⫶services:
002⫶ #…
003⫶ App\Rest\InputParser\GreetingInput:
004⫶ parent: Ibexa\Rest\Server\Common\Parser
005⫶ tags:
006⫶ - { name: ibexa.rest.input.parser, mediaType: application/vnd.ibexa.api.GreetingInput }


code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php


code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorResolver.php

docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@58:``` php
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@58:``` php
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@59:[[= include_code('code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorDispatcher.php') =]]
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@59:[[= include_code('code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorResolver.php') =]]
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@60:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Rest\Output;
004⫶
docs/api/rest_api/extending_rest_api/adding_custom_media_type.md@60:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Rest\Output;
004⫶
005⫶use Ibexa\Contracts\Rest\Output\Generator;
006⫶use Ibexa\Contracts\Rest\Output\ValueObjectVisitorDispatcher as BaseValueObjectVisitorDispatcher;
007⫶use Ibexa\Contracts\Rest\Output\Visitor;
008⫶
009⫶class ValueObjectVisitorDispatcher // extends BaseValueObjectVisitorDispatcher TODO: Rewrite this example in https://issues.ibexa.co/browse/IBX-8190
010⫶{
005⫶use Ibexa\Contracts\Rest\Output\ValueObjectVisitor;
006⫶use Ibexa\Contracts\Rest\Output\ValueObjectVisitorResolverInterface;
007⫶
008⫶class ValueObjectVisitorResolver implements ValueObjectVisitorResolverInterface
009⫶{
010⫶ /** @var array<string, ValueObjectVisitor> */
011⫶    private array $visitors;
012⫶
011⫶    private array $visitors;
012⫶
013⫶    private Visitor $outputVisitor;
014⫶
015⫶ private Generator $outputGenerator;
016⫶
017⫶ public function __construct(
018⫶ iterable $visitors,
019⫶ private readonly BaseValueObjectVisitorDispatcher $valueObjectVisitorDispatcher
020⫶ ) {
021⫶ $this->visitors = [];
022⫶ foreach ($visitors as $type => $visitor) {
023⫶ $this->visitors[$type] = $visitor;
024⫶ }
025⫶ }
026⫶
027⫶ public function setOutputVisitor(Visitor $outputVisitor): void
028⫶ {
029⫶ $this->outputVisitor = $outputVisitor;
030⫶ $this->valueObjectVisitorDispatcher->setOutputVisitor($outputVisitor);
031⫶ }
032⫶
033⫶ public function setOutputGenerator(Generator $outputGenerator): void
034⫶ {
035⫶ $this->outputGenerator = $outputGenerator;
036⫶ $this->valueObjectVisitorDispatcher->setOutputGenerator($outputGenerator);
037⫶ }
038⫶
039⫶ public function visit($data)
040⫶ {
041⫶ $className = $data::class;
042⫶ if (isset($this->visitors[$className])) {
043⫶ return $this->visitors[$className]->visit($this->outputVisitor, $this->outputGenerator, $data);
044⫶ }
045⫶
046⫶ return $this->valueObjectVisitorDispatcher->visit($data);
047⫶ }
048⫶}


code_samples/api/rest_api/src/Rest/Output/ValueObjectVisitorResolver.php
013⫶    /** @param iterable<string, ValueObjectVisitor> $visitors */
014⫶ public function __construct(iterable $visitors, private readonly ValueObjectVisitorResolverInterface $valueObjectVisitorResolver)
015⫶ {
016⫶ $this->visitors = [];
017⫶ foreach ($visitors as $type => $visitor) {
018⫶ $this->visitors[$type] = $visitor;
019⫶ }
020⫶ }
021⫶
022⫶ public function resolveValueObjectVisitor(object $object): ?ValueObjectVisitor
023⫶ {
024⫶ $className = $object::class;
025⫶
026⫶ return $this->visitors[$className] ?? $this->valueObjectVisitorResolver->resolveValueObjectVisitor($object);
027⫶ }
028⫶}


Download colorized diff

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