Skip to content

Commit

Permalink
feat: add simple search endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
lukmzig committed Jan 15, 2025
1 parent 32bcee7 commit 05de941
Show file tree
Hide file tree
Showing 18 changed files with 604 additions and 4 deletions.
33 changes: 33 additions & 0 deletions config/search.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
services:
_defaults:
autowire: true
autoconfigure: true
public: false

# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
Pimcore\Bundle\StudioBackendBundle\Search\Controller\:
resource: '../src/Search/Controller'
public: true
tags: [ 'controller.service_arguments' ]

#
# Hydrator
#

Pimcore\Bundle\StudioBackendBundle\Search\Hydrator\SimpleSearchHydratorInterface:
class: Pimcore\Bundle\StudioBackendBundle\Search\Hydrator\SimpleSearchHydrator

#
# Repositories
#

Pimcore\Bundle\StudioBackendBundle\Search\Repository\SearchRepositoryInterface:
class: Pimcore\Bundle\StudioBackendBundle\Search\Repository\SearchRepository

#
# Services
#

Pimcore\Bundle\StudioBackendBundle\Search\Service\SearchServiceInterface:
class: Pimcore\Bundle\StudioBackendBundle\Search\Service\SearchService
3 changes: 2 additions & 1 deletion doc/05_Additional_Custom_Attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,5 @@ final class AssetEvent extends AbstractPreResponseEvent
- `pre_response.custom_report_chart_data`
- `pre_response.custom_report_report`
- `pre_response.custom_report_tree_config_node`
- `pre_response.custom_report_tree_node`
- `pre_response.custom_report_tree_node`
- `pre_response.simple_search.result`
1 change: 0 additions & 1 deletion src/DataIndex/SearchIndexFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public function applyFilters(mixed $parameters, string $type): QueryInterface
}

// apply type specific filters

foreach ($this->getTypeFilters($filters, $type) as $filter) {
$query = $filter->apply($parameters, $query);
}
Expand Down
9 changes: 7 additions & 2 deletions src/Exception/Api/SearchException.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@
namespace Pimcore\Bundle\StudioBackendBundle\Exception\Api;

use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use Throwable;

/**
* @internal
*/
final class SearchException extends AbstractApiException
{
public function __construct(string $type)
public function __construct(string $type, ?Throwable $previous = null)
{
parent::__construct(HttpResponseCodes::BAD_REQUEST->value, 'Search for ' . $type . ' failed');
parent::__construct(
HttpResponseCodes::BAD_REQUEST->value,
sprintf('Search for %s failed', $type),
previous: $previous
);
}
}
14 changes: 14 additions & 0 deletions src/Icon/Service/IconService.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

namespace Pimcore\Bundle\StudioBackendBundle\Icon\Service;

use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Asset\SearchResult\AssetSearchResultItem;
use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\DataObject\SearchResult\DataObjectSearchResultItem;
use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Interfaces\ElementSearchResultItemInterface;
use Pimcore\Bundle\StudioBackendBundle\Response\ElementIcon;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementIconTypes;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\ElementTypes;
Expand All @@ -25,6 +27,18 @@ final class IconService implements IconServiceInterface
{
private string $defaultIcon = 'unknown';

public function getIconForElement(ElementSearchResultItemInterface $resultItem): ElementIcon
{
return match (true) {
$resultItem instanceof AssetSearchResultItem => $this->getIconForAsset(
$resultItem->getType(),
$resultItem->getMimeType()
),
$resultItem instanceof DataObjectSearchResultItem => $this->getIconForDataObject($resultItem),
default => new ElementIcon(ElementIconTypes::NAME->value, $this->defaultIcon)
};
}

public function getIconForAsset(string $assetType, ?string $mimeType): ElementIcon
{
if ($assetType === 'document' && $mimeType !== null) {
Expand Down
3 changes: 3 additions & 0 deletions src/Icon/Service/IconServiceInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
namespace Pimcore\Bundle\StudioBackendBundle\Icon\Service;

use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\DataObject\SearchResult\DataObjectSearchResultItem;
use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Interfaces\ElementSearchResultItemInterface;
use Pimcore\Bundle\StudioBackendBundle\Response\ElementIcon;

interface IconServiceInterface
{
public function getIconForElement(ElementSearchResultItemInterface $resultItem): ElementIcon;

public function getIconForAsset(string $assetType, string $mimeType): ElementIcon;

public function getIconForDataObject(DataObjectSearchResultItem $dataObject): ElementIcon;
Expand Down
5 changes: 5 additions & 0 deletions src/OpenApi/Config/Tags.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@
name: Tags::Schedule->value,
description: 'tag_schedule_description'
)]
#[Tag(
name: Tags::Search->value,
description: 'tag_search_description'
)]
#[Tag(
name: Tags::Settings->value,
description: 'tag_settings_description'
Expand Down Expand Up @@ -154,6 +158,7 @@ enum Tags: string
case Notifications = 'Notifications';
case Properties = 'Properties';
case Role = 'Role Management';
case Search = 'Search';
case Schedule = 'Schedule';
case Settings = 'Settings';
case Tags = 'Tags';
Expand Down
89 changes: 89 additions & 0 deletions src/Search/Controller/SimpleController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?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\Search\Controller;

use OpenApi\Attributes\Get;
use Pimcore\Bundle\StudioBackendBundle\Controller\AbstractApiController;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\SearchException;
use Pimcore\Bundle\StudioBackendBundle\Exception\Api\UserNotFoundException;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Query\PageParameter;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Query\PageSizeParameter;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Parameter\Query\TextFieldParameter;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Property\GenericCollection;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\Content\CollectionJson;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\DefaultResponses;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Attribute\Response\SuccessResponse;
use Pimcore\Bundle\StudioBackendBundle\OpenApi\Config\Tags;
use Pimcore\Bundle\StudioBackendBundle\Search\MappedParameter\SimpleSearchParameter;
use Pimcore\Bundle\StudioBackendBundle\Search\Schema\SimpleSearchResult;
use Pimcore\Bundle\StudioBackendBundle\Search\Service\SearchServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\Util\Constant\HttpResponseCodes;
use Pimcore\Bundle\StudioBackendBundle\Util\Trait\PaginatedResponseTrait;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Attribute\MapQueryString;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Serializer\SerializerInterface;

/**
* @internal
*/
final class SimpleController extends AbstractApiController
{
use PaginatedResponseTrait;

private const ROUTE = '/search';

public function __construct(
SerializerInterface $serializer,
private readonly SearchServiceInterface $searchService,
) {
parent::__construct($serializer);
}

/**
* @throws SearchException|UserNotFoundException
*/
#[Route(path: self::ROUTE, name: 'pimcore_studio_api_search', methods: ['GET'])]
#[Get(
path: self::PREFIX . self::ROUTE,
operationId: 'simple_search_get',
description: 'simple_search_get_description',
summary: 'simple_search_get_summary',
tags: [Tags::Search->name]
)]
#[PageParameter]
#[PageSizeParameter]
#[TextFieldParameter(name: 'searchTerm', description: 'simple_search_get_search_term_parameter', required: false)]
#[SuccessResponse(
description: 'simple_search_get_success_response',
content: new CollectionJson(new GenericCollection(SimpleSearchResult::class))
)]
#[DefaultResponses([
HttpResponseCodes::UNAUTHORIZED,
HttpResponseCodes::BAD_REQUEST,
])]
public function doSimpleSearch(#[MapQueryString] SimpleSearchParameter $parameters): JsonResponse
{
$collection = $this->searchService->doSimpleSearch($parameters);

return $this->getPaginatedCollection(
$this->serializer,
$collection->getItems(),
$collection->getTotalItems()
);
}
}
46 changes: 46 additions & 0 deletions src/Search/Event/PreResponse/SimpleSearchResultEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?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\Search\Event\PreResponse;

use Pimcore\Bundle\StudioBackendBundle\Element\Schema\CustomAttributes;
use Pimcore\Bundle\StudioBackendBundle\Event\AbstractPreResponseEvent;
use Pimcore\Bundle\StudioBackendBundle\Search\Schema\SimpleSearchResult;

final class SimpleSearchResultEvent extends AbstractPreResponseEvent
{
public const EVENT_NAME = 'pre_response.simple_search.result';

public function __construct(private readonly SimpleSearchResult $result)
{
parent::__construct($this->result);
}

public function getSimpleSearchResult(): SimpleSearchResult
{
return $this->result;
}

public function getCustomAttributes(): ?CustomAttributes
{
return $this->result->getCustomAttributes();
}

public function setCustomAttributes(CustomAttributes $customAttributes): void
{
$this->result->setCustomAttributes($customAttributes);
}
}
46 changes: 46 additions & 0 deletions src/Search/Hydrator/SimpleSearchHydrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?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\Search\Hydrator;

use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Interfaces\ElementSearchResultItemInterface;
use Pimcore\Bundle\StudioBackendBundle\Icon\Service\IconServiceInterface;
use Pimcore\Bundle\StudioBackendBundle\Search\Schema\SimpleSearchResult;
use Pimcore\Bundle\StudioBackendBundle\Util\Trait\ElementProviderTrait;

/**
* @internal
*/
final readonly class SimpleSearchHydrator implements SimpleSearchHydratorInterface
{
use ElementProviderTrait;

public function __construct(
private IconServiceInterface $iconService
) {
}

public function hydrate(ElementSearchResultItemInterface $resultItem): SimpleSearchResult
{
return new SimpleSearchResult(
$resultItem->getId(),
$resultItem->getElementType()->value,
$resultItem->getType(),
$resultItem->getFullPath(),
$this->iconService->getIconForElement($resultItem)
);
}
}
28 changes: 28 additions & 0 deletions src/Search/Hydrator/SimpleSearchHydratorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?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\Search\Hydrator;

use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Interfaces\ElementSearchResultItemInterface;
use Pimcore\Bundle\StudioBackendBundle\Search\Schema\SimpleSearchResult;

/**
* @internal
*/
interface SimpleSearchHydratorInterface
{
public function hydrate(ElementSearchResultItemInterface $resultItem): SimpleSearchResult;
}
40 changes: 40 additions & 0 deletions src/Search/MappedParameter/SimpleSearchParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?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\Search\MappedParameter;

use Pimcore\Bundle\StudioBackendBundle\MappedParameter\CollectionParameters;

/**
* @internal
*/
final readonly class SimpleSearchParameter extends CollectionParameters
{
public function __construct(
int $page = 1,
int $pageSize = 50,
private ?string $searchTerm = null,
) {
parent::__construct($page, $pageSize);
}

public function getSearchTerm(): ?string
{
return $this->searchTerm;
}

}
Loading

0 comments on commit 05de941

Please sign in to comment.