feat(cronjob): implement CronJobProcessor and CronJobRepository for job scheduling and processing
- Added CronJobProcessor class to handle job creation and queue processing. - Implemented CronJobRepository for database interactions related to cron jobs. - Introduced CronJobType class to define job types, priorities, and statuses. - Created ApiloLogger for logging actions related to job processing. - Initialized apilo-sync-queue.json for job queue management.
This commit is contained in:
@@ -48,7 +48,7 @@ class AttributeRepository
|
||||
FROM pp_shop_attributes AS sa
|
||||
WHERE {$whereSql}
|
||||
";
|
||||
$stmtCount = $this->db->query($sqlCount, $params);
|
||||
$stmtCount = $this->db->query($sqlCount, $whereData['params']);
|
||||
$countRows = $stmtCount ? $stmtCount->fetchAll() : [];
|
||||
$total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;
|
||||
|
||||
@@ -534,6 +534,216 @@ class AttributeRepository
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zwraca aktywne atrybuty z wartościami i wielojęzycznymi nazwami dla REST API.
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function listForApi(): array
|
||||
{
|
||||
// 1. Get all active attribute IDs (1 query)
|
||||
$rows = $this->db->select('pp_shop_attributes', ['id', 'type', 'status'], [
|
||||
'status' => 1,
|
||||
'ORDER' => ['o' => 'ASC'],
|
||||
]);
|
||||
if (!is_array($rows) || empty($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$attrIds = [];
|
||||
foreach ($rows as $row) {
|
||||
$id = (int)($row['id'] ?? 0);
|
||||
if ($id > 0) {
|
||||
$attrIds[] = $id;
|
||||
}
|
||||
}
|
||||
if (empty($attrIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 2. Batch load ALL attribute translations (1 query)
|
||||
$allAttrTranslations = $this->db->select(
|
||||
'pp_shop_attributes_langs',
|
||||
['attribute_id', 'lang_id', 'name'],
|
||||
['attribute_id' => $attrIds]
|
||||
);
|
||||
$attrNamesMap = [];
|
||||
if (is_array($allAttrTranslations)) {
|
||||
foreach ($allAttrTranslations as $t) {
|
||||
$aId = (int)($t['attribute_id'] ?? 0);
|
||||
$langId = (string)($t['lang_id'] ?? '');
|
||||
if ($aId > 0 && $langId !== '') {
|
||||
$attrNamesMap[$aId][$langId] = (string)($t['name'] ?? '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Batch load ALL values for those attribute IDs (1 query)
|
||||
$allValueRows = $this->db->select(
|
||||
'pp_shop_attributes_values',
|
||||
['id', 'attribute_id', 'is_default', 'impact_on_the_price'],
|
||||
[
|
||||
'attribute_id' => $attrIds,
|
||||
'ORDER' => ['id' => 'ASC'],
|
||||
]
|
||||
);
|
||||
$valuesByAttr = [];
|
||||
$allValueIds = [];
|
||||
if (is_array($allValueRows)) {
|
||||
foreach ($allValueRows as $vRow) {
|
||||
$valueId = (int)($vRow['id'] ?? 0);
|
||||
$attrId = (int)($vRow['attribute_id'] ?? 0);
|
||||
if ($valueId > 0 && $attrId > 0) {
|
||||
$valuesByAttr[$attrId][] = $vRow;
|
||||
$allValueIds[] = $valueId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Batch load ALL value translations (1 query)
|
||||
$valueNamesMap = [];
|
||||
if (!empty($allValueIds)) {
|
||||
$allValueTranslations = $this->db->select(
|
||||
'pp_shop_attributes_values_langs',
|
||||
['value_id', 'lang_id', 'name'],
|
||||
['value_id' => $allValueIds]
|
||||
);
|
||||
if (is_array($allValueTranslations)) {
|
||||
foreach ($allValueTranslations as $vt) {
|
||||
$vId = (int)($vt['value_id'] ?? 0);
|
||||
$langId = (string)($vt['lang_id'] ?? '');
|
||||
if ($vId > 0 && $langId !== '') {
|
||||
$valueNamesMap[$vId][$langId] = (string)($vt['name'] ?? '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Assemble result in-memory
|
||||
$result = [];
|
||||
foreach ($rows as $row) {
|
||||
$attributeId = (int)($row['id'] ?? 0);
|
||||
if ($attributeId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$names = isset($attrNamesMap[$attributeId]) ? $attrNamesMap[$attributeId] : [];
|
||||
|
||||
$values = [];
|
||||
if (isset($valuesByAttr[$attributeId])) {
|
||||
foreach ($valuesByAttr[$attributeId] as $vRow) {
|
||||
$valueId = (int)$vRow['id'];
|
||||
$impact = $vRow['impact_on_the_price'];
|
||||
$values[] = [
|
||||
'id' => $valueId,
|
||||
'names' => isset($valueNamesMap[$valueId]) ? $valueNamesMap[$valueId] : [],
|
||||
'is_default' => (int)($vRow['is_default'] ?? 0),
|
||||
'impact_on_the_price' => ($impact !== null && $impact !== '') ? (float)$impact : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'id' => $attributeId,
|
||||
'type' => (int)($row['type'] ?? 0),
|
||||
'status' => (int)($row['status'] ?? 0),
|
||||
'names' => $names,
|
||||
'values' => $values,
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find existing attribute by name/type or create a new one for API integration.
|
||||
*
|
||||
* @return array{id:int,created:bool}|null
|
||||
*/
|
||||
public function ensureAttributeForApi(string $name, int $type = 0, string $langId = 'pl'): ?array
|
||||
{
|
||||
$normalizedName = trim($name);
|
||||
$normalizedLangId = trim($langId) !== '' ? trim($langId) : 'pl';
|
||||
$normalizedType = $this->toTypeValue($type);
|
||||
if ($normalizedName === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$existingId = $this->findAttributeIdByNameAndType($normalizedName, $normalizedType);
|
||||
if ($existingId > 0) {
|
||||
return ['id' => $existingId, 'created' => false];
|
||||
}
|
||||
|
||||
$this->db->insert('pp_shop_attributes', [
|
||||
'status' => 1,
|
||||
'type' => $normalizedType,
|
||||
'o' => $this->nextOrder(),
|
||||
]);
|
||||
$attributeId = (int) $this->db->id();
|
||||
if ($attributeId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->db->insert('pp_shop_attributes_langs', [
|
||||
'attribute_id' => $attributeId,
|
||||
'lang_id' => $normalizedLangId,
|
||||
'name' => $normalizedName,
|
||||
]);
|
||||
|
||||
$this->clearTempAndCache();
|
||||
$this->clearFrontCache($attributeId, 'frontAttributeDetails');
|
||||
|
||||
return ['id' => $attributeId, 'created' => true];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find existing value by name within attribute or create a new one for API integration.
|
||||
*
|
||||
* @return array{id:int,created:bool}|null
|
||||
*/
|
||||
public function ensureAttributeValueForApi(int $attributeId, string $name, string $langId = 'pl'): ?array
|
||||
{
|
||||
$normalizedName = trim($name);
|
||||
$normalizedLangId = trim($langId) !== '' ? trim($langId) : 'pl';
|
||||
$attributeId = max(0, $attributeId);
|
||||
|
||||
if ($attributeId <= 0 || $normalizedName === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$attributeExists = (int) $this->db->count('pp_shop_attributes', ['id' => $attributeId]) > 0;
|
||||
if (!$attributeExists) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$existingId = $this->findAttributeValueIdByName($attributeId, $normalizedName);
|
||||
if ($existingId > 0) {
|
||||
return ['id' => $existingId, 'created' => false];
|
||||
}
|
||||
|
||||
$this->db->insert('pp_shop_attributes_values', [
|
||||
'attribute_id' => $attributeId,
|
||||
'impact_on_the_price' => null,
|
||||
'is_default' => 0,
|
||||
]);
|
||||
$valueId = (int) $this->db->id();
|
||||
if ($valueId <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->db->insert('pp_shop_attributes_values_langs', [
|
||||
'value_id' => $valueId,
|
||||
'lang_id' => $normalizedLangId,
|
||||
'name' => $normalizedName,
|
||||
'value' => null,
|
||||
]);
|
||||
|
||||
$this->clearTempAndCache();
|
||||
$this->clearFrontCache($valueId, 'frontValueDetails');
|
||||
|
||||
return ['id' => $valueId, 'created' => true];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{sql: string, params: array<string, mixed>}
|
||||
*/
|
||||
@@ -851,6 +1061,52 @@ class AttributeRepository
|
||||
return $this->defaultLangId;
|
||||
}
|
||||
|
||||
private function findAttributeIdByNameAndType(string $name, int $type): int
|
||||
{
|
||||
$statement = $this->db->query(
|
||||
'SELECT sa.id
|
||||
FROM pp_shop_attributes sa
|
||||
INNER JOIN pp_shop_attributes_langs sal ON sal.attribute_id = sa.id
|
||||
WHERE sa.type = :type
|
||||
AND LOWER(TRIM(sal.name)) = LOWER(TRIM(:name))
|
||||
ORDER BY sa.id ASC
|
||||
LIMIT 1',
|
||||
[
|
||||
':type' => $type,
|
||||
':name' => $name,
|
||||
]
|
||||
);
|
||||
if (!$statement) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$id = $statement->fetchColumn();
|
||||
return $id === false ? 0 : (int) $id;
|
||||
}
|
||||
|
||||
private function findAttributeValueIdByName(int $attributeId, string $name): int
|
||||
{
|
||||
$statement = $this->db->query(
|
||||
'SELECT sav.id
|
||||
FROM pp_shop_attributes_values sav
|
||||
INNER JOIN pp_shop_attributes_values_langs savl ON savl.value_id = sav.id
|
||||
WHERE sav.attribute_id = :attribute_id
|
||||
AND LOWER(TRIM(savl.name)) = LOWER(TRIM(:name))
|
||||
ORDER BY sav.id ASC
|
||||
LIMIT 1',
|
||||
[
|
||||
':attribute_id' => $attributeId,
|
||||
':name' => $name,
|
||||
]
|
||||
);
|
||||
if (!$statement) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$id = $statement->fetchColumn();
|
||||
return $id === false ? 0 : (int) $id;
|
||||
}
|
||||
|
||||
// ── Frontend methods ──────────────────────────────────────────
|
||||
|
||||
public function frontAttributeDetails(int $attributeId, string $langId): array
|
||||
|
||||
Reference in New Issue
Block a user