Files
kupshop/bundles/KupShop/GraphQLBundle/ApiShared/ApiUtil.php
2025-08-02 16:30:27 +02:00

268 lines
8.7 KiB
PHP

<?php
declare(strict_types=1);
namespace KupShop\GraphQLBundle\ApiShared;
use KupShop\GraphQLBundle\ApiAdmin\Types\DateFilterInput;
use KupShop\GraphQLBundle\ApiAdmin\Types\Parameters;
use KupShop\GraphQLBundle\ApiShared\Types\Country;
use KupShop\GraphQLBundle\ApiShared\Types\Enums\SortEnum;
use KupShop\GraphQLBundle\ApiShared\Types\Language;
use KupShop\GraphQLBundle\ApiShared\Types\Price\Currency;
use KupShop\GraphQLBundle\Contracts\AbstractDataType;
use KupShop\GraphQLBundle\Exception\GraphQLValidationException;
use KupShop\I18nBundle\Translations\BlocksTranslation;
use KupShop\KupShopBundle\Context\CountryContext;
use KupShop\KupShopBundle\Context\CurrencyContext;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\KupShopBundle\Util\ObjectUtil;
use Query\Operator;
use Query\QueryBuilder;
use Query\Translation;
use Symfony\Component\DependencyInjection\Container;
class ApiUtil
{
private static array $dataTypeClassNames = [];
public static function getDataTypeClassName(string $originalClassName): string
{
if (static::$dataTypeClassNames[$originalClassName] ?? null) {
return static::$dataTypeClassNames[$originalClassName];
}
$className = $originalClassName;
if ($service = ServiceContainer::getService($originalClassName, Container::NULL_ON_INVALID_REFERENCE)) {
$className = $service::class;
}
return static::$dataTypeClassNames[$originalClassName] = $className;
}
public static function wrapItems(iterable $items, string $wrapper): array
{
$result = [];
foreach ($items as $item) {
$result[] = new $wrapper($item);
}
return $result;
}
public static function getLimit(int $currentLimit, int $maxLimit = 1000): int
{
return min($currentLimit, $maxLimit);
}
public static function getLimitSpec(int $offset, int $limit, int $maxLimit = 1000): callable
{
return function (QueryBuilder $qb) use ($limit, $offset, $maxLimit) {
$qb->setFirstResult($offset)
->setMaxResults(static::getLimit($limit, $maxLimit));
};
}
public static function getSortSpec(?Parameters $sort, ?string $alias = null): ?callable
{
if (!$sort) {
return null;
}
return function (QueryBuilder $qb) use ($sort, $alias) {
/** @var SortEnum $value */
foreach (array_filter($sort->getData()) as $field => $value) {
$column = $alias ? "{$alias}.{$field}" : $field;
$qb->orderBy($column, $value->value);
}
};
}
public static function getDateTimeFilter(DateFilterInput $filterInput, string $field): callable
{
static $counter = 0;
return function (QueryBuilder $qb) use ($filterInput, $field, &$counter) {
$e = $qb->expr();
$operators = [];
foreach (array_filter(get_object_vars($filterInput)) as $operator => $date) {
$parameterName = 'gql_dateFilter_'.$operator.'_'.$counter++;
$filterDate = static::prepareDateTimeForDB($date);
$qb->setParameter($parameterName, $filterDate);
switch ($operator) {
case 'eq':
$operators[] = Operator::equals([$field => $filterDate]);
break;
case 'lt':
$operators[] = $e->lt($field, ':'.$parameterName);
break;
case 'le':
$operators[] = $e->lte($field, ':'.$parameterName);
break;
case 'gt':
$operators[] = $e->gt($field, ':'.$parameterName);
break;
case 'ge':
$operators[] = $e->gte($field, ':'.$parameterName);
break;
}
}
return Operator::andX($operators);
};
}
public static function prepareDateTime(\DateTimeInterface $dateTime): \DateTimeImmutable
{
if (!($dateTime instanceof \DateTime) && !($dateTime instanceof \DateTimeImmutable)) {
throw new \InvalidArgumentException('Argument $dateTime must be instance of DateTime or DateTimeImmutable!');
}
if ($dateTime instanceof \DateTime) {
return \DateTimeImmutable::createFromMutable($dateTime);
}
return $dateTime;
}
public static function prepareDateTimeFromDB(string|\DateTimeInterface|null $dateTime, string $format = 'Y-m-d H:i:s'): ?\DateTimeImmutable
{
if (!$dateTime) {
return null;
}
if ($dateTime instanceof \DateTimeInterface) {
if ($dateTime instanceof \DateTime) {
$date = \DateTimeImmutable::createFromMutable($dateTime);
} else {
$date = $dateTime;
}
} else {
if (!($date = \DateTimeImmutable::createFromFormat($format, $dateTime))) {
$date = null;
}
}
return $date;
}
public static function prepareDateTimeForDB(?\DateTimeInterface $dateTime): string
{
if (!$dateTime) {
return '';
}
return $dateTime->format(\DateTime::ISO8601);
}
public static function prepareCurrency(string $currency): Currency
{
$currencyContext = Contexts::get(CurrencyContext::class);
return new Currency(
$currencyContext->getAll()[$currency]
);
}
public static function prepareLanguage(string $language): Language
{
$languageContext = Contexts::get(LanguageContext::class);
return new Language(
$languageContext->getAll()[$language]
);
}
public static function prepareCountry(string $country): Country
{
$countryContext = Contexts::get(CountryContext::class);
if (empty($country)) {
$country = array_keys($countryContext->getAll())[0];
}
return new Country(
$countryContext->getAll()[$country]
);
}
public static function validateTranslationLanguage(string $language): void
{
$languageContext = Contexts::get(LanguageContext::class);
if (!isset($languageContext->getAll()[$language])) {
throw new GraphQLValidationException(
sprintf('Language "%s" does not exists!', $language)
);
}
if ($languageContext->getDefaultId() === $language) {
throw new GraphQLValidationException(
sprintf('Cannot update translations of language "%s"! Language "%s" is default language.', $language, $language)
);
}
}
public static function validateObjectRequiredProperties(object $object): void
{
$properties = ObjectUtil::getUninitializedRequiredProperties($object);
if (!empty($properties)) {
throw new GraphQLValidationException('Some mandatory fields are not filled in: ['.implode(', ', $properties).']');
}
}
public static function fetchBlocksByRootIds(array $rootBlockIds): array
{
if (empty($rootBlockIds)) {
return [];
}
$qb = sqlQueryBuilder()
->select('b.*')
->from('blocks', 'b')
->andWhere(Operator::inIntArray($rootBlockIds, 'b.id_root'))
->andWhere(Translation::coalesceTranslatedFields(BlocksTranslation::class))
->orderBy('b.id_root, b.position, b.id_parent, b.id');
$data = [];
foreach ($qb->execute() as $row) {
$data[$row['id_root']] = $data[$row['id_root']] ?? [];
$data[$row['id_root']][] = $row;
}
return $data;
}
public static function updateObjectCustomData(string $tableName, int $objectId, ?AbstractDataType $data = null, string $fieldName = 'data'): void
{
if (empty($data)) {
return;
}
sqlGetConnection()->transactional(function () use ($tableName, $objectId, $data, $fieldName) {
$currentData = sqlQueryBuilder()
->select($fieldName)
->from($tableName)
->where(Operator::equals(['id' => $objectId]))
->execute()->fetchOne();
$currentData = json_decode($currentData ?: '', true) ?: [];
$customData = array_merge($currentData, $data->asArray());
sqlQueryBuilder()
->update($tableName)
->directValues([$fieldName => json_encode($customData)])
->where(Operator::equals(['id' => $objectId]))
->execute();
});
}
}