feat: image processing improve

This commit is contained in:
2025-12-06 12:59:55 +03:00
parent 4e416ead49
commit 38668fb4a7
37 changed files with 411 additions and 366 deletions

View File

@@ -5,9 +5,6 @@ use Cart\User;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use Openguru\OpenCartFramework\Application;
use Openguru\OpenCartFramework\Http\Response as HttpResponse;
use Openguru\OpenCartFramework\ImageTool\ImageTool;
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator;
use Openguru\OpenCartFramework\Support\Arr;
use Psr\Log\LoggerInterface;
@@ -114,6 +111,7 @@ class ControllerExtensionModuleTgshop extends Controller
$data['customer_groups'] = $this->getCustomerGroups();
$data['themes'] = self::$themes;
$data['telecart_module_version'] = module_version();
$data['shop_base_url'] = HTTPS_CATALOG;
$data['action'] = $this->url->link(
'extension/module/tgshop',
@@ -128,21 +126,9 @@ class ControllerExtensionModuleTgshop extends Controller
{
$logger = $this->createLogger();
try {
$this
->createApplication($logger)
->bootAndHandleRequest();
} catch (Throwable $e) {
$logger->error($e->getMessage(), ['exception' => $e]);
http_response_code(HttpResponse::HTTP_INTERNAL_SERVER_ERROR);
header('Content-Type: application/json');
echo json_encode([
'error' => $e->getMessage(),
'code' => $e->getCode(),
'file' => $e->getFile(),
'line' => $e->getLine(),
], JSON_THROW_ON_ERROR);
}
$this
->createApplication($logger)
->bootAndHandleRequest();
}
protected function validate(): bool
@@ -170,31 +156,6 @@ class ControllerExtensionModuleTgshop extends Controller
$data = array_merge($data, $this->error);
$data['breadcrumbs'] = array();
$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_home'),
'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true)
);
$data['breadcrumbs'][] = array(
'text' => $this->language->get('text_module'),
'href' => $this->url->link(
'marketplace/extension',
'user_token=' . $this->session->data['user_token'] . '&type=module',
true
)
);
$data['breadcrumbs'][] = array(
'text' => $this->language->get('heading_title'),
'href' => $this->url->link(
'extension/module/tgshop',
'user_token=' . $this->session->data['user_token'],
true
)
);
if (isset($this->session->data['success'])) {
$data['success'] = $this->session->data['success'];
@@ -255,6 +216,9 @@ class ControllerExtensionModuleTgshop extends Controller
'shop_base_url' => HTTPS_CATALOG, // for catalog: HTTPS_SERVER, for admin: HTTPS_CATALOG
'language_id' => (int) $this->config->get('config_language_id'),
],
'paths' => [
'images' => DIR_IMAGE,
],
'logs' => [
'path' => DIR_LOGS,
],
@@ -281,7 +245,6 @@ class ControllerExtensionModuleTgshop extends Controller
$app = ApplicationFactory::create($items);
$app->bind(OcRegistryDecorator::class, fn() => new OcRegistryDecorator($this->registry));
$app->bind(ImageToolInterface::class, fn() => new ImageTool(DIR_IMAGE, HTTPS_SERVER));
$app->setLogger($logger);
return $app;

View File

@@ -18,9 +18,9 @@
<script>
window.TeleCart = {
user_token: '{{ user_token }}',
mainpage_slider: '{{ mainpage_slider }}',
themes: '{{ themes | json_encode }}',
order_statuses: '{{ order_statuses | json_encode }}',
shop_base_url: '{{ shop_base_url }}',
};
</script>
<div id="app" class="telecart-admin-app">App Loading...</div>

View File

@@ -7,8 +7,6 @@ use Cart\Currency;
use Cart\Tax;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use Openguru\OpenCartFramework\ImageTool\ImageTool;
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
use Openguru\OpenCartFramework\OpenCart\Currency as TelecartCurrency;
use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator;
use Openguru\OpenCartFramework\Support\Arr;
@@ -54,6 +52,9 @@ class ControllerExtensionTgshopHandle extends Controller
'language_id' => (int)$this->config->get('config_language_id'),
'oc_timezone' => $this->config->get('config_timezone'),
],
'paths' => [
'images' => DIR_IMAGE,
],
'logs' => [
'path' => DIR_LOGS,
],
@@ -86,7 +87,6 @@ class ControllerExtensionTgshopHandle extends Controller
$app->bind(Url::class, fn() => $this->url);
$app->bind(Currency::class, fn() => new TelecartCurrency($this->registry));
$app->bind(Tax::class, fn() => $this->tax);
$app->bind(ImageToolInterface::class, fn() => new ImageTool(DIR_IMAGE, HTTPS_SERVER));
$app->bind(Cart::class, fn() => $this->cart);
$app->bind(OcRegistryDecorator::class, fn() => new OcRegistryDecorator($this->registry));
$app->singleton(Log::class, fn() => $this->log);

View File

@@ -8,6 +8,7 @@ use App\ServiceProviders\AppServiceProvider;
use App\ServiceProviders\SettingsServiceProvider;
use Openguru\OpenCartFramework\Application;
use Openguru\OpenCartFramework\Cache\CacheServiceProvider;
use Openguru\OpenCartFramework\ImageTool\ImageToolServiceProvider;
use Openguru\OpenCartFramework\QueryBuilder\QueryBuilderServiceProvider;
use Openguru\OpenCartFramework\Router\RouteServiceProvider;
use Openguru\OpenCartFramework\Support\Arr;
@@ -33,6 +34,7 @@ class ApplicationFactory
CacheServiceProvider::class,
TelegramServiceProvider::class,
TeleCartPulseServiceProvider::class,
ImageToolServiceProvider::class,
]);
}
}

View File

@@ -3,22 +3,19 @@
namespace Bastion\Handlers;
use App\Services\SettingsService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
use Symfony\Component\HttpFoundation\JsonResponse;
class DictionariesHandler
{
private Builder $queryBuilder;
private ImageToolInterface $ocImageTool;
private SettingsService $settings;
public function __construct(Builder $queryBuilder, ImageToolInterface $ocImageTool, SettingsService $settings)
public function __construct(Builder $queryBuilder, SettingsService $settings)
{
$this->queryBuilder = $queryBuilder;
$this->ocImageTool = $ocImageTool;
$this->settings = $settings;
}

View File

@@ -6,7 +6,7 @@ use JsonException;
use Openguru\OpenCartFramework\Exceptions\EntityNotFoundException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
class FormsHandler

View File

@@ -10,7 +10,7 @@ use Openguru\OpenCartFramework\Cache\CacheInterface;
use Openguru\OpenCartFramework\Config\Settings;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
use Openguru\OpenCartFramework\Support\Arr;

View File

@@ -9,7 +9,7 @@ use GuzzleHttp\Exception\GuzzleException;
use Openguru\OpenCartFramework\Cache\CacheInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
use Openguru\OpenCartFramework\Support\Arr;
use Openguru\OpenCartFramework\Telegram\Exceptions\TelegramClientException;
use Openguru\OpenCartFramework\Telegram\TelegramService;

View File

@@ -4,7 +4,6 @@ namespace Openguru\OpenCartFramework\Container;
use Openguru\OpenCartFramework\Events\EventDispatcher;
use Openguru\OpenCartFramework\Exceptions\ContainerDependencyResolutionException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Support\Arr;
use Phar;
use Psr\Container\ContainerInterface;
@@ -13,6 +12,7 @@ use ReflectionException;
use ReflectionMethod;
use ReflectionNamedType;
use RuntimeException;
use Symfony\Component\HttpFoundation\Response;
// phpcs:disable PSR1.Files.SideEffects
if (! defined('BP_BASE_PATH')) {
@@ -118,7 +118,7 @@ class Container implements ContainerInterface
* @param class-string $abstract
* @throws ReflectionException
*/
public function call(string $abstract, string $method): JsonResponse
public function call(string $abstract, string $method): Response
{
if (! class_exists($abstract)) {
throw new ContainerDependencyResolutionException('Could not resolve the concrete: ' . $abstract);

View File

@@ -5,10 +5,11 @@ namespace Openguru\OpenCartFramework;
use ErrorException;
use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface;
use Openguru\OpenCartFramework\Exceptions\ActionNotFoundException;
use Openguru\OpenCartFramework\Exceptions\HttpNotFoundException;
use Openguru\OpenCartFramework\Exceptions\InvalidApiTokenException;
use Openguru\OpenCartFramework\Exceptions\NonLoggableExceptionInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
use Psr\Log\LoggerInterface;
use Throwable;
@@ -72,6 +73,13 @@ class ErrorHandler
exit(1);
}
if ($exception instanceof HttpNotFoundException) {
(new JsonResponse([
'message' => $exception->getMessage(),
], Response::HTTP_NOT_FOUND))->send();
return;
}
if ($exception instanceof InvalidApiTokenException) {
(new JsonResponse([
'message' => $exception->getMessage(),

View File

@@ -0,0 +1,10 @@
<?php
namespace Openguru\OpenCartFramework\Exceptions;
use Exception;
class HttpNotFoundException extends Exception
{
}

View File

@@ -1,70 +0,0 @@
<?php
namespace Openguru\OpenCartFramework\Http;
class Response
{
public const HTTP_CONTINUE = 100;
public const HTTP_SWITCHING_PROTOCOLS = 101;
public const HTTP_PROCESSING = 102; // RFC2518
public const HTTP_EARLY_HINTS = 103; // RFC8297
public const HTTP_OK = 200;
public const HTTP_CREATED = 201;
public const HTTP_ACCEPTED = 202;
public const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
public const HTTP_NO_CONTENT = 204;
public const HTTP_RESET_CONTENT = 205;
public const HTTP_PARTIAL_CONTENT = 206;
public const HTTP_MULTI_STATUS = 207; // RFC4918
public const HTTP_ALREADY_REPORTED = 208; // RFC5842
public const HTTP_IM_USED = 226; // RFC3229
public const HTTP_MULTIPLE_CHOICES = 300;
public const HTTP_MOVED_PERMANENTLY = 301;
public const HTTP_FOUND = 302;
public const HTTP_SEE_OTHER = 303;
public const HTTP_NOT_MODIFIED = 304;
public const HTTP_USE_PROXY = 305;
public const HTTP_RESERVED = 306;
public const HTTP_TEMPORARY_REDIRECT = 307;
public const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238
public const HTTP_BAD_REQUEST = 400;
public const HTTP_UNAUTHORIZED = 401;
public const HTTP_PAYMENT_REQUIRED = 402;
public const HTTP_FORBIDDEN = 403;
public const HTTP_NOT_FOUND = 404;
public const HTTP_METHOD_NOT_ALLOWED = 405;
public const HTTP_NOT_ACCEPTABLE = 406;
public const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
public const HTTP_REQUEST_TIMEOUT = 408;
public const HTTP_CONFLICT = 409;
public const HTTP_GONE = 410;
public const HTTP_LENGTH_REQUIRED = 411;
public const HTTP_PRECONDITION_FAILED = 412;
public const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
public const HTTP_REQUEST_URI_TOO_LONG = 414;
public const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
public const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
public const HTTP_EXPECTATION_FAILED = 417;
public const HTTP_I_AM_A_TEAPOT = 418; // RFC2324
public const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540
public const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
public const HTTP_LOCKED = 423; // RFC4918
public const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
public const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04
public const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
public const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
public const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
public const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
public const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451; // RFC7725
public const HTTP_INTERNAL_SERVER_ERROR = 500;
public const HTTP_NOT_IMPLEMENTED = 501;
public const HTTP_BAD_GATEWAY = 502;
public const HTTP_SERVICE_UNAVAILABLE = 503;
public const HTTP_GATEWAY_TIMEOUT = 504;
public const HTTP_VERSION_NOT_SUPPORTED = 505;
public const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295
public const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918
public const HTTP_LOOP_DETECTED = 508; // RFC5842
public const HTTP_NOT_EXTENDED = 510; // RFC2774
public const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
}

View File

@@ -0,0 +1,197 @@
<?php
namespace Openguru\OpenCartFramework\ImageTool;
use Intervention\Image\Image;
use Intervention\Image\ImageManager;
use RuntimeException;
use Symfony\Component\HttpFoundation\Response;
class ImageFactory
{
private ?string $path = null;
private string $fullPath;
private array $modifications = [];
private string $imageDir;
private string $siteUrl;
private ImageManager $manager;
private ?Image $image = null;
private array $options = [];
private static array $defaults = [
'format' => 'webp',
'quality' => 90,
'no_image_path' => 'no_image.png',
];
private static array $modificators = [
'resize' => [self::class, 'resizeModification'],
'cover' => [self::class, 'coverModification'],
];
public function __construct(string $imageDir, string $siteUrl, string $driver, array $options = [])
{
$this->imageDir = rtrim($imageDir, '/');
$this->siteUrl = rtrim($siteUrl, '/');
$this->manager = new ImageManager(['driver' => $driver]);
$this->options = array_merge(self::$defaults, $options);
}
/**
* @throws ImageNotFoundException
*/
public function make(?string $path = null): self
{
$this->path = $path;
$this->fullPath = $this->imageDir . '/' . $this->path;
if (! $this->path || ! file_exists($this->fullPath)) {
$this->path = $this->options['no_image_path'];
$this->fullPath = $this->imageDir . '/' . $this->path;
}
$this->ensureFileExists($this->fullPath);
return $this;
}
public function resize(?int $width = null, ?int $height = null, $operation = 'resize'): self
{
$this->modifications['resize'] = [
'width' => $width,
'height' => $height,
];
return $this;
}
public function cover(?int $width = null, ?int $height = null): self
{
$this->modifications['cover'] = [
'width' => $width,
'height' => $height,
];
return $this;
}
public function url(?string $format = null, ?int $quality = null): string
{
$format = $format ?? $this->options['format'];
$quality = $quality ?? $this->options['quality'];
$newImage = $this->resolveNewImagePath($format);
if (file_exists($newImage)) {
return $this->siteUrl . '/image/' . ltrim($newImage, $this->imageDir);
}
$this->image = $this->manager->make($this->fullPath);
$this->applyModifications();
$this->ensureDestinationFolterExists($newImage);
$this->image->encode($format, $quality)->save($newImage);
return $this->siteUrl . '/image/' . ltrim($newImage, $this->imageDir);
}
public function response(?string $format = null, ?int $quality = null): Response
{
$format = $format ?? $this->options['format'];
$quality = $quality ?? $this->options['quality'];
$newImage = $this->resolveNewImagePath($format);
if (file_exists($newImage)) {
return $this->manager->make($newImage)->response();
}
$this->image = $this->manager->make($this->fullPath);
$this->applyModifications();
$this->ensureDestinationFolterExists($newImage);
$this->image->encode($format, $quality)->save($newImage);
return $this->image->response();
}
public function getRealSize(): array
{
$this->image = $this->manager->make($this->fullPath);
return [$this->image->getWidth(), $this->image->getHeight()];
}
/**
* @throws ImageNotFoundException
*/
private function ensureFileExists(string $fullPath): void
{
if (! file_exists($fullPath)) {
throw new ImageNotFoundException($fullPath);
}
}
private function resolveNewImagePath(string $format): string
{
$filename = $this->path;
$pathinfo = pathinfo($filename);
$filename = $pathinfo['filename'];
$basename = $pathinfo['basename'];
$imagePath = rtrim($this->path, $basename);
foreach ($this->modifications as $name => $modification) {
$filename .= '_' . $name . '_' . $modification['width'] . 'x' . $modification['height'];
}
if ($this->modifications) {
$filename .= hash('SHA256', json_encode($this->modifications));
}
return $this->imageDir . '/cache/' . $imagePath . $filename . '.' . $format;
}
private function applyModifications(): void
{
foreach ($this->modifications as $name => $value) {
$method = "{$name}Modification";
if (! method_exists($this, $method)) {
throw new RuntimeException('Modification method ' . $method . ' does not exist');
}
$this->{$method}(...array_values($value));
}
}
private function resizeModification(?int $width = null, ?int $height = null): void
{
if ($width || $height) {
$this->image->resize($width, $height, static fn($constraint) => $constraint->aspectRatio());
}
}
private function coverModification(?int $width = null, ?int $height = null): void
{
if ($width || $height) {
$this->image->fit($width, $height);
}
}
private function ensureDestinationFolterExists(string $newImage): void
{
$path = pathinfo($newImage, PATHINFO_DIRNAME);
if (! file_exists($path)) {
if (! mkdir($path, 0777, true) && ! is_dir($path)) {
throw new RuntimeException(sprintf('Directory "%s" was not created', $path));
}
}
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Openguru\OpenCartFramework\ImageTool;
use Exception;
use Throwable;
class ImageNotFoundException extends Exception
{
public function __construct(string $path = "", $code = 500, Throwable $previous = null)
{
$message = 'Image file not found: ' . $path;
parent::__construct($message, $code, $previous);
}
}

View File

@@ -1,127 +0,0 @@
<?php
namespace Openguru\OpenCartFramework\ImageTool;
use Intervention\Image\ImageManager;
use InvalidArgumentException;
class ImageTool implements ImageToolInterface
{
private string $imageDir;
private string $siteUrl;
private ImageManager $manager;
public function __construct(string $imageDir, string $siteUrl = '/')
{
$this->imageDir = rtrim($imageDir, '/') . '/';
$this->siteUrl = $siteUrl;
$driver = extension_loaded('imagick') ? 'imagick' : 'gd';
$this->manager = new ImageManager(['driver' => $driver]);
}
public function resize(
string $path,
?int $width = null,
?int $height = null,
?string $default = null,
string $format = 'webp'
): ?string {
$filename = is_file($this->imageDir . $path) ? $path : $default;
if (! $width && ! $height) {
throw new InvalidArgumentException('Width or height must be set');
}
if (! $filename || ! is_file($this->imageDir . $filename)) {
return null;
}
$realPath = realpath($this->imageDir . $filename);
if (strpos($realPath, realpath($this->imageDir)) !== 0) {
return null; // безопасность
}
$extless = substr($filename, 0, strrpos($filename, '.'));
$imageNew = 'cache/' . $extless . '-' . $width . 'x' . $height . '.' . $format;
$fullNewPath = $this->imageDir . $imageNew;
$fullOldPath = $this->imageDir . $filename;
if (! is_file($fullNewPath) || filemtime($fullOldPath) > filemtime($fullNewPath)) {
$dirPath = dirname($fullNewPath);
if (! is_dir($dirPath)) {
mkdir($dirPath, 0777, true);
}
$image = $this->manager->make($fullOldPath)
->resize($width, $height, function ($constraint) {
$constraint->aspectRatio();
// $constraint->upsize();
})
->resizeCanvas($width, $height, 'center', false, null);
$image->encode($format, 75)->save($fullNewPath, 75, $format);
}
return rtrim($this->siteUrl, '/') . '/image/' . str_replace($this->imageDir, '', $fullNewPath);
}
public function getUrl(string $path): string
{
return rtrim($this->siteUrl, '/') . '/image/' . str_replace($this->imageDir, '', $path);
}
public function getRealSize(string $path): array
{
$fullPath = $this->imageDir . $path;
if (! is_file($fullPath)) {
throw new \RuntimeException('Image file not found: ' . $path);
}
$img = $this->manager->make($fullPath);
return [$img->getWidth(), $img->getHeight()];
}
public function cover(string $path, int $width, int $height, string $position = 'center'): ?string
{
$format = 'webp';
if (! $width && ! $height) {
throw new InvalidArgumentException('Width or height must be set');
}
if (! $path || ! is_file($this->imageDir . $path)) {
return null;
}
$realPath = realpath($this->imageDir . $path);
if (strpos($realPath, realpath($this->imageDir)) !== 0) {
return null; // безопасность
}
$extless = substr($path, 0, strrpos($path, '.'));
$imageNew = 'cache/' . $extless . '-' . $width . 'x' . $height . '.' . $format;
$fullNewPath = $this->imageDir . $imageNew;
$fullOldPath = $this->imageDir . $path;
if (! is_file($fullNewPath) || filemtime($fullOldPath) > filemtime($fullNewPath)) {
$dirPath = dirname($fullNewPath);
if (! is_dir($dirPath)) {
mkdir($dirPath, 0777, true);
}
$image = $this->manager->make($fullOldPath)
->fit($width, $height, function ($constraint) {
// $constraint->upsize();
}, $position);
$image->encode($format, 75)->save($fullNewPath, 75, $format);
}
return rtrim($this->siteUrl, '/') . '/image/' . str_replace($this->imageDir, '', $fullNewPath);
}
}

View File

@@ -1,19 +0,0 @@
<?php
namespace Openguru\OpenCartFramework\ImageTool;
interface ImageToolInterface
{
public function getUrl(string $path): string;
public function getRealSize(string $path): array;
public function resize(
string $path,
?int $width = null,
?int $height = null,
?string $default = null,
string $format = 'webp'
): ?string;
public function cover(string $path, int $width, int $height, string $position = 'center'): ?string;
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Openguru\OpenCartFramework\ImageTool;
use Openguru\OpenCartFramework\Container\Container;
use Openguru\OpenCartFramework\Container\ServiceProvider;
class ImageToolServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->container->bind(ImageFactory::class, function (Container $container) {
$driver = extension_loaded('imagick') ? 'imagick' : 'gd';
return new ImageFactory(
$container->getConfigValue('paths.images'),
$container->getConfigValue('app.shop_base_url'),
$driver
);
});
}
}

View File

@@ -6,6 +6,7 @@ use App\ServiceProviders\AppServiceProvider;
use App\ServiceProviders\SettingsServiceProvider;
use Openguru\OpenCartFramework\Application;
use Openguru\OpenCartFramework\Cache\CacheServiceProvider;
use Openguru\OpenCartFramework\ImageTool\ImageToolServiceProvider;
use Openguru\OpenCartFramework\QueryBuilder\QueryBuilderServiceProvider;
use Openguru\OpenCartFramework\Router\RouteServiceProvider;
use Openguru\OpenCartFramework\Support\Arr;
@@ -32,6 +33,7 @@ class ApplicationFactory
TelegramServiceProvider::class,
ValidatorServiceProvider::class,
TeleCartPulseServiceProvider::class,
ImageToolServiceProvider::class,
])
->withMiddlewares([
TelegramValidateInitDataMiddleware::class,

View File

@@ -4,7 +4,7 @@ namespace App\Exceptions;
use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
use Openguru\OpenCartFramework\Telegram\Exceptions\TelegramInvalidSignatureException;
use Psr\Log\LoggerInterface;
use Throwable;

View File

@@ -6,24 +6,25 @@ namespace App\Handlers;
use App\Services\SettingsService;
use App\Support\Utils;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
use Openguru\OpenCartFramework\ImageTool\ImageFactory;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
use Openguru\OpenCartFramework\Support\Str;
use Symfony\Component\HttpFoundation\JsonResponse;
class CategoriesHandler
{
private const THUMB_SIZE = 150;
private Builder $queryBuilder;
private ImageToolInterface $ocImageTool;
private ImageFactory $image;
private SettingsService $settings;
public function __construct(Builder $queryBuilder, ImageToolInterface $ocImageTool, SettingsService $settings)
public function __construct(Builder $queryBuilder, ImageFactory $ocImageTool, SettingsService $settings)
{
$this->queryBuilder = $queryBuilder;
$this->ocImageTool = $ocImageTool;
$this->image = $ocImageTool;
$this->settings = $settings;
}
@@ -63,7 +64,7 @@ class CategoriesHandler
return [
'id' => (int) $category['id'],
'image' => $category['image'] ?? '',
'name' => Utils::htmlEntityEncode($category['name']),
'name' => Str::htmlEntityEncode($category['name']),
'description' => $category['description'],
'children' => $category['children'],
];
@@ -82,12 +83,10 @@ class CategoriesHandler
$category['children'] = $children;
}
$image = $this->ocImageTool->resize(
$category['image'] ?? '',
self::THUMB_SIZE,
self::THUMB_SIZE,
'no_image.png'
);
$image = $this->image
->make($category['image'])
->resize(self::THUMB_SIZE, self::THUMB_SIZE)
->url();
$branch[] = [
'id' => (int) $category['id'],

View File

@@ -6,7 +6,7 @@ use JsonException;
use Openguru\OpenCartFramework\Exceptions\EntityNotFoundException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
class FormsHandler

View File

@@ -3,7 +3,7 @@
namespace App\Handlers;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
class HealthCheckHandler
{

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Handlers;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\ImageTool\ImageFactory;
use Symfony\Component\HttpFoundation\Response;
class ImageHandler
{
private ImageFactory $image;
public function __construct(ImageFactory $image)
{
$this->image = $image;
}
public function getImage(Request $request): Response
{
$path = $request->query->get('path');
[$width, $height] = $this->parseSize($request->query->get('size'));
return $this->image
->make($path)
->resize($width, $height)
->response();
}
private function parseSize(?string $size = null): array
{
if (! $size) {
return [null, null];
}
$sizes = explode('x', $size);
return array_map(static fn($value) => is_numeric($value) ? (int) $value : null, $sizes);
}
}

View File

@@ -6,7 +6,7 @@ use App\Exceptions\OrderValidationFailedException;
use App\Services\OrderCreateService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
class OrderHandler
{

View File

@@ -6,7 +6,7 @@ use App\Models\TelegramCustomer;
use Carbon\Carbon;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
use Openguru\OpenCartFramework\Support\Arr;
use Openguru\OpenCartFramework\Telegram\Enums\TelegramHeader;
use Openguru\OpenCartFramework\Telegram\TelegramService;

View File

@@ -10,7 +10,7 @@ use Exception;
use Openguru\OpenCartFramework\Exceptions\EntityNotFoundException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
use Psr\Log\LoggerInterface;
use RuntimeException;

View File

@@ -5,27 +5,27 @@ namespace App\Handlers;
use App\Services\SettingsService;
use Exception;
use GuzzleHttp\Exception\ClientException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
use Openguru\OpenCartFramework\ImageTool\ImageFactory;
use Openguru\OpenCartFramework\Router\Router;
use Openguru\OpenCartFramework\Telegram\TelegramService;
use Symfony\Component\HttpFoundation\JsonResponse;
class SettingsHandler
{
private SettingsService $settings;
private ImageToolInterface $imageTool;
private ImageFactory $image;
private Router $router;
private TelegramService $telegramService;
public function __construct(
SettingsService $settings,
ImageToolInterface $imageTool,
ImageFactory $image,
Router $router,
TelegramService $telegramService
) {
$this->settings = $settings;
$this->imageTool = $imageTool;
$this->image = $image;
$this->router = $router;
$this->telegramService = $telegramService;
}
@@ -39,11 +39,11 @@ class SettingsHandler
$icons = [];
if ($appIcon) {
$icons['icon192'] = $this->imageTool->resize($appIcon, 192, 192, 'no_image.png', 'png') . '?_v=' . $hash;
$icons['icon180'] = $this->imageTool->resize($appIcon, 180, 180, 'no_image.png', 'png') . '?_v=' . $hash;
$icons['icon152'] = $this->imageTool->resize($appIcon, 152, 152, 'no_image.png', 'png') . '?_v=' . $hash;
$icons['icon120'] = $this->imageTool->resize($appIcon, 120, 120, 'no_image.png', 'png') . '?_v=' . $hash;
$appIcon = $this->imageTool->resize($appIcon, null, 64, 'no_image.png', 'png') . '?_v=' . $hash;
$icons['icon192'] = $this->image->make($appIcon)->resize(192, 192)->url('png') . '?_v=' . $hash;
$icons['icon180'] = $this->image->make($appIcon)->resize(180, 180)->url('png') . '?_v=' . $hash;;
$icons['icon152'] = $this->image->make($appIcon)->resize(152, 152)->url('png') . '?_v=' . $hash;;
$icons['icon120'] = $this->image->make($appIcon)->resize(120, 120)->url('png') . '?_v=' . $hash;
$appIcon = $this->image->make($appIcon)->resize(null, 64)->url('png') . '?_v=' . $hash;
}
return new JsonResponse([
@@ -83,8 +83,8 @@ class SettingsHandler
$appIcon = $this->settings->config()->getApp()->getAppIcon();
if ($appIcon) {
$icon192 = $this->imageTool->resize($appIcon, 192, 192, 'no_image.png', 'png');
$icon512 = $this->imageTool->resize($appIcon, 512, 512, 'no_image.png', 'png');
$icon192 = $this->image->make($appIcon)->resize(192, 192)->url('png');
$icon512 = $this->image->make($appIcon)->resize(512, 512)->url('png');
$manifest['icons'] = [
[
'src' => $icon192,

View File

@@ -7,7 +7,7 @@ namespace App\Handlers;
use App\Services\TelegramCustomerService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
use Openguru\OpenCartFramework\Support\Arr;
use Openguru\OpenCartFramework\TeleCartPulse\TrackingIdGenerator;
use Openguru\OpenCartFramework\Telegram\Enums\TelegramHeader;

View File

@@ -6,7 +6,7 @@ namespace App\Handlers;
use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Http\Response;
use Symfony\Component\HttpFoundation\Response;
use Openguru\OpenCartFramework\TeleCartPulse\PulseIngestException;
use Openguru\OpenCartFramework\TeleCartPulse\TeleCartPulseService;
use Psr\Log\LoggerInterface;

View File

@@ -3,8 +3,7 @@
namespace App\Services;
use Openguru\OpenCartFramework\Cache\CacheInterface;
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
use Psr\Log\LoggerInterface;
use Openguru\OpenCartFramework\ImageTool\ImageFactory;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
use RuntimeException;
@@ -18,23 +17,20 @@ class BlocksService
'products_carousel' => [self::class, 'processProductsCarousel'],
];
private LoggerInterface $logger;
private ImageToolInterface $imageTool;
private ImageFactory $image;
private CacheInterface $cache;
private SettingsService $settings;
private Builder $queryBuilder;
private ProductsService $productsService;
public function __construct(
LoggerInterface $logger,
ImageToolInterface $imageTool,
ImageFactory $image,
CacheInterface $cache,
SettingsService $settings,
Builder $queryBuilder,
ProductsService $productsService
) {
$this->logger = $logger;
$this->imageTool = $imageTool;
$this->image = $image;
$this->cache = $cache;
$this->settings = $settings;
$this->queryBuilder = $queryBuilder;
@@ -68,11 +64,8 @@ class BlocksService
$slides = $block['data']['slides'];
foreach ($slides as $slideIndex => $slide) {
if (is_file(DIR_IMAGE . $slide['image'])) {
$block['data']['slides'][$slideIndex]['image'] = $this->imageTool->cover(
$slide['image'],
1110,
600
);
$image = $this->image->make($slide['image']);
$block['data']['slides'][$slideIndex]['image'] = $image->cover(1110, 600)->url();
}
}

View File

@@ -2,14 +2,13 @@
namespace App\Services;
use App\Support\Utils;
use Cart\Currency;
use Cart\Tax;
use Exception;
use Openguru\OpenCartFramework\CriteriaBuilder\CriteriaBuilder;
use Openguru\OpenCartFramework\Exceptions\EntityNotFoundException;
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
use Psr\Log\LoggerInterface;
use Openguru\OpenCartFramework\ImageTool\ImageFactory;
use Openguru\OpenCartFramework\ImageTool\ImageNotFoundException;
use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator;
use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
@@ -17,6 +16,8 @@ use Openguru\OpenCartFramework\QueryBuilder\RawExpression;
use Openguru\OpenCartFramework\QueryBuilder\Table;
use Openguru\OpenCartFramework\Support\Arr;
use Openguru\OpenCartFramework\Support\PaginationHelper;
use Openguru\OpenCartFramework\Support\Str;
use Psr\Log\LoggerInterface;
class ProductsService
{
@@ -24,7 +25,7 @@ class ProductsService
private Currency $currency;
private Tax $tax;
private SettingsService $settings;
private ImageToolInterface $ocImageTool;
private ImageFactory $image;
private OcRegistryDecorator $oc;
private LoggerInterface $logger;
private CriteriaBuilder $criteriaBuilder;
@@ -34,7 +35,7 @@ class ProductsService
Currency $currency,
Tax $tax,
SettingsService $settings,
ImageToolInterface $ocImageTool,
ImageFactory $image,
OcRegistryDecorator $registry,
LoggerInterface $logger,
CriteriaBuilder $criteriaBuilder
@@ -43,12 +44,15 @@ class ProductsService
$this->currency = $currency;
$this->tax = $tax;
$this->settings = $settings;
$this->ocImageTool = $ocImageTool;
$this->image = $image;
$this->oc = $registry;
$this->logger = $logger;
$this->criteriaBuilder = $criteriaBuilder;
}
/**
* @throws ImageNotFoundException
*/
public function getProductsResponse(array $params, int $languageId): array
{
$page = $params['page'];
@@ -141,7 +145,7 @@ class ProductsService
$productsImagesMap = [];
foreach ($productsImages as $item) {
$productsImagesMap[$item['product_id']][] = [
'url' => $this->ocImageTool->resize($item['image'], $imageWidth, $imageHeight, 'placeholder.png'),
'url' => $this->image->make($item['image'])->resize($imageWidth, $imageHeight)->url(),
'alt' => 'Product Image',
];
}
@@ -157,16 +161,13 @@ class ProductsService
'data' => array_map(function ($product) use ($productsImagesMap, $imageWidth, $imageHeight, $currency) {
$allImages = [];
$image = $this->ocImageTool->resize(
$product['product_image'],
$imageWidth,
$imageHeight,
'placeholder.png'
);
$image = $this->image->make($product['product_image'])
->resize($imageWidth, $imageHeight)
->url();
$allImages[] = [
'url' => $image,
'alt' => Utils::htmlEntityEncode($product['product_name']),
'alt' => Str::htmlEntityEncode($product['product_name']),
];
$priceNumeric = $this->tax->calculate(
@@ -197,7 +198,7 @@ class ProductsService
return [
'id' => (int) $product['product_id'],
'product_quantity' => (int) $product['product_quantity'],
'name' => Utils::htmlEntityEncode($product['product_name']),
'name' => Str::htmlEntityEncode($product['product_name']),
'price' => $price,
'special' => $special,
'image' => $image,
@@ -251,13 +252,13 @@ class ProductsService
$data['tab_review'] = sprintf($this->oc->language->get('tab_review'), $product_info['reviews']);
$data['product_id'] = $productId;
$data['name'] = Utils::htmlEntityEncode($product_info['name']);
$data['name'] = Str::htmlEntityEncode($product_info['name']);
$data['manufacturer'] = $product_info['manufacturer'];
$data['model'] = $product_info['model'];
$data['reward'] = $product_info['reward'];
$data['points'] = (int) $product_info['points'];
$data['description'] = Utils::htmlEntityEncode($product_info['description']);
$data['share'] = Utils::htmlEntityEncode(
$data['description'] = Str::htmlEntityEncode($product_info['description']);
$data['share'] = Str::htmlEntityEncode(
$this->oc->url->link('product/product', 'product_id=' . $productId)
);
@@ -281,23 +282,16 @@ class ProductsService
$images = [];
foreach ($allImages as $imagePath) {
try {
[$width, $height] = $this->ocImageTool->getRealSize($imagePath);
[$width, $height] = $this->image->make($imagePath)->getRealSize();
$images[] = [
'thumbnailURL' => $this->ocImageTool->resize(
$imagePath,
$imageThumbWidth,
$imageThumbHeight,
'placeholder.png'
),
'largeURL' => $this->ocImageTool->resize(
$imagePath,
$imageFullWidth,
$imageFullHeight,
'placeholder.png'
),
'thumbnailURL' => $this->image
->make($imagePath)
->resize($imageThumbWidth, $imageThumbHeight)
->url(),
'largeURL' => $this->image->make($imagePath)->resize($imageFullWidth, $imageFullHeight)->url(),
'width' => $width,
'height' => $height,
'alt' => Utils::htmlEntityEncode($product_info['name']),
'alt' => Str::htmlEntityEncode($product_info['name']),
];
} catch (Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);

View File

@@ -7,6 +7,7 @@ use App\Handlers\ETLHandler;
use App\Handlers\FiltersHandler;
use App\Handlers\FormsHandler;
use App\Handlers\HealthCheckHandler;
use App\Handlers\ImageHandler;
use App\Handlers\OrderHandler;
use App\Handlers\PrivacyPolicyHandler;
use App\Handlers\ProductsHandler;
@@ -22,6 +23,7 @@ return [
'filtersForMainPage' => [FiltersHandler::class, 'getFiltersForMainPage'],
'getCart' => [CartHandler::class, 'index'],
'getForm' => [FormsHandler::class, 'getForm'],
'getImage' => [ImageHandler::class, 'getImage'],
'health' => [HealthCheckHandler::class, 'handle'],
'ingest' => [TelemetryHandler::class, 'ingest'],
'heartbeat' => [TelemetryHandler::class, 'heartbeat'],