Skip to content

Commit

Permalink
[Data Object] Add inheritance information to detail endpoint (#680)
Browse files Browse the repository at this point in the history
* add inheritance info

* Apply php-cs-fixer changes

* small refactoring
  • Loading branch information
lukmzig authored Jan 14, 2025
1 parent 9000b94 commit 9ad7478
Show file tree
Hide file tree
Showing 14 changed files with 581 additions and 69 deletions.
3 changes: 3 additions & 0 deletions config/data_objects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ services:
Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataService

Pimcore\Bundle\StudioBackendBundle\DataObject\Service\InheritanceServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\InheritanceService

Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterService

Expand Down
109 changes: 103 additions & 6 deletions src/DataObject/Data/Adapter/ClassificationStoreAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@
use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\ClassificationStore\DefinitionCacheResolverInterface;
use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\ClassificationStore\GroupConfigResolverInterface;
use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\ClassificationStore\ServiceResolverInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataInheritanceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\InheritanceData;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Util\Trait\ValidateFieldTypeTrait;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\DatabaseException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotFoundException;
use Pimcore\Model\DataObject\ClassDefinition\Data;
use Pimcore\Model\DataObject\ClassDefinition\Data\Classificationstore as ClassificationstoreDefinition;
use Pimcore\Model\DataObject\Classificationstore;
Expand All @@ -46,7 +49,10 @@
* @internal
*/
#[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)]
final readonly class ClassificationStoreAdapter implements SetterDataInterface, DataNormalizerInterface
final readonly class ClassificationStoreAdapter implements
SetterDataInterface,
DataNormalizerInterface,
DataInheritanceInterface
{
use ValidateFieldTypeTrait;

Expand Down Expand Up @@ -100,14 +106,14 @@ public function normalize(
$resultItems = [];

foreach ($this->getActiveGroups($value) as $groupId => $groupConfig) {
$resultItems[$groupConfig->getName()] = [];
$keys = $this->getClassificationStoreKeysFromGroup($groupConfig);
$resultItems[$groupId] = [];
$keys = $this->getClassificationStoreKeysFromGroup($groupId);
foreach ($validLanguages as $validLanguage) {
foreach ($keys as $key) {
$normalizedValue = $this->getNormalizedValue($value, $groupId, $key, $validLanguage);

if ($normalizedValue !== null) {
$resultItems[$groupConfig->getName()][$validLanguage][$key->getName()] = $normalizedValue;
$resultItems[$groupId][$validLanguage][$key->getKeyId()] = $normalizedValue;
}
}
}
Expand All @@ -116,6 +122,41 @@ public function normalize(
return $resultItems;
}

public function getFieldInheritance(
Concrete $object,
Data $fieldDefinition,
string $key,
?FieldContextData $contextData = null
): array {
$inheritedData = [];
if (!$fieldDefinition instanceof ClassificationstoreDefinition) {
return [];
}
$languages = $this->getValidLanguages($fieldDefinition);

foreach ($this->getStoreDefinitions($object, $fieldDefinition) as $groupId => $groupDefinitions) {
foreach ($groupDefinitions as $groupKeyId => $definition) {
foreach ($languages as $language) {
$originId = $this->getOriginId(
$object,
$definition,
$key,
$groupId,
$groupKeyId,
$language
);

$inheritedData[$groupId][$language][$groupKeyId] = new InheritanceData(
$originId,
$originId !== $object->getId()
);
}
}
}

return $inheritedData;
}

/**
* @throws Exception
*/
Expand Down Expand Up @@ -216,6 +257,29 @@ private function cleanupStoreGroups(Classificationstore $container): void
}
}

private function getStoreDefinitions(
Concrete $dataObject,
ClassificationstoreDefinition $classificationStore
): array {
$mapping = [];
foreach ($classificationStore->recursiveGetActiveGroupsIds($dataObject) as $groupId => $active) {
if (!$active) {
continue;
}

$keys = $this->getClassificationStoreKeysFromGroup($groupId);
foreach ($keys as $groupKey) {
$definition = $this->serviceResolver->getFieldDefinitionFromKeyConfig($groupKey);
if ($definition === null) {
continue;
}
$mapping[$groupKey->getGroupId()][$groupKey->getKeyId()] = $definition;
}
}

return $mapping;
}

private function getValidLanguages(ClassificationstoreDefinition $classificationStore): array
{
$languages = [MappingProperty::NOT_LOCALIZED_KEY];
Expand Down Expand Up @@ -247,10 +311,10 @@ private function getActiveGroups(ClassificationstoreModel $value): array
/**
* @return KeyGroupRelation[]
*/
private function getClassificationStoreKeysFromGroup(GroupConfig $groupConfig): array
private function getClassificationStoreKeysFromGroup(int $groupId): array
{
$listing = new KeyGroupRelationListing();
$listing->addConditionParam('groupId = ?', $groupConfig->getId());
$listing->addConditionParam('groupId = ?', $groupId);

return $listing->getList();
}
Expand Down Expand Up @@ -285,4 +349,37 @@ private function getNormalizedValue(

return $this->dataService->getNormalizedValue($value, $fieldDefinition);
}

/**
* @throws NotFoundException
*/
private function getOriginId(
Concrete $object,
Data $fieldDefinition,
string $key,
int $groupId,
int $groupKeyId,
string $language
): int {
$data = $this->getValidFieldValue($object, $key)
->getLocalizedKeyValue($groupId, $groupKeyId, $language, true, true);

if (!$fieldDefinition->isEmpty($data)) {
return $object->getId();
}

$parent = $object->getNextParentForInheritance();
if ($parent === null) {
return $object->getId();
}

return $this->getOriginId(
$parent,
$fieldDefinition,
$key,
$groupId,
$groupKeyId,
$language
);
}
}
38 changes: 37 additions & 1 deletion src/DataObject/Data/Adapter/LocalizedFieldsAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@
namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Adapter;

use Exception;
use Pimcore\Bundle\StaticResolverBundle\Lib\ToolResolverInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataInheritanceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\InheritanceServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Util\Trait\ValidateFieldTypeTrait;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\DatabaseException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\InvalidArgumentException;
Expand All @@ -43,14 +46,19 @@
* @internal
*/
#[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)]
final readonly class LocalizedFieldsAdapter implements SetterDataInterface, DataNormalizerInterface
final readonly class LocalizedFieldsAdapter implements
SetterDataInterface,
DataNormalizerInterface,
DataInheritanceInterface
{
use ValidateFieldTypeTrait;

public function __construct(
private DataAdapterServiceInterface $dataAdapterService,
private DataServiceInterface $dataService,
private InheritanceServiceInterface $inheritanceService,
private SecurityServiceInterface $securityService,
private ToolResolverInterface $toolResolver
) {
}

Expand Down Expand Up @@ -143,6 +151,34 @@ public function normalize(
return $result;
}

public function getFieldInheritance(
Concrete $object,
Data $fieldDefinition,
string $key,
?FieldContextData $contextData = null
): array {
if (!$fieldDefinition instanceof Localizedfields) {
return [];
}

$inheritedData = [];
$contextObject = $contextData?->getContextObject();
$fields = $fieldDefinition->getChildren();

foreach ($fields as $field) {
foreach ($this->toolResolver->getValidLanguages() as $language) {
$inheritedData[$field->getName()][$language] = $this->inheritanceService->processFieldDefinition(
$object,
$field,
$key,
new FieldContextData(contextObject: $contextObject, language: $language)
);
}
}

return $inheritedData;
}

private function getAllowedLanguages(
Concrete $element,
array $languageData
Expand Down
49 changes: 47 additions & 2 deletions src/DataObject/Data/Adapter/ObjectBricksAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@

use Exception;
use Pimcore\Bundle\StaticResolverBundle\Models\DataObject\Objectbrick\DefinitionResolverInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataInheritanceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\DataNormalizerInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\SetterDataInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterLoaderInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataAdapterServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\DataServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Service\InheritanceServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\DataObject\Util\Trait\ValidateFieldTypeTrait;
use Pimcore\Model\DataObject\ClassDefinition\Data;
use Pimcore\Model\DataObject\ClassDefinition\Data\Objectbricks;
Expand All @@ -38,14 +40,18 @@
* @internal
*/
#[AutoconfigureTag(DataAdapterLoaderInterface::ADAPTER_TAG)]
final readonly class ObjectBricksAdapter implements SetterDataInterface, DataNormalizerInterface
final readonly class ObjectBricksAdapter implements
SetterDataInterface,
DataNormalizerInterface,
DataInheritanceInterface
{
use ValidateFieldTypeTrait;

public function __construct(
private DataAdapterServiceInterface $dataAdapterService,
private DataServiceInterface $dataService,
private DefinitionResolverInterface $definitionResolver
private DefinitionResolverInterface $definitionResolver,
private InheritanceServiceInterface $inheritanceService,
) {
}

Expand Down Expand Up @@ -107,6 +113,45 @@ public function normalize(
return $resultItems;
}

public function getFieldInheritance(
Concrete $object,
Data $fieldDefinition,
string $key,
?FieldContextData $contextData = null
): array {
$value = $this->getValidFieldValue($object, $key);
if (!$value instanceof Objectbrick) {
return [];
}

$inheritanceData = [];
foreach ($value->getAllowedBrickTypes() as $type) {
$brickGetter = 'get' . $type;
$brick = $value->$brickGetter();
if (!$brick) {
continue;
}

$inheritanceData[$type] = [];
$brickDefinition = $this->definitionResolver->getByKey($type);
if ($brickDefinition === null) {
continue;
}

$contextData = new FieldContextData($brick, $contextData?->getLanguage());
foreach ($brickDefinition->getFieldDefinitions() as $definition) {
$inheritanceData[$type][$definition->getName()] = $this->inheritanceService->processFieldDefinition(
$object,
$definition,
$key,
$contextData
);
}
}

return $inheritanceData;
}

/**
* @throws Exception
*/
Expand Down
31 changes: 31 additions & 0 deletions src/DataObject/Data/DataInheritanceInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);

/**
* Pimcore
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license http://www.pimcore.org/license GPLv3 and PCL
*/

namespace Pimcore\Bundle\StudioBackendBundle\DataObject\Data;

use Pimcore\Bundle\StudioBackendBundle\DataObject\Data\Model\FieldContextData;
use Pimcore\Model\DataObject\ClassDefinition\Data;
use Pimcore\Model\DataObject\Concrete;

interface DataInheritanceInterface
{
public function getFieldInheritance(
Concrete $object,
Data $fieldDefinition,
string $key,
?FieldContextData $contextData = null
): array;
}
13 changes: 13 additions & 0 deletions src/DataObject/Data/Model/FieldContextData.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

use Pimcore\Model\DataObject\Data\BlockElement;
use Pimcore\Model\DataObject\Fieldcollection;
use Pimcore\Model\DataObject\Objectbrick;
use Pimcore\Model\DataObject\Objectbrick\Data\AbstractData;
use function is_array;

Expand Down Expand Up @@ -57,6 +58,18 @@ public function getFieldValueFromContextObject(string $fieldName): mixed
return null;
}

public function getBrickValueFromElement(Objectbrick $brick, string $fieldName): mixed
{
$contextObject = $this->getContextObject();
if (!$contextObject instanceof AbstractData) {
return null;
}
$brickGetter = 'get' . ucfirst($contextObject->getType());
$fieldGetter = 'get' . ucfirst($fieldName);

return $brick->$brickGetter()->$fieldGetter($this->getLanguage());
}

private function getFromBlockData(string $fieldName, array $blockData): mixed
{
foreach ($blockData as $value) {
Expand Down
Loading

0 comments on commit 9ad7478

Please sign in to comment.