<?php
namespace Millerdigital\EleqPimcore\Controller;
use Millerdigital\EleqPimcore\Formatter\ProductFormatter;
use Millerdigital\EleqPimcore\Service\FilterService;
use Pimcore\Model\Asset;
use Pimcore\Model\DataObject\Contact;
use Pimcore\Model\DataObject\Product;
use Pimcore\Model\Element\Tag;
use Pimcore\Model\DataObject;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\Polyfill\Intl\Icu\Locale;
class ApiController extends AbstractController {
private TranslatorInterface $translator;
public function __construct(
TranslatorInterface $translator,
) {
$this->translator = $translator;
}
#[Route('/api/product/{slug}', name: 'product')]
public function fetchProduct(Request $request, string $slug): Response
{
$language = self::getRequestLanguage($request);
$this->translator->setlocale($language);
$product = Product::getBySlug($slug, $language, 1);
if ($product && $product->getPath() == "/Products/" &&
($request->query->get('public') === null || $product->getShowOnWebsite($language))) {
$product = ProductFormatter::formatProductDetail($product, $this->translator, $request);
return $this->json($product, 200, ["Content-Type" => "application/json"]);
}
throw $this->createNotFoundException();
}
#[Route('/api/products/{offset}/{limit}', name: 'products')]
public function fetchProducts(Request $request, $offset = 0, $limit = 20): Response
{
$language = self::getRequestLanguage($request);
$this->translator->setlocale($language);
$productListing = new Product\Listing();
$productListing->setLocale($language);
$productListing->addConditionParam('o_path = ?', '/Products/');
// sort by Order product-property
$productListing->setOrderKey(
['order', 'oo_id',]
);
$productListing->setOrder(
['desc', 'asc']
);
FilterService::applyFilters($productListing, $request->query->all());
$filters = FilterService::getFilters($productListing->load(), $this->translator);
$products = $productListing->getItems($offset, $limit);
$products = array_map(fn(Product $product) => ProductFormatter::formatProduct($product, $this->translator, $request), $products);
$count = $productListing->count();
$result = [
'count' => $count,
'filters' => $filters,
'products' => $products
];
if (!$count) {
$contact = Contact::getByELEQID('NPFR');
if ($contact) {
$result['contact'] = ProductFormatter::formatContact($contact, $language, $request);
}
}
return $this->json($result, 200, ["Content-Type" => "application/json"]);
}
#[Route('/api/search', name: 'search')]
public function searchProducts(Request $request): Response
{
$language = self::getRequestLanguage($request);
$this->translator->setlocale($language);
$productListing = new Product\Listing();
$productListing->setLocale($language);
$searchQuery = preg_replace('/[^a-zA-Z0-9]/', '', $_REQUEST['query']);
$condition = 'REPLACE(REGEXP_REPLACE(%s, \'[^0-9a-zA-Z ]\', \'\'), " ", "") like "%%' . $searchQuery . '%%"';
$conditions = array_map(fn($field) => sprintf($condition, $field), ['Name', 'modelName']);
$productListing->addConditionParam('(' . implode(' OR ', $conditions) . ')');
$limit = isset($_REQUEST['limit']) ? intval($_REQUEST['limit']) : 0;
if(isset($_REQUEST['public'])) $productListing->addConditionParam('ShowOnWebsite');
$products = self::postProcessSearch($productListing->getItems(0, 100));
if (!$limit || sizeof($products) < $limit) {
$relatedProducts = array_map(fn(Product $product) => $product->getRelatedProducts(), $products);
$productListing = new Product\Listing();
$productListing->addConditionParam('RelatedProducts REGEXP ?', [',' . implode('|', array_keys($products)) . ',']);
$relatedProducts = self::postProcessSearch(array_merge(...$relatedProducts, ...[$productListing->load()]));
$products = array_merge($products, $relatedProducts);
}
$tagListing = new Tag\Listing();
$tagListing->addConditionParam(sprintf($condition, 'Name'));
$objects = array_map(fn(Tag $tag) => Tag::getElementsForTag($tag, 'object'), $tagListing->load());
$taggedProducts = array_filter(array_merge(...$objects), fn(DataObject $object) => $object instanceof Product);
$products = self::postProcessSearch(array_merge($products, $taggedProducts));
$products = array_map(fn(Product $product) => ProductFormatter::formatProduct($product, $this->translator, $request), $products);
$assets = array_map(fn(Tag $tag) => Tag::getElementsForTag($tag, 'asset'), $tagListing->load());
$assets = ProductFormatter::formatDocuments(array_merge(...$assets), $language);
$searchResults = array_merge($products, $assets);
$searchResults = $limit ? array_slice($searchResults, 0, $limit) : $searchResults;
return $this->json($searchResults);
}
#[Route('/api/document/{file}/download', name: 'document')]
public function fetchDocument(string $file): Response
{
$assetListing = new Asset\Listing();
$assetListing->addConditionParam('filename = ?', $file);
$asset = $assetListing->getItems(0, 1)[0] ?? null;
if (!$asset) {
throw $this->createNotFoundException();
}
$stream = $asset->getStream();
return new StreamedResponse(function () use ($stream) {
fpassthru($stream);
}, 200, [
'Content-Type' => $asset->getMimeType(),
'Content-Disposition' => sprintf('attachment; filename="%s"', $asset->getFilename()),
'Content-Length' => $asset->getFileSize(),
]);
}
private static function getRequestLanguage(Request $request): ?string
{
if ($language = $request->query->get('lang')) {
return $language;
}
if ($language = $request->getPreferredLanguage()) {
return substr($language, 0, strrpos($language, '_'));
}
return Locale::getDefault();
}
private static function postProcessSearch(array $products): array
{
$products = array_map(fn(Product $product) => is_a($product->getParent(), Product::class) ? $product->getParent() : $product, $products);
return array_combine(array_map(fn(Product $product) => $product->getId(), $products), $products);
}
}