Skip to content

Instantly share code, notes, and snippets.

@ProxiBlue
Created February 4, 2025 23:24
Show Gist options
  • Save ProxiBlue/5754200fd86f4c71555ef30c6ac6cd20 to your computer and use it in GitHub Desktop.
Save ProxiBlue/5754200fd86f4c71555ef30c6ac6cd20 to your computer and use it in GitHub Desktop.
convert dynamic category products rules to elasticsuite rules
<?php
namespace ProxiBlue\MigrateTasks\Console\Command;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Console\Cli;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Serialize\Serializer\Json;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ConvertRulesCommand extends Command
{
const SOURCE_ATTRIBUTE_ID = 297;
const TARGET_ATTRIBUTE_ID = 780;
const IS_VIRTUAL_CATEGORY_ATTRIBUTE_ID = 778;
const STORE_ID = 0;
private ResourceConnection $resourceConnection;
private Json $jsonSerializer;
public function __construct(
ResourceConnection $resourceConnection,
Json $jsonSerializer
) {
$this->resourceConnection = $resourceConnection;
$this->jsonSerializer = $jsonSerializer;
parent::__construct();
}
protected function configure()
{
$this->setName('proxiblue_migratetasks:convert-rules')
->setDescription('Convert rules from Magento 1 to Magento 2 module format and set is_virtual_category');
parent::configure();
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$connection = $this->resourceConnection->getConnection();
$tableName = $this->resourceConnection->getTableName('catalog_category_entity_text');
try {
// Fetch rows for conversion
$query = $connection->select()
->from($tableName)
->where('attribute_id = ?', self::SOURCE_ATTRIBUTE_ID)
->order('entity_id ASC')
->order('store_id DESC');
$rows = $connection->fetchAll($query);
foreach ($rows as $row) {
$oldValue = $row['value'];
$entityId = $row['entity_id'];
if(empty($oldValue)) {
continue;
}
// Skip if the rule has already been converted
if (strpos($oldValue, 'Converted') === 0) {
$output->writeln("<comment>Skipping already converted rule for entity ID {$entityId}.</comment>");
continue;
}
try {
// Decode old Magento 1 rules (unserialize)
$ruleData = unserialize($oldValue);
} catch (\Exception $e) {
$output->writeln("<error>Failed to unserialize rule for entity ID {$entityId}</error>");
continue;
}
// Check for complex rules or invalid conditions
$containsComplex = $this->checkForComplexRules($ruleData);
$isValidAggregator = isset($ruleData['1--1']['aggregator']) && $ruleData['1--1']['aggregator'] === 'all';
$isValidValue = isset($ruleData['1--1']['value']) && $ruleData['1--1']['value'] === '1';
if ($containsComplex || !$isValidAggregator || !$isValidValue) {
$output->writeln("<comment>Rule for entity ID {$entityId} marked as complex or invalid and not converted.</comment>");
continue;
}
// Convert rule and handle database updates
try {
$convertedRule = $this->convertRule($ruleData);
$convertedValue = $this->jsonSerializer->serialize($convertedRule);
// Update original row with 'Converted:' prefix
$connection->update(
$tableName,
['value' => 'Converted:' . $oldValue],
['entity_id = ?' => $entityId, 'attribute_id = ?' => self::SOURCE_ATTRIBUTE_ID]
);
// Insert the converted rule into the database
$connection->insert($tableName, [
'entity_id' => $entityId,
'attribute_id' => self::TARGET_ATTRIBUTE_ID,
'store_id' => self::STORE_ID,
'value' => $convertedValue,
]);
// Set `is_virtual_category` attribute for the entity
$connection->insertOnDuplicate('catalog_category_entity_int', [
'entity_id' => $entityId,
'attribute_id' => self::IS_VIRTUAL_CATEGORY_ATTRIBUTE_ID,
'store_id' => self::STORE_ID,
'value' => '1', // Assuming '1' means it is a virtual category
]);
$output->writeln("<info>Successfully converted rule for entity ID {$entityId} and set is_virtual_category.</info>");
} catch (\Exception $e) {
$output->writeln("<error>Failed to convert rule for entity ID {$entityId}: {$e->getMessage()}</error>");
}
}
} catch (\Exception $e) {
throw new LocalizedException(__('Error occurred during rule conversion: %1', $e->getMessage()));
}
return Cli::RETURN_SUCCESS;
}
private function checkForComplexRules(array $ruleData): bool
{
$combineRuleCount = 0;
foreach ($ruleData as $rule) {
if (isset($rule['type']) && $rule['type'] === 'dyncatprod/rule_condition_combine') {
$combineRuleCount++;
}
// If more than one is found, it is complex
if ($combineRuleCount > 1) {
return true;
}
}
return false;
}
private function convertRule(array $ruleData): array
{
$conditions = [];
foreach ($ruleData as $key => $rule) {
// Skip combined rules as they're not valid to convert
if (isset($rule['type']) && $rule['type'] === 'dyncatprod/rule_condition_product') {
$conditions[] = [
'type' => 'Smile\\ElasticsuiteVirtualCategory\\Model\\Rule\\Condition\\Product',
'attribute' => $rule['attribute'],
'operator' => $this->mapOperator($rule['operator']),
'value' => isset($rule['value']) ? (array)$rule['value'] : [],
'is_value_processed' => false,
];
}
}
return [
'type' => 'Smile\\ElasticsuiteVirtualCategory\\Model\\Rule\\Condition\\Combine',
'attribute' => null,
'operator' => null,
'value' => '1',
'is_value_processed' => null,
'aggregator' => 'all',
'conditions' => $conditions,
];
}
private function mapOperator(string $operator): string
{
// Example operator mapping - adapt as needed
$operatorMap = [
'{}' => '()',
'!{}' => '!()',
];
return $operatorMap[$operator] ?? $operator;
}
}
@ProxiBlue
Copy link
Author

is not fancy, used during an m1 to m2 migration from ProxiBlue Dynamic Category products to elasticsuite Virtual category rules
Only handles not complex rules. so basically product attribute rules only. Is all I needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment