268 lines
8.7 KiB
PHP
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();
|
|
});
|
|
}
|
|
}
|