Skip to content

Commit

Permalink
Add ImageMap, Collection Property Resolver and ResourceLoader (#4)
Browse files Browse the repository at this point in the history
* Add ImageMap Property Resolver

* Add single collection and multi collection property resolver
  • Loading branch information
alexander-schranz authored and Prokyonn committed Nov 5, 2024
1 parent 62239fe commit 9195430
Show file tree
Hide file tree
Showing 8 changed files with 860 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

/*
* This file is part of Sulu.
*
* (c) Sulu GmbH
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Sulu\Bundle\MediaBundle\Infrastructure\Sulu\Content\PropertyResolver;

use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Value\ContentView;
use Sulu\Bundle\ContentBundle\Content\Application\PropertyResolver\PropertyResolverInterface;
use Sulu\Bundle\MediaBundle\Infrastructure\Sulu\Content\ResourceLoader\CollectionResourceLoader;

/**
* @internal if you need to override this service, create a new service with based on PropertyResolverInterface instead of extending this class
*
* @final
*/
class CollectionSelectionPropertyResolver implements PropertyResolverInterface
{
public function resolve(mixed $data, string $locale, array $params = []): ContentView
{
if (!\is_array($data)
|| 0 === \count($data)
|| !\array_is_list($data)
) {
return ContentView::create([], ['ids' => [], ...$params]);
}

/** @var string $resourceLoaderKey */
$resourceLoaderKey = $params['resourceLoader'] ?? CollectionResourceLoader::getKey();

return ContentView::createResolvables(
$data,
$resourceLoaderKey,
['ids' => $data, ...$params],
);
}

public static function getType(): string
{
return 'collection_selection';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php

declare(strict_types=1);

/*
* This file is part of Sulu.
*
* (c) Sulu GmbH
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Sulu\Bundle\MediaBundle\Infrastructure\Sulu\Content\PropertyResolver;

use Psr\Log\LoggerInterface;
use Sulu\Bundle\AdminBundle\Metadata\FormMetadata\FieldMetadata;
use Sulu\Bundle\AdminBundle\Metadata\FormMetadata\FormMetadata;
use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Value\ContentView;
use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Value\ResolvableResource;
use Sulu\Bundle\ContentBundle\Content\Application\MetadataResolver\MetadataResolver;
use Sulu\Bundle\ContentBundle\Content\Application\PropertyResolver\PropertyResolverInterface;
use Sulu\Bundle\MediaBundle\Infrastructure\Sulu\Content\ResourceLoader\MediaResourceLoader;

/**
* @internal if you need to override this service, create a new service with based on ResourceLoaderInterface instead of extending this class
*
* @final
*/
class ImageMapPropertyResolver implements PropertyResolverInterface // TODO we may should implement a PropertyResolverAwareMetadataInterface
{
private MetadataResolver $metadataResolver;

public function __construct(
private readonly LoggerInterface $logger,
private readonly bool $debug = false,
) {
}

/**
* @internal
*
* Prevent circular dependency by injecting the MetadataResolver after instantiation
*/
public function setMetadataResolver(MetadataResolver $metadataResolver): void
{
$this->metadataResolver = $metadataResolver;
}

public function resolve(mixed $data, string $locale, array $params = []): ContentView
{
$hotspots = (\is_array($data) && isset($data['hotspots']) && \is_array($data['hotspots'])) && \array_is_list($data['hotspots'])
? $data['hotspots']
: [];

$hotspots = [] !== $hotspots ? $this->resolveHotspots($hotspots, $locale, $params) : ContentView::create([], []);

$returnedParams = $params;
unset($returnedParams['metadata']); // TODO we may should implement a PropertyResolverAwareMetadataInterface

if (!\is_array($data)
|| !isset($data['imageId'])
|| !\is_numeric($data['imageId'])
) {
return ContentView::create([
'image' => null,
'hotspots' => $hotspots->getContent(),
], [
'imageId' => null,
'hotspots' => $hotspots->getView(),
...$returnedParams,
]);
}

/** @var string $resourceLoaderKey */
$resourceLoaderKey = $params['resourceLoader'] ?? MediaResourceLoader::getKey();
$imageId = (int) $data['imageId'];

return ContentView::create(
[
'image' => new ResolvableResource($imageId, $resourceLoaderKey),
'hotspots' => $hotspots->getContent(),
],
[
'imageId' => $imageId,
'hotspots' => $hotspots->getView(),
...$returnedParams,
],
);
}

/**
* @param non-empty-array<array<mixed>> $hotspots
* @param array<string, mixed> $params
*/
private function resolveHotspots(array $hotspots, string $locale, array $params): ContentView
{
$metadata = $params['metadata'] ?? null;
\assert($metadata instanceof FieldMetadata, 'Metadata must be set to resolve hotspots.');
$metadataTypes = $metadata->getTypes();
$content = [];
$view = [];
foreach ($hotspots as $key => $block) {
if (!\is_array($block) || !isset($block['type']) || !\is_string($block['type'])) {
continue;
}
if (!isset($block['hotspot']) || !\is_array($block['hotspot'])) {
continue;
}

$type = $block['type'];
$formMetadata = $metadataTypes[$type] ?? null;

if (!$formMetadata instanceof FormMetadata) {
$errorMessage = \sprintf(
'Metadata type "%s" in "%s" not found, founded types are: "%s"',
$type,
$metadata->getName(),
\implode('", "', \array_keys($metadataTypes)),
);

$this->logger->error($errorMessage);

if ($this->debug) {
throw new \UnexpectedValueException($errorMessage);
}

$type = $metadata->getDefaultType();
$formMetadata = $metadataTypes[$type] ?? null;
if (!$formMetadata instanceof FormMetadata) {
continue;
}
}

$content[$key] = [
'type' => $type,
'hotspot' => $block['hotspot'],
];

$view[$key] = [];

foreach ($this->metadataResolver->resolveItems($formMetadata->getItems(), $block, $locale) as $field => $resolvedItem) {
$content[$key][$field] = $resolvedItem->getContent();
$view[$key][$field] = $resolvedItem->getView();
}
}

return ContentView::create(\array_values($content), \array_values($view));
}

public static function getType(): string
{
return 'image_map';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

/*
* This file is part of Sulu.
*
* (c) Sulu GmbH
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Sulu\Bundle\MediaBundle\Infrastructure\Sulu\Content\PropertyResolver;

use Sulu\Bundle\ContentBundle\Content\Application\ContentResolver\Value\ContentView;
use Sulu\Bundle\ContentBundle\Content\Application\PropertyResolver\PropertyResolverInterface;
use Sulu\Bundle\MediaBundle\Infrastructure\Sulu\Content\ResourceLoader\CollectionResourceLoader;

/**
* @internal if you need to override this service, create a new service with based on PropertyResolverInterface instead of extending this class
*
* @final
*/
class SingleCollectionSelectionPropertyResolver implements PropertyResolverInterface
{
public function resolve(mixed $data, string $locale, array $params = []): ContentView
{
if (!\is_numeric($data)) {
return ContentView::create(null, ['id' => null, ...$params]);
}

/** @var string $resourceLoaderKey */
$resourceLoaderKey = $params['resourceLoader'] ?? CollectionResourceLoader::getKey();

return ContentView::createResolvable(
(int) $data,
$resourceLoaderKey,
[
'id' => $data,
...$params,
],
);
}

public static function getType(): string
{
return 'single_collection_selection';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

/*
* This file is part of Sulu.
*
* (c) Sulu GmbH
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Sulu\Bundle\MediaBundle\Infrastructure\Sulu\Content\ResourceLoader;

use Sulu\Bundle\ContentBundle\Content\Application\ResourceLoader\ResourceLoaderInterface;
use Sulu\Bundle\MediaBundle\Collection\Manager\CollectionManagerInterface;
use Sulu\Bundle\MediaBundle\Media\Exception\CollectionNotFoundException;

/**
* @internal if you need to override this service, create a new service with based on ResourceLoaderInterface instead of extending this class
*
* @final
*/
class CollectionResourceLoader implements ResourceLoaderInterface
{
public const RESOURCE_LOADER_KEY = 'collection';

public function __construct(
private CollectionManagerInterface $collectionManager,
) {
}

public function load(array $ids, ?string $locale, array $params = []): array
{
$mappedResult = [];
foreach ($ids as $id) {
try {
$collection = $this->collectionManager->getById($id, $locale); // TODO load all over one query
$mappedResult[$collection->getId()] = $collection;
} catch (CollectionNotFoundException $e) {
// @ignoreException: do not crash page if selected collection is deleted
}
}

return $mappedResult;
}

public static function getKey(): string
{
return self::RESOURCE_LOADER_KEY;
}
}
Loading

0 comments on commit 9195430

Please sign in to comment.