Skip to content

5.0: Use RelationListIterator - #2548

Merged
adriendupuis merged 29 commits into
5.0from
5.0-relationlistiterator
Aug 26, 2026
Merged

5.0: Use RelationListIterator#2548
adriendupuis merged 29 commits into
5.0from
5.0-relationlistiterator

Conversation

@adriendupuis

@adriendupuis adriendupuis commented Nov 20, 2024

Copy link
Copy Markdown
Contributor
Question Answer
JIRA Ticket N/A
Versions 5.0
Edition All

To get all relations, use RelationListIteratorAdapter + BatchIterator instead of ContentService::countRelations() + ContentService::loadRelationList()

Follows #2544 and in particular this comment.

Previews:

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

Fix "Argument #1 ($contentId) must be of type int, string given"
loadRelations is deprecated in 4.5, and removed in 5.0

Fix "Call to an undefined method Ibexa\Contracts\Core\Repository\ContentService::loadRelations()." on PHPStan + 5.0
loadRelations is deprecated in 4.5, and removed in 5.0

Fix "Call to an undefined method Ibexa\Contracts\Core\Repository\ContentService::loadRelations()." on PHPStan + 5.0
@adriendupuis adriendupuis added the Wait with merge PRs that shouldn't be merged instantly label Nov 21, 2024
# Conflicts:
#	code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php
#	code_samples/front/embed_content/src/Controller/RelationController.php
#	docs/content_management/content_api/browsing_content.md
@mnocon
mnocon changed the base branch from master to 5.0 May 19, 2025 07:33
@adriendupuis adriendupuis removed the Wait with merge PRs that shouldn't be merged instantly label May 20, 2025
# Conflicts:
#	docs/content_management/content_api/browsing_content.md
@github-actions

github-actions Bot commented Jun 6, 2025

Copy link
Copy Markdown

# Conflicts:
#	docs/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-Iterator-BatchIteratorAdapter-RelationListIteratorAdapter.html
@adriendupuis
adriendupuis marked this pull request as ready for review July 22, 2025 10:13

@mnocon mnocon left a comment

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.

No comments about the code, looks solid 💪

Only one suggestion about the wording, as it's not clear to me right now.

Comment thread docs/content_management/content_api/browsing_content.md Outdated
@adriendupuis
adriendupuis requested a review from mnocon July 22, 2025 14:58

@mnocon mnocon left a comment

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.

I'd mention https://doc.ibexa.co/en/latest/search/search_api/#processing-large-result-sets for people not familiar with it (I've found this page only recently, when adjusting the code samples for v5)

Comment thread docs/content_management/content_api/browsing_content.md Outdated
# Conflicts:
#	docs/content_management/content_api/browsing_content.md
#	docs/templating/embed_and_list_content/embed_content.md
@adriendupuis
adriendupuis force-pushed the 5.0-relationlistiterator branch from bf389ca to c3599fe Compare August 25, 2026 10:22
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

Summary

Status Count
🔍 Total 750701
🔗 Unique 15048
✅ Successful 6305
⏳ Timeouts 0
🔀 Redirected 0
👻 Excluded 744396
❓ Unknown 0
🚫 Errors 0
⛔ Unsupported 0

Full Github Actions output

@adriendupuis
adriendupuis force-pushed the 5.0-relationlistiterator branch from 8cdb1ab to 304c554 Compare August 25, 2026 10:55
@adriendupuis
adriendupuis requested a review from mnocon August 25, 2026 12:58
Comment thread docs/content_management/content_api/browsing_content.md Outdated
Comment thread docs/content_management/content_api/browsing_content.md Outdated
Comment thread docs/content_management/content_api/browsing_content.md Outdated
Comment thread docs/content_management/content_api/browsing_content.md
Comment thread docs/content_management/content_api/browsing_content.md Outdated
Comment thread docs/content_management/content_api/browsing_content.md Outdated
adriendupuis and others added 4 commits August 25, 2026 16:15
Co-authored-by: Marek Nocoń <mnocon@users.noreply.github.com>
Co-authored-by: Marek Nocoń <mnocon@users.noreply.github.com>
Co-authored-by: Marek Nocoń <mnocon@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

code_samples/ change report

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

code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php


code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php

docs/content_management/content_api/browsing_content.md@28:``` php hl_lines="14"
docs/content_management/content_api/browsing_content.md@29:[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 0, 5) =]]
docs/content_management/content_api/browsing_content.md@30:[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 21, 23) =]]
docs/content_management/content_api/browsing_content.md@31:// ...
docs/content_management/content_api/browsing_content.md@32:
docs/content_management/content_api/browsing_content.md@33:[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 42, 44) =]][[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 50, 58) =]]
docs/content_management/content_api/browsing_content.md@34:[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 113, 116) =]]
docs/content_management/content_api/browsing_content.md@35:```
docs/content_management/content_api/browsing_content.md@28:``` php hl_lines="13"
docs/content_management/content_api/browsing_content.md@29:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 1, 5) =]]
docs/content_management/content_api/browsing_content.md@30:
docs/content_management/content_api/browsing_content.md@31:// …
docs/content_management/content_api/browsing_content.md@32:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 24, 25) =]]
docs/content_management/content_api/browsing_content.md@33: // …
docs/content_management/content_api/browsing_content.md@34:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 45, 46) =]]
docs/content_management/content_api/browsing_content.md@35:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 53, 60) =]]
docs/content_management/content_api/browsing_content.md@36:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 119) =]]
docs/content_management/content_api/browsing_content.md@37:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Command;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Command;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶
007⫶class ViewContentMetaDataCommand extends Command
008⫶{
009⫶
010⫶// ...
011⫶
012⫶ protected function execute(InputInterface $input, OutputInterface $output): int
013⫶ {
014❇️ $contentInfo = $this->contentService->loadContentInfo($contentId);
015⫶
016⫶ $output->writeln("Name: $contentInfo->name");
017⫶ $output->writeln('Last modified: ' . $contentInfo->modificationDate->format('Y-m-d'));
018⫶ $output->writeln('Published: ' . $contentInfo->publishedDate->format('Y-m-d'));
019⫶ $output->writeln("RemoteId: $contentInfo->remoteId");
020⫶ $output->writeln("Main Language: $contentInfo->mainLanguageCode");
021⫶ $output->writeln('Always available: ' . ($contentInfo->alwaysAvailable ? 'Yes' : 'No'));
022⫶
023⫶ return self::SUCCESS;
024⫶ }
025⫶}

docs/content_management/content_api/browsing_content.md@48:``` php
docs/content_management/content_api/browsing_content.md@49:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 56, 61, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@50:```

001⫶$output->writeln("RemoteId: $contentInfo->remoteId");
002⫶$output->writeln("Main Language: $contentInfo->mainLanguageCode");
003⫶$output->writeln('Always available: ' . ($contentInfo->alwaysAvailable ? 'Yes' : 'No'));
004⫶
005⫶// Locations
006⫶$locations = $this->locationService->loadLocations($contentInfo);
007⫶// …
008⫶class ViewContentMetaDataCommand extends Command
009⫶{
010⫶ // …
011⫶ protected function execute(InputInterface $input, OutputInterface $output): int
012⫶ {
013❇️ $contentInfo = $this->contentService->loadContentInfo($contentId);
014⫶
015⫶ $output->writeln("Name: $contentInfo->name");
016⫶ $output->writeln('Last modified: ' . $contentInfo->modificationDate->format('Y-m-d'));
017⫶ $output->writeln('Published: ' . $contentInfo->publishedDate->format('Y-m-d'));
018⫶ $output->writeln("RemoteId: $contentInfo->remoteId");
019⫶ $output->writeln("Main Language: $contentInfo->mainLanguageCode");
020⫶ $output->writeln('Always available: ' . ($contentInfo->alwaysAvailable ? 'Yes' : 'No'));

docs/content_management/content_api/browsing_content.md@50:``` php
docs/content_management/content_api/browsing_content.md@51:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 63, 66, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@52:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 69, 69, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@53:```

001⫶$locations = $this->locationService->loadLocations($contentInfo);
002⫶
003⫶foreach ($locations as $location) {
004⫶ $output->writeln('Location: ' . $location->pathString);
005⫶}


docs/content_management/content_api/browsing_content.md@62:``` php
docs/content_management/content_api/browsing_content.md@63:[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 73, 76) =]][[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 77, 80) =]]
docs/content_management/content_api/browsing_content.md@64:```

001⫶ $versionInfos = $this->contentService->loadVersions($contentInfo);
002⫶ foreach ($versionInfos as $versionInfo) {
003⫶ $output->write("Version $versionInfo->versionNo");
004⫶ $output->writeln(' in ' . $versionInfo->getInitialLanguage()->name);
005⫶ }

docs/content_management/content_api/browsing_content.md@70:``` php
docs/content_management/content_api/browsing_content.md@71:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 70, 71, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@72:```
docs/content_management/content_api/browsing_content.md@65:``` php
docs/content_management/content_api/browsing_content.md@66:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 63, 65, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@67:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 67, 69, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@68:```

001⫶$locations = $this->locationService->loadLocations($contentInfo);
002⫶
003⫶foreach ($locations as $location) {
004⫶ $urlAlias = $this->urlAliasService->reverseLookup($location);
005⫶ $output->writeln('URL alias: ' . $urlAlias->path);
006⫶}

docs/content_management/content_api/browsing_content.md@74:``` php
docs/content_management/content_api/browsing_content.md@75:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 72, 73, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@76:```

001⫶$content = $this->contentService->loadContent($contentId);
002⫶$output->writeln('Content type: ' . $content->getContentType()->getName());


001⫶$content = $this->contentService->loadContent($contentId);
002⫶$output->writeln('Content type: ' . $content->getContentType()->getName());

docs/content_management/content_api/browsing_content.md@78:``` php
docs/content_management/content_api/browsing_content.md@79:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 74, 79, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@80:```
docs/content_management/content_api/browsing_content.md@82:``` php
docs/content_management/content_api/browsing_content.md@83:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 76, 81, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@84:```

001⫶$versionInfos = $this->contentService->loadVersions($contentInfo);
002⫶foreach ($versionInfos as $versionInfo) {
003⫶ $output->write("Version $versionInfo->versionNo");
004⫶ $output->write(' by ' . $versionInfo->getCreator()->getName());
005⫶ $output->writeln(' in ' . $versionInfo->getInitialLanguage()->name);
006⫶}


001⫶$versionInfos = $this->contentService->loadVersions($contentInfo);
002⫶foreach ($versionInfos as $versionInfo) {
003⫶ $output->write("Version $versionInfo->versionNo");
004⫶ $output->write(' by ' . $versionInfo->getCreator()->getName());
005⫶ $output->writeln(' in ' . $versionInfo->getInitialLanguage()->name);
006⫶}

docs/content_management/content_api/browsing_content.md@84:``` php
docs/content_management/content_api/browsing_content.md@85:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 81, 81, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@86:```
docs/content_management/content_api/browsing_content.md@88:``` php
docs/content_management/content_api/browsing_content.md@89:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 83, 83, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@90:```

001⫶$versionInfoArray = iterator_to_array($this->contentService->loadVersions($contentInfo, VersionInfo::STATUS_ARCHIVED));


001⫶$versionInfoArray = iterator_to_array($this->contentService->loadVersions($contentInfo, VersionInfo::STATUS_ARCHIVED));

docs/content_management/content_api/browsing_content.md@100:``` php
docs/content_management/content_api/browsing_content.md@101:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 93, 99, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@102:```
docs/content_management/content_api/browsing_content.md@110:``` php
docs/content_management/content_api/browsing_content.md@111:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 95, 105, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@112:```

001⫶$versionInfo = $this->contentService->loadVersionInfo($contentInfo);

001⫶$versionInfo = $this->contentService->loadVersionInfo($contentInfo);
002⫶$relationCount = $this->contentService->countRelations($versionInfo);
003⫶$relationList = $this->contentService->loadRelationList($versionInfo, 0, $relationCount);
004⫶foreach ($relationList as $relationListItem) {
005⫶ $name = $relationListItem->hasRelation() ? $relationListItem->getRelation()->destinationContentInfo->name : '(Unauthorized)';
006⫶ $output->writeln("Relation to content '$name'");
007⫶}

docs/content_management/content_api/browsing_content.md@123:``` php
docs/content_management/content_api/browsing_content.md@124:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 102, 102, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@125:```

001⫶$output->writeln('Owner: ' . $contentInfo->getOwner()->getName());
002⫶$relationListIterator = new BatchIterator(
003⫶ new RelationListIteratorAdapter(
004⫶ $this->contentService,
005⫶ $versionInfo
006⫶ )
007⫶);
008⫶foreach ($relationListIterator as $relationListItem) {
009⫶ $name = $relationListItem->hasRelation() ? $relationListItem->getRelation()->destinationContentInfo->name : '(Unauthorized)';
010⫶ $output->writeln("Relation to content '$name'");
011⫶}

docs/content_management/content_api/browsing_content.md@133:``` php

docs/content_management/content_api/browsing_content.md@133:``` php
docs/content_management/content_api/browsing_content.md@134:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 105, 105, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@134:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 108, 108, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@135:```

docs/content_management/content_api/browsing_content.md@135:```

001⫶$output->writeln('Owner: ' . $contentInfo->getOwner()->getName());

docs/content_management/content_api/browsing_content.md@143:``` php
docs/content_management/content_api/browsing_content.md@144:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 111, 111, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@145:```

001⫶$output->writeln('Section: ' . $contentInfo->getSection()->name);

001⫶$output->writeln('Section: ' . $contentInfo->getSection()->name);

docs/content_management/content_api/browsing_content.md@148:``` php
docs/content_management/content_api/browsing_content.md@149:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 108, 112, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@150:```
docs/content_management/content_api/browsing_content.md@158:``` php
docs/content_management/content_api/browsing_content.md@159:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 114, 118, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@160:```

001⫶$stateGroups = $this->objectStateService->loadObjectStateGroups();
002⫶foreach ($stateGroups as $stateGroup) {
003⫶ $state = $this->objectStateService->getContentState($contentInfo, $stateGroup);
004⫶ $output->writeln("Object state: $state->identifier");
005⫶}


code_samples/front/embed_content/src/Controller/RelationController.php


001⫶$stateGroups = $this->objectStateService->loadObjectStateGroups();
002⫶foreach ($stateGroups as $stateGroup) {
003⫶ $state = $this->objectStateService->getContentState($contentInfo, $stateGroup);
004⫶ $output->writeln("Object state: $state->identifier");
005⫶}


code_samples/front/embed_content/src/Controller/RelationController.php

docs/templating/embed_and_list_content/embed_content.md@39:``` php hl_lines="23 27 28"
docs/templating/embed_and_list_content/embed_content.md@39:``` php hl_lines="21 26-31"
docs/templating/embed_and_list_content/embed_content.md@40:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php') =]]
docs/templating/embed_and_list_content/embed_content.md@41:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Controller;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
docs/templating/embed_and_list_content/embed_content.md@40:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php') =]]
docs/templating/embed_and_list_content/embed_content.md@41:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Controller;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶use Ibexa\Contracts\Core\Repository\LocationService;
007⫶use Ibexa\Core\MVC\Symfony\View\View;
008⫶
009⫶class RelationController
010⫶{
011⫶ public function __construct(
012⫶ private readonly ContentService $contentService,
013⫶ private readonly LocationService $locationService
014⫶ ) {
015⫶ }
016⫶
017⫶ public function showContentAction(View $view, $locationId): View
018⫶ {
019⫶ $acceptedContentTypes = $view->getParameter('accepted_content_types');
020⫶
021⫶ $location = $this->locationService->loadLocation($locationId);
022⫶ $contentInfo = $location->getContentInfo();
023❇️ $versionInfo = $this->contentService->loadVersionInfo($contentInfo);
024⫶ $relationList = $this->contentService->loadRelationList($versionInfo);
025⫶
026⫶ $items = [];
027❇️
028❇️ foreach ($relationList as $relationListItem) {
029⫶ if ($relationListItem->hasRelation() && in_array($relationListItem->getRelation()->getDestinationContentInfo()->getContentType()->identifier, $acceptedContentTypes)) {
030⫶ $items[] = $this->contentService->loadContentByContentInfo($relationListItem->getRelation()->getDestinationContentInfo());
031⫶ }
032⫶ }
033⫶
034⫶ $view->addParameters([
035⫶ 'items' => $items,
036⫶ ]);
037⫶
038⫶ return $view;
039⫶ }
040⫶}
006⫶use Ibexa\Contracts\Core\Repository\Iterator\BatchIterator;
007⫶use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\RelationListIteratorAdapter;
008⫶use Ibexa\Contracts\Core\Repository\LocationService;
009⫶use Ibexa\Core\MVC\Symfony\View\View;
010⫶
011⫶class RelationController
012⫶{
013⫶ public function __construct(
014⫶ private readonly ContentService $contentService,
015⫶ private readonly LocationService $locationService
016⫶ ) {
017⫶ }
018⫶
019⫶ public function showContentAction(View $view, $locationId): View
020⫶ {
021❇️ $acceptedContentTypes = $view->getParameter('accepted_content_types');
022⫶
023⫶ $location = $this->locationService->loadLocation($locationId);
024⫶ $contentInfo = $location->getContentInfo();
025⫶ $versionInfo = $this->contentService->loadVersionInfo($contentInfo);
026❇️ $relationListIterator = new BatchIterator(
027❇️ new RelationListIteratorAdapter(
028❇️ $this->contentService,
029❇️ $versionInfo
030❇️ )
031❇️ );
032⫶
033⫶ $items = [];
034⫶
035⫶ foreach ($relationListIterator as $relationListItem) {
036⫶ if ($relationListItem->hasRelation() && in_array($relationListItem->getRelation()->getDestinationContentInfo()->getContentType()->identifier, $acceptedContentTypes)) {
037⫶ $items[] = $this->contentService->loadContentByContentInfo($relationListItem->getRelation()->getDestinationContentInfo());
038⫶ }
039⫶ }
040⫶
041⫶ $view->addParameters([
042⫶ 'items' => $items,
043⫶ ]);
044⫶
045⫶ return $view;
046⫶ }
047⫶}

docs/templating/queries_and_controllers/controllers.md@15:``` php

docs/templating/queries_and_controllers/controllers.md@15:``` php
docs/templating/queries_and_controllers/controllers.md@16:[[= include_file('code_samples/front/embed_content/src/Controller/RelationController.php', 0, 10) =]][[= include_file('code_samples/front/embed_content/src/Controller/RelationController.php', 16, 18) =]][[= include_file('code_samples/front/embed_content/src/Controller/RelationController.php', 37, 40) =]]
docs/templating/queries_and_controllers/controllers.md@17:```
docs/templating/queries_and_controllers/controllers.md@16:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php', 1, 5) =]]
docs/templating/queries_and_controllers/controllers.md@17:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php', 8, 9) =]]
docs/templating/queries_and_controllers/controllers.md@18:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php', 10, 12) =]]
docs/templating/queries_and_controllers/controllers.md@19:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php', 19, 20) =]]
docs/templating/queries_and_controllers/controllers.md@20:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php', 45) =]]
docs/templating/queries_and_controllers/controllers.md@21:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Controller;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶use Ibexa\Contracts\Core\Repository\LocationService;
007⫶use Ibexa\Core\MVC\Symfony\View\View;
008⫶
009⫶class RelationController
010⫶{
011⫶ public function showContentAction(View $view, $locationId): View
012⫶ {

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Controller;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶use Ibexa\Contracts\Core\Repository\LocationService;
007⫶use Ibexa\Core\MVC\Symfony\View\View;
008⫶
009⫶class RelationController
010⫶{
011⫶ public function showContentAction(View $view, $locationId): View
012⫶ {
013⫶        return $view;
014⫶ }
015⫶}


Download colorized diff

@github-actions

Copy link
Copy Markdown

code_samples/ change report

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

code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php


code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php

docs/content_management/content_api/browsing_content.md@28:``` php hl_lines="14"
docs/content_management/content_api/browsing_content.md@29:[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 0, 5) =]]
docs/content_management/content_api/browsing_content.md@30:[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 21, 23) =]]
docs/content_management/content_api/browsing_content.md@31:// ...
docs/content_management/content_api/browsing_content.md@32:
docs/content_management/content_api/browsing_content.md@33:[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 42, 44) =]][[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 50, 58) =]]
docs/content_management/content_api/browsing_content.md@34:[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 113, 116) =]]
docs/content_management/content_api/browsing_content.md@35:```
docs/content_management/content_api/browsing_content.md@28:``` php hl_lines="13"
docs/content_management/content_api/browsing_content.md@29:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 1, 5) =]]
docs/content_management/content_api/browsing_content.md@30:
docs/content_management/content_api/browsing_content.md@31:// …
docs/content_management/content_api/browsing_content.md@32:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 24, 25) =]]
docs/content_management/content_api/browsing_content.md@33: // …
docs/content_management/content_api/browsing_content.md@34:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 45, 46) =]]
docs/content_management/content_api/browsing_content.md@35:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 53, 60) =]]
docs/content_management/content_api/browsing_content.md@36:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 119) =]]
docs/content_management/content_api/browsing_content.md@37:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Command;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Command;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶
007⫶class ViewContentMetaDataCommand extends Command
008⫶{
009⫶
010⫶// ...
011⫶
012⫶ protected function execute(InputInterface $input, OutputInterface $output): int
013⫶ {
014❇️ $contentInfo = $this->contentService->loadContentInfo($contentId);
015⫶
016⫶ $output->writeln("Name: $contentInfo->name");
017⫶ $output->writeln('Last modified: ' . $contentInfo->modificationDate->format('Y-m-d'));
018⫶ $output->writeln('Published: ' . $contentInfo->publishedDate->format('Y-m-d'));
019⫶ $output->writeln("RemoteId: $contentInfo->remoteId");
020⫶ $output->writeln("Main Language: $contentInfo->mainLanguageCode");
021⫶ $output->writeln('Always available: ' . ($contentInfo->alwaysAvailable ? 'Yes' : 'No'));
022⫶
023⫶ return self::SUCCESS;
024⫶ }
025⫶}

docs/content_management/content_api/browsing_content.md@48:``` php
docs/content_management/content_api/browsing_content.md@49:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 56, 61, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@50:```

001⫶$output->writeln("RemoteId: $contentInfo->remoteId");
002⫶$output->writeln("Main Language: $contentInfo->mainLanguageCode");
003⫶$output->writeln('Always available: ' . ($contentInfo->alwaysAvailable ? 'Yes' : 'No'));
004⫶
005⫶// Locations
006⫶$locations = $this->locationService->loadLocations($contentInfo);
007⫶// …
008⫶class ViewContentMetaDataCommand extends Command
009⫶{
010⫶ // …
011⫶ protected function execute(InputInterface $input, OutputInterface $output): int
012⫶ {
013❇️ $contentInfo = $this->contentService->loadContentInfo($contentId);
014⫶
015⫶ $output->writeln("Name: $contentInfo->name");
016⫶ $output->writeln('Last modified: ' . $contentInfo->modificationDate->format('Y-m-d'));
017⫶ $output->writeln('Published: ' . $contentInfo->publishedDate->format('Y-m-d'));
018⫶ $output->writeln("RemoteId: $contentInfo->remoteId");
019⫶ $output->writeln("Main Language: $contentInfo->mainLanguageCode");
020⫶ $output->writeln('Always available: ' . ($contentInfo->alwaysAvailable ? 'Yes' : 'No'));

docs/content_management/content_api/browsing_content.md@50:``` php
docs/content_management/content_api/browsing_content.md@51:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 63, 66, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@52:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 69, 69, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@53:```

001⫶$locations = $this->locationService->loadLocations($contentInfo);
002⫶
003⫶foreach ($locations as $location) {
004⫶ $output->writeln('Location: ' . $location->pathString);
005⫶}


docs/content_management/content_api/browsing_content.md@62:``` php
docs/content_management/content_api/browsing_content.md@63:[[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 73, 76) =]][[= include_file('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 77, 80) =]]
docs/content_management/content_api/browsing_content.md@64:```

001⫶ $versionInfos = $this->contentService->loadVersions($contentInfo);
002⫶ foreach ($versionInfos as $versionInfo) {
003⫶ $output->write("Version $versionInfo->versionNo");
004⫶ $output->writeln(' in ' . $versionInfo->getInitialLanguage()->name);
005⫶ }

docs/content_management/content_api/browsing_content.md@70:``` php
docs/content_management/content_api/browsing_content.md@71:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 70, 71, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@72:```
docs/content_management/content_api/browsing_content.md@65:``` php
docs/content_management/content_api/browsing_content.md@66:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 63, 65, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@67:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 67, 69, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@68:```

001⫶$locations = $this->locationService->loadLocations($contentInfo);
002⫶
003⫶foreach ($locations as $location) {
004⫶ $urlAlias = $this->urlAliasService->reverseLookup($location);
005⫶ $output->writeln('URL alias: ' . $urlAlias->path);
006⫶}

docs/content_management/content_api/browsing_content.md@74:``` php
docs/content_management/content_api/browsing_content.md@75:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 72, 73, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@76:```

001⫶$content = $this->contentService->loadContent($contentId);
002⫶$output->writeln('Content type: ' . $content->getContentType()->getName());


001⫶$content = $this->contentService->loadContent($contentId);
002⫶$output->writeln('Content type: ' . $content->getContentType()->getName());

docs/content_management/content_api/browsing_content.md@78:``` php
docs/content_management/content_api/browsing_content.md@79:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 74, 79, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@80:```
docs/content_management/content_api/browsing_content.md@82:``` php
docs/content_management/content_api/browsing_content.md@83:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 76, 81, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@84:```

001⫶$versionInfos = $this->contentService->loadVersions($contentInfo);
002⫶foreach ($versionInfos as $versionInfo) {
003⫶ $output->write("Version $versionInfo->versionNo");
004⫶ $output->write(' by ' . $versionInfo->getCreator()->getName());
005⫶ $output->writeln(' in ' . $versionInfo->getInitialLanguage()->name);
006⫶}


001⫶$versionInfos = $this->contentService->loadVersions($contentInfo);
002⫶foreach ($versionInfos as $versionInfo) {
003⫶ $output->write("Version $versionInfo->versionNo");
004⫶ $output->write(' by ' . $versionInfo->getCreator()->getName());
005⫶ $output->writeln(' in ' . $versionInfo->getInitialLanguage()->name);
006⫶}

docs/content_management/content_api/browsing_content.md@84:``` php
docs/content_management/content_api/browsing_content.md@85:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 81, 81, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@86:```
docs/content_management/content_api/browsing_content.md@88:``` php
docs/content_management/content_api/browsing_content.md@89:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 83, 83, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@90:```

001⫶$versionInfoArray = iterator_to_array($this->contentService->loadVersions($contentInfo, VersionInfo::STATUS_ARCHIVED));


001⫶$versionInfoArray = iterator_to_array($this->contentService->loadVersions($contentInfo, VersionInfo::STATUS_ARCHIVED));

docs/content_management/content_api/browsing_content.md@100:``` php
docs/content_management/content_api/browsing_content.md@101:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 93, 99, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@102:```
docs/content_management/content_api/browsing_content.md@110:``` php
docs/content_management/content_api/browsing_content.md@111:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 95, 105, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@112:```

001⫶$versionInfo = $this->contentService->loadVersionInfo($contentInfo);

001⫶$versionInfo = $this->contentService->loadVersionInfo($contentInfo);
002⫶$relationCount = $this->contentService->countRelations($versionInfo);
003⫶$relationList = $this->contentService->loadRelationList($versionInfo, 0, $relationCount);
004⫶foreach ($relationList as $relationListItem) {
005⫶ $name = $relationListItem->hasRelation() ? $relationListItem->getRelation()->destinationContentInfo->name : '(Unauthorized)';
006⫶ $output->writeln("Relation to content '$name'");
007⫶}

docs/content_management/content_api/browsing_content.md@123:``` php
docs/content_management/content_api/browsing_content.md@124:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 102, 102, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@125:```

001⫶$output->writeln('Owner: ' . $contentInfo->getOwner()->getName());
002⫶$relationListIterator = new BatchIterator(
003⫶ new RelationListIteratorAdapter(
004⫶ $this->contentService,
005⫶ $versionInfo
006⫶ )
007⫶);
008⫶foreach ($relationListIterator as $relationListItem) {
009⫶ $name = $relationListItem->hasRelation() ? $relationListItem->getRelation()->destinationContentInfo->name : '(Unauthorized)';
010⫶ $output->writeln("Relation to content '$name'");
011⫶}

docs/content_management/content_api/browsing_content.md@133:``` php

docs/content_management/content_api/browsing_content.md@133:``` php
docs/content_management/content_api/browsing_content.md@134:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 105, 105, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@134:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 108, 108, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@135:```

docs/content_management/content_api/browsing_content.md@135:```

001⫶$output->writeln('Owner: ' . $contentInfo->getOwner()->getName());

docs/content_management/content_api/browsing_content.md@143:``` php
docs/content_management/content_api/browsing_content.md@144:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 111, 111, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@145:```

001⫶$output->writeln('Section: ' . $contentInfo->getSection()->name);

001⫶$output->writeln('Section: ' . $contentInfo->getSection()->name);

docs/content_management/content_api/browsing_content.md@148:``` php
docs/content_management/content_api/browsing_content.md@149:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 108, 112, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@150:```
docs/content_management/content_api/browsing_content.md@158:``` php
docs/content_management/content_api/browsing_content.md@159:[[= include_code('code_samples/api/public_php_api/src/Command/ViewContentMetaDataCommand.php', 114, 118, remove_indent=True) =]]
docs/content_management/content_api/browsing_content.md@160:```

001⫶$stateGroups = $this->objectStateService->loadObjectStateGroups();
002⫶foreach ($stateGroups as $stateGroup) {
003⫶ $state = $this->objectStateService->getContentState($contentInfo, $stateGroup);
004⫶ $output->writeln("Object state: $state->identifier");
005⫶}


code_samples/front/embed_content/src/Controller/RelationController.php


001⫶$stateGroups = $this->objectStateService->loadObjectStateGroups();
002⫶foreach ($stateGroups as $stateGroup) {
003⫶ $state = $this->objectStateService->getContentState($contentInfo, $stateGroup);
004⫶ $output->writeln("Object state: $state->identifier");
005⫶}


code_samples/front/embed_content/src/Controller/RelationController.php

docs/templating/embed_and_list_content/embed_content.md@39:``` php hl_lines="23 27 28"
docs/templating/embed_and_list_content/embed_content.md@39:``` php hl_lines="21 26-31"
docs/templating/embed_and_list_content/embed_content.md@40:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php') =]]
docs/templating/embed_and_list_content/embed_content.md@41:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Controller;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
docs/templating/embed_and_list_content/embed_content.md@40:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php') =]]
docs/templating/embed_and_list_content/embed_content.md@41:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Controller;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶use Ibexa\Contracts\Core\Repository\LocationService;
007⫶use Ibexa\Core\MVC\Symfony\View\View;
008⫶
009⫶class RelationController
010⫶{
011⫶ public function __construct(
012⫶ private readonly ContentService $contentService,
013⫶ private readonly LocationService $locationService
014⫶ ) {
015⫶ }
016⫶
017⫶ public function showContentAction(View $view, $locationId): View
018⫶ {
019⫶ $acceptedContentTypes = $view->getParameter('accepted_content_types');
020⫶
021⫶ $location = $this->locationService->loadLocation($locationId);
022⫶ $contentInfo = $location->getContentInfo();
023❇️ $versionInfo = $this->contentService->loadVersionInfo($contentInfo);
024⫶ $relationList = $this->contentService->loadRelationList($versionInfo);
025⫶
026⫶ $items = [];
027❇️
028❇️ foreach ($relationList as $relationListItem) {
029⫶ if ($relationListItem->hasRelation() && in_array($relationListItem->getRelation()->getDestinationContentInfo()->getContentType()->identifier, $acceptedContentTypes)) {
030⫶ $items[] = $this->contentService->loadContentByContentInfo($relationListItem->getRelation()->getDestinationContentInfo());
031⫶ }
032⫶ }
033⫶
034⫶ $view->addParameters([
035⫶ 'items' => $items,
036⫶ ]);
037⫶
038⫶ return $view;
039⫶ }
040⫶}
006⫶use Ibexa\Contracts\Core\Repository\Iterator\BatchIterator;
007⫶use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\RelationListIteratorAdapter;
008⫶use Ibexa\Contracts\Core\Repository\LocationService;
009⫶use Ibexa\Core\MVC\Symfony\View\View;
010⫶
011⫶class RelationController
012⫶{
013⫶ public function __construct(
014⫶ private readonly ContentService $contentService,
015⫶ private readonly LocationService $locationService
016⫶ ) {
017⫶ }
018⫶
019⫶ public function showContentAction(View $view, $locationId): View
020⫶ {
021❇️ $acceptedContentTypes = $view->getParameter('accepted_content_types');
022⫶
023⫶ $location = $this->locationService->loadLocation($locationId);
024⫶ $contentInfo = $location->getContentInfo();
025⫶ $versionInfo = $this->contentService->loadVersionInfo($contentInfo);
026❇️ $relationListIterator = new BatchIterator(
027❇️ new RelationListIteratorAdapter(
028❇️ $this->contentService,
029❇️ $versionInfo
030❇️ )
031❇️ );
032⫶
033⫶ $items = [];
034⫶
035⫶ foreach ($relationListIterator as $relationListItem) {
036⫶ if ($relationListItem->hasRelation() && in_array($relationListItem->getRelation()->getDestinationContentInfo()->getContentType()->identifier, $acceptedContentTypes)) {
037⫶ $items[] = $this->contentService->loadContentByContentInfo($relationListItem->getRelation()->getDestinationContentInfo());
038⫶ }
039⫶ }
040⫶
041⫶ $view->addParameters([
042⫶ 'items' => $items,
043⫶ ]);
044⫶
045⫶ return $view;
046⫶ }
047⫶}

docs/templating/queries_and_controllers/controllers.md@15:``` php

docs/templating/queries_and_controllers/controllers.md@15:``` php
docs/templating/queries_and_controllers/controllers.md@16:[[= include_file('code_samples/front/embed_content/src/Controller/RelationController.php', 0, 10) =]][[= include_file('code_samples/front/embed_content/src/Controller/RelationController.php', 16, 18) =]][[= include_file('code_samples/front/embed_content/src/Controller/RelationController.php', 37, 40) =]]
docs/templating/queries_and_controllers/controllers.md@17:```
docs/templating/queries_and_controllers/controllers.md@16:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php', 1, 5) =]]
docs/templating/queries_and_controllers/controllers.md@17:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php', 8, 9) =]]
docs/templating/queries_and_controllers/controllers.md@18:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php', 10, 12) =]]
docs/templating/queries_and_controllers/controllers.md@19:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php', 19, 20) =]]
docs/templating/queries_and_controllers/controllers.md@20:[[= include_code('code_samples/front/embed_content/src/Controller/RelationController.php', 45) =]]
docs/templating/queries_and_controllers/controllers.md@21:```

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Controller;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶use Ibexa\Contracts\Core\Repository\LocationService;
007⫶use Ibexa\Core\MVC\Symfony\View\View;
008⫶
009⫶class RelationController
010⫶{
011⫶ public function showContentAction(View $view, $locationId): View
012⫶ {

001⫶<?php declare(strict_types=1);
002⫶
003⫶namespace App\Controller;
004⫶
005⫶use Ibexa\Contracts\Core\Repository\ContentService;
006⫶use Ibexa\Contracts\Core\Repository\LocationService;
007⫶use Ibexa\Core\MVC\Symfony\View\View;
008⫶
009⫶class RelationController
010⫶{
011⫶ public function showContentAction(View $view, $locationId): View
012⫶ {
013⫶        return $view;
014⫶ }
015⫶}


Download colorized diff

@adriendupuis
adriendupuis requested a review from mnocon August 25, 2026 14:32

@mnocon mnocon left a comment

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.

Thank you!

@adriendupuis
adriendupuis merged commit 4147003 into 5.0 Aug 26, 2026
13 checks passed
@adriendupuis
adriendupuis deleted the 5.0-relationlistiterator branch August 26, 2026 07:06
adriendupuis added a commit that referenced this pull request Aug 26, 2026
To get all relations, use `RelationListIteratorAdapter` + `BatchIterator` instead of `ContentService::countRelations()` + `ContentService::loadRelationList()`

---------

Co-authored-by: Marek Nocoń <mnocon@users.noreply.github.com>
adriendupuis added a commit that referenced this pull request Aug 26, 2026
To get all relations, use `RelationListIteratorAdapter` + `BatchIterator` instead of `ContentService::countRelations()` + `ContentService::loadRelationList()`

---------

Co-authored-by: Marek Nocoń <mnocon@users.noreply.github.com>
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