# Browsing and viewing content

To retrieve a content item and its information, you need to make use of the [`ContentService`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-ContentService.html).

The service should be [injected into the constructor of your command or controller](https://doc.ibexa.co/en/latest/api/php_api/php_api/#service-container).

Content REST API

To learn how to load content items using the REST API, see [REST API reference](https://doc.ibexa.co/en/latest/api/rest_api/rest_api_reference/rest_api_reference.html#tag/Objects/operation/api_contentobjects_contentId_get).

Console commands

To learn more about commands in Symfony, refer to [Console Commands](https://symfony.com/doc/7.4/console.html).

## Viewing content metadata

### ContentInfo

Basic content metadata is available through [`ContentInfo`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-Values-Content-ContentInfo.html) objects and their properties. This value object provides primitive fields, such as `contentTypeId`, `publishedDate`, or `mainLocationId`, and methods for retrieving selected properties.

You can also use it to request other content-related value objects from various services:

```
<?php declare(strict_types=1);

namespace App\Command;

use Ibexa\Contracts\Core\Repository\ContentService;

class ViewContentMetaDataCommand extends Command
{

// ...

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $contentInfo = $this->contentService->loadContentInfo($contentId);

        $output->writeln("Name: $contentInfo->name");
        $output->writeln('Last modified: ' . $contentInfo->modificationDate->format('Y-m-d'));
        $output->writeln('Published: ' . $contentInfo->publishedDate->format('Y-m-d'));
        $output->writeln("RemoteId: $contentInfo->remoteId");
        $output->writeln("Main Language: $contentInfo->mainLanguageCode");
        $output->writeln('Always available: ' . ($contentInfo->alwaysAvailable ? 'Yes' : 'No'));

        return self::SUCCESS;
    }
}
```

`ContentInfo` is loaded from the [`ContentService`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-ContentService.html) (line 8). It provides you with basic content metadata such as modification and publication dates or main language code.

Retrieving content information in a controller

To retrieve content information in a controller, you also make use of the `ContentService`, but rendering specific elements (for example, content information or field values) is relegated to [templates](https://doc.ibexa.co/en/latest/templating/templates/templates/index.md).

### Locations

To get the locations of a content item you need to make use of the [`LocationService`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-LocationService.html):

```
        $output->writeln("RemoteId: $contentInfo->remoteId");
        $output->writeln("Main Language: $contentInfo->mainLanguageCode");
        $output->writeln('Always available: ' . ($contentInfo->alwaysAvailable ? 'Yes' : 'No'));

        // Locations
        $locations = $this->locationService->loadLocations($contentInfo);
```

[`LocationService::loadLocations`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-LocationService.html#method_loadLocations) uses `ContentInfo` to get all the locations of a content item. This method returns an array of [`Location`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Persistence-Content-Location.html) value objects. For each location, the code above prints out its `pathString` (the internal representation of the path).

#### URL Aliases

The [`URLAliasService`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-URLAliasService.html) additionally enables you to retrieve the human-readable [URL alias](https://doc.ibexa.co/en/latest/content_management/url_management/url_management/#url-aliases) of each location.

[`URLAliasService::reverseLookup`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-URLAliasService.html#method_reverseLookup) gets the location's main [URL alias](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-Values-Content-URLAlias.html):

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

### Content type

You can retrieve the content type of a content item through the [`getContentType`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-Values-Content-ContentInfo.html#method_getContentType) method of the ContentInfo object:

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

### Versions

To iterate over the versions of a content item, use the [`ContentService::loadVersions`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-ContentService.html#method_loadVersions) method, which returns an array of `VersionInfo` value objects.

```
        $versionInfos = $this->contentService->loadVersions($contentInfo);
        foreach ($versionInfos as $versionInfo) {
            $output->write("Version $versionInfo->versionNo");
            $output->write(' by ' . $versionInfo->getCreator()->getName());
            $output->writeln(' in ' . $versionInfo->getInitialLanguage()->name);
        }
```

You can additionally provide the `loadVersions` method with the version status to get only versions of a specific status, for example:

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

Note

Requesting version data may be impossible for an anonymous user. Make sure to [authenticate](https://doc.ibexa.co/en/latest/api/php_api/php_api/#setting-the-repository-user) as a user with sufficient permissions.

### Relations

Content Relations are versioned. To list Relations to and from your content, you need to pass a `VersionInfo` object to the [`ContentService::loadRelationList`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-ContentService.html#method_loadRelationList) method. This method loads only the specified subset of relations to improve performance and was created with pagination in mind. You can get the current version's `VersionInfo` using [`ContentService::loadVersionInfo`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-ContentService.html#method_loadVersionInfo).

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

You can also specify the version number as the second argument to get Relations for a specific version:

```
$versionInfo = $this->contentService->loadVersionInfo($contentInfo, 2);
```

`loadRelationList` provides an iterable [`RelationList`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-Values-Content-RelationList.html) object listing [`Relation`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-Values-Content-Relation.html) objects. `Relation` has two main properties: `destinationContentInfo`, and `sourceContentInfo`. It also holds the [relation type](https://doc.ibexa.co/en/latest/content_management/content_relations/index.md), and the optional field this relation is made with.

### Owning user

You can use the `getOwner` method of the `ContentInfo` object to load the content item's owner as a `User` value object.

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

To get the creator of the current version and not the content item's owner, you need to use the `creatorId` property from the current version's `VersionInfo` object.

### Section

You can find the section to which a content item belongs through the [`getSection`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-Values-Content-ContentInfo.html#method_getSection) method of the ContentInfo object:

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

Note

Requesting section data may be impossible for an anonymous user. Make sure to [authenticate](https://doc.ibexa.co/en/latest/api/php_api/php_api/#setting-the-repository-user) as a user with sufficient permissions.

### Object states

You can retrieve [object states](https://doc.ibexa.co/en/latest/administration/content_organization/object_states/index.md) of a content item using [`ObjectStateService::getContentState`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-ObjectStateService.html#method_getContentState). You need to provide it with the object state group. All object state groups can be retrieved through [`loadObjectStateGroups`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-ObjectStateService.html#method_loadObjectStateGroups).

```
        $stateGroups = $this->objectStateService->loadObjectStateGroups();
        foreach ($stateGroups as $stateGroup) {
            $state = $this->objectStateService->getContentState($contentInfo, $stateGroup);
            $output->writeln("Object state: $state->identifier");
        }
```

## Viewing content with fields

To retrieve the fields of the selected content item, you can use the following command:

```
<?php declare(strict_types=1);

namespace App\Command;

use Ibexa\Contracts\Core\Repository\ContentService;
use Ibexa\Contracts\Core\Repository\ContentTypeService;
use Ibexa\Contracts\Core\Repository\FieldTypeService;

// ...
class ViewContentCommand extends Command
{

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $contentId = (int) $input->getArgument('contentId');

        $content = $this->contentService->loadContent($contentId);
        $contentType = $this->contentTypeService->loadContentType($content->contentInfo->contentTypeId);

        foreach ($contentType->fieldDefinitions as $fieldDefinition) {
            $output->writeln('Field: ' . $fieldDefinition->identifier);
            $fieldType = $this->fieldTypeService->getFieldType($fieldDefinition->fieldTypeIdentifier);
            $field = $content->getFieldValue($fieldDefinition->identifier);
            $valueHash = $fieldType->toHash($field);
            $output->writeln('Value:');
            $output->writeln($valueHash);
        }

        return self::SUCCESS;
    }
}
```

Line 9 shows how [`ContentService::loadContent`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-ContentService.html#method_loadContent) loads the content item provided to the command. Line 10 makes use of the [`ContentTypeService`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-ContentTypeService.html) to retrieve the content type of the requested item.

Lines 12-19 iterate over fields defined by the content type. For each field they print out its identifier, and then using [`FieldTypeService`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-FieldTypeService.html) retrieve the field's value and print it out to the console.

## Viewing content in different languages

The repository is SiteAccess-aware, so languages defined by the SiteAccess are automatically taken into account when loading content.

To load a specific language, provide its language code when loading the content item:

```
$content = $this->contentService->loadContent($contentId, ['ger-DE']);
```

To load all languages as a prioritized list, use `Language::ALL`:

```
$contentService->loadContent($content->id, Language::ALL);
```

## Getting all content in a subtree

To go through all the content items contained in a subtree, you need to use the [`LocationService`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-LocationService.html).

```
    private function browseLocation(Location $location, OutputInterface $output, int $depth = 0): void
    {
        $output->writeln($location->contentInfo->name);

        $children = $this->locationService->loadLocationChildren($location);
        foreach ($children->locations as $child) {
            $this->browseLocation($child, $output, $depth + 1);
        }
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $locationId = (int) $input->getArgument('locationId');

        $location = $this->locationService->loadLocation($locationId);
        $this->browseLocation($location, $output);

        return self::SUCCESS;
    }
```

`loadLocation` (line 15) returns a value object, here a `Location`.

[`LocationService::loadLocationChildren`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-LocationService.html#method_loadLocationChildren) (line 5) returns a [`LocationList`](https://doc.ibexa.co/en/latest/api/php_api/php_api_reference/classes/Ibexa-Contracts-Core-Repository-Values-Content-LocationList.html) value object that you can iterate over.

Note

Refer to [Searching](https://doc.ibexa.co/en/latest/search/search_api/index.md) for information on more complex search queries.

## Getting parent location

To get the parent location of content, you first need to determine which location is the main one, in case the content item has multiple locations. You can do it through the `getMainLocation` method of the ContentInfo object.

Next, use the `getParentLocation` method of the location object to access the parent location:

```
$mainLocation = $contentInfo->getMainLocation();
$output->writeln("Parent Location: " . $mainLocation->getParentLocation()->pathString);
```

## Getting content from a location

When dealing with location objects (and Trash objects), you can get access to content item directly using `$location->getContent`. In Twig this can also be accessed by `location.content`. This is a lazy property. It triggers loading of content when first used. In case of bulk of locations coming from Search or location Service, the content is also loaded in bulk for the whole location result set.

## Comparing content versions

You can compare two versions of a content item using the `VersionComparisonService`. The versions must have the same language.

For example, to get the comparison between the `name` field of two versions:

```
$versionFrom = $this->contentService->loadVersionInfo($contentInfo, $versionFromId);
$versionTo = $this->contentService->loadVersionInfo($contentInfo, $versionToId);

$nameComparison = $this->comparisonService->compare($versionFrom, $versionTo)->getFieldValueDiffByIdentifier('name')->getComparisonResult();
```

`getComparisonResult` returns a `ComparisonResult` object, which depends on the field type being compared. In the example of a Text Line (ibexa_string) field, it's an array of `StringDiff` objects.

Each diff contains a section of the field to compare (for example, a part of a text line) and its status, which can be "unchanged", "added" or "removed".
