init
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Esun\MultiLocaleTranslation\Model\Queue;
|
||||
|
||||
use Esun\MultiLocaleTranslation\Model\EntityPool;
|
||||
use Esun\MultiLocaleTranslation\Model\LocaleResolver;
|
||||
use Esun\MultiLocaleTranslation\Model\ProviderPool;
|
||||
use Esun\MultiLocaleTranslation\Model\TranslationLogger;
|
||||
use Magento\Framework\App\ResourceConnection;
|
||||
use Magento\Framework\DB\Sql\Expression;
|
||||
|
||||
class QueueProcessor
|
||||
{
|
||||
private const TABLE_NAME = 'esun_localetranslation_queue';
|
||||
|
||||
public function __construct(
|
||||
private readonly ResourceConnection $resourceConnection,
|
||||
private readonly EntityPool $entityPool,
|
||||
private readonly ProviderPool $providerPool,
|
||||
private readonly LocaleResolver $localeResolver,
|
||||
private readonly TranslationLogger $translationLogger
|
||||
) {
|
||||
}
|
||||
|
||||
public function process(int $limit = 10, int $timeLimit = 50): int
|
||||
{
|
||||
$processed = 0;
|
||||
$startedAt = microtime(true);
|
||||
$limit = min(50, max(1, $limit));
|
||||
$timeLimit = min(300, max(10, $timeLimit));
|
||||
|
||||
for ($index = 0; $index < $limit; $index++) {
|
||||
if ($processed > 0 && microtime(true) - $startedAt >= $timeLimit) {
|
||||
break;
|
||||
}
|
||||
$task = $this->claimNext();
|
||||
if ($task === null) {
|
||||
break;
|
||||
}
|
||||
$this->processTask($task);
|
||||
$processed++;
|
||||
}
|
||||
|
||||
return $processed;
|
||||
}
|
||||
|
||||
/** @return array<string, mixed>|null */
|
||||
private function claimNext(): ?array
|
||||
{
|
||||
$connection = $this->resourceConnection->getConnection();
|
||||
$table = $this->resourceConnection->getTableName(self::TABLE_NAME);
|
||||
|
||||
while (true) {
|
||||
$queueId = (int)$connection->fetchOne(
|
||||
$connection->select()->from($table, 'queue_id')
|
||||
->where('status = ?', 'pending')
|
||||
->order('queue_id ASC')
|
||||
->limit(1)
|
||||
);
|
||||
if ($queueId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$affected = $connection->update(
|
||||
$table,
|
||||
[
|
||||
'status' => 'processing',
|
||||
'attempts' => new Expression('attempts + 1'),
|
||||
'started_at' => new Expression('CURRENT_TIMESTAMP'),
|
||||
'finished_at' => null,
|
||||
'error_message' => null,
|
||||
],
|
||||
['queue_id = ?' => $queueId, 'status = ?' => 'pending']
|
||||
);
|
||||
if ($affected === 1) {
|
||||
$task = $connection->fetchRow(
|
||||
$connection->select()->from($table)->where('queue_id = ?', $queueId)
|
||||
);
|
||||
return is_array($task) ? $task : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $task */
|
||||
private function processTask(array $task): void
|
||||
{
|
||||
$queueId = (int)$task['queue_id'];
|
||||
$provider = (string)$task['provider'];
|
||||
$entityType = (string)$task['entity_type'];
|
||||
$entityId = (int)$task['entity_id'];
|
||||
$sourceStoreId = (int)$task['source_store_id'];
|
||||
$targetStoreId = (int)$task['target_store_id'];
|
||||
$sourceLanguage = '';
|
||||
$targetLanguage = '';
|
||||
$providerFields = [];
|
||||
|
||||
try {
|
||||
$entity = $this->entityPool->get($entityType);
|
||||
foreach ($entity->getFields($entityId, $sourceStoreId, $targetStoreId) as $field) {
|
||||
$value = (string)($field['source_value'] ?? '');
|
||||
if ($value === '' || empty($field['code'])) {
|
||||
continue;
|
||||
}
|
||||
$providerFields[(string)$field['code']] = [
|
||||
'value' => $value,
|
||||
'is_html' => !empty($field['is_html']),
|
||||
];
|
||||
}
|
||||
|
||||
$sourceLanguage = $this->localeResolver->getLanguageCode($sourceStoreId, $provider, false);
|
||||
$targetLanguage = $this->localeResolver->getLanguageCode($targetStoreId, $provider, true);
|
||||
if ($providerFields === []) {
|
||||
$translations = [];
|
||||
} elseif ($sourceLanguage !== '' && $sourceLanguage === $targetLanguage) {
|
||||
$translations = $this->copyValues($providerFields);
|
||||
} else {
|
||||
$translations = $this->providerPool->get($provider)->translate(
|
||||
$providerFields,
|
||||
$sourceLanguage,
|
||||
$targetLanguage
|
||||
);
|
||||
}
|
||||
|
||||
$entity->apply($entityId, $targetStoreId, $translations);
|
||||
$this->safeLogSuccess(
|
||||
$provider,
|
||||
$entityType,
|
||||
$entityId,
|
||||
$sourceStoreId,
|
||||
$targetStoreId,
|
||||
$sourceLanguage,
|
||||
$targetLanguage,
|
||||
$providerFields,
|
||||
$translations
|
||||
);
|
||||
$this->finish($queueId, 'success', null);
|
||||
} catch (\Throwable $exception) {
|
||||
$this->safeLogFailure(
|
||||
$provider,
|
||||
$entityType,
|
||||
$entityId,
|
||||
$sourceStoreId,
|
||||
$targetStoreId,
|
||||
$sourceLanguage,
|
||||
$targetLanguage,
|
||||
$providerFields,
|
||||
$exception->getMessage()
|
||||
);
|
||||
$this->finish($queueId, 'failed', $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $fields @return array<string, string> */
|
||||
private function copyValues(array $fields): array
|
||||
{
|
||||
$values = [];
|
||||
foreach ($fields as $code => $field) {
|
||||
$values[$code] = (string)($field['value'] ?? '');
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
private function finish(int $queueId, string $status, ?string $error): void
|
||||
{
|
||||
$connection = $this->resourceConnection->getConnection();
|
||||
$connection->update(
|
||||
$this->resourceConnection->getTableName(self::TABLE_NAME),
|
||||
[
|
||||
'status' => $status,
|
||||
'error_message' => $error,
|
||||
'finished_at' => new Expression('CURRENT_TIMESTAMP'),
|
||||
],
|
||||
['queue_id = ?' => $queueId]
|
||||
);
|
||||
}
|
||||
|
||||
private function safeLogSuccess(
|
||||
string $provider,
|
||||
string $entityType,
|
||||
int $entityId,
|
||||
int $sourceStoreId,
|
||||
int $targetStoreId,
|
||||
string $sourceLanguage,
|
||||
string $targetLanguage,
|
||||
array $fields,
|
||||
array $translations
|
||||
): void {
|
||||
try {
|
||||
$this->translationLogger->logSuccess(
|
||||
$provider,
|
||||
$entityType,
|
||||
$entityId,
|
||||
$sourceStoreId,
|
||||
$targetStoreId,
|
||||
$sourceLanguage,
|
||||
$targetLanguage,
|
||||
$fields,
|
||||
$translations,
|
||||
$sourceLanguage === $targetLanguage ? 'copied' : 'success'
|
||||
);
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
private function safeLogFailure(
|
||||
string $provider,
|
||||
string $entityType,
|
||||
int $entityId,
|
||||
int $sourceStoreId,
|
||||
int $targetStoreId,
|
||||
string $sourceLanguage,
|
||||
string $targetLanguage,
|
||||
array $fields,
|
||||
string $error
|
||||
): void {
|
||||
try {
|
||||
$this->translationLogger->logFailure(
|
||||
$provider,
|
||||
$entityType,
|
||||
$entityId,
|
||||
$sourceStoreId,
|
||||
$targetStoreId,
|
||||
$sourceLanguage,
|
||||
$targetLanguage,
|
||||
$fields,
|
||||
$error
|
||||
);
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user