321 lines
11 KiB
PHP
321 lines
11 KiB
PHP
<?php
|
|
|
|
namespace KupShop\AdminBundle\Util;
|
|
|
|
use Query\Operator;
|
|
|
|
class SplitVariationsToProductUtil
|
|
{
|
|
/**
|
|
* @var ProductUtils
|
|
*/
|
|
protected $productUtils;
|
|
|
|
/**
|
|
* @required
|
|
*/
|
|
public function setProductUtils(ProductUtils $productUtils): void
|
|
{
|
|
$this->productUtils = $productUtils;
|
|
}
|
|
|
|
/**
|
|
* Splits variations by choices label. Removes label from a product, creates n-1 new copies of the product for each n label values
|
|
* and splits the variations to them. If there's no label left on new products after operation, it removes last variation from products and
|
|
* copies data of variation to product.
|
|
*
|
|
* @param $idProduct int
|
|
* @param $idLabel int
|
|
* @param false $nameAppend
|
|
*/
|
|
public function splitByLabel($idProduct, $idLabel, $nameAppend = false)
|
|
{
|
|
$productsCreated = [];
|
|
|
|
sqlGetConnection()->transactional(
|
|
function () use ($idProduct, $idLabel, $nameAppend, &$productsCreated) {
|
|
$productLabelsVariations = sqlFetchAll(
|
|
sqlQueryBuilder()->select("GROUP_CONCAT(pv.id SEPARATOR ',') variations, pvc.id_value, pvcv.value")
|
|
->from('products_variations', 'pv')
|
|
->innerJoin('pv', 'products_variations_combination', 'pvc', 'pvc.id_variation = pv.id')
|
|
->innerJoin('pvc', 'products_variations_choices_values', 'pvcv', 'pvcv.id = pvc.id_value')
|
|
->where(
|
|
Operator::equals(
|
|
[
|
|
'pv.id_product' => $idProduct,
|
|
'pvc.id_label' => $idLabel,
|
|
]
|
|
)
|
|
)
|
|
->groupBy('pvc.id_value')
|
|
->execute(),
|
|
'id_value'
|
|
);
|
|
|
|
$this->removeLabelFromProduct($idProduct, $idLabel);
|
|
|
|
$productLabels = $this->getChoicesLabels($idProduct);
|
|
|
|
$originalProductLabel = array_key_last($productLabelsVariations);
|
|
|
|
foreach ($productLabelsVariations as $idLabel => $productLabelVariations) {
|
|
$variations = explode(',', $productLabelVariations['variations']);
|
|
$newIdProduct = $idProduct;
|
|
|
|
if ($originalProductLabel != $idLabel) {
|
|
$newIdProduct = $this->productUtils->duplicateProduct($idProduct);
|
|
$productsCreated[] = $newIdProduct;
|
|
$this->productUtils->copyProductData($idProduct, $newIdProduct, ['_all_' => true, 'pricelist' => 'products', 'photos' => 'products', 'variations' => false]);
|
|
|
|
foreach ($productLabels ?? [] as $label) {
|
|
sqlQueryBuilder()->insert('products_variations_choices_categorization')
|
|
->directValues(
|
|
[
|
|
'id_product' => $newIdProduct,
|
|
'id_label' => $label['id_label'],
|
|
'list_order' => $label['list_order'],
|
|
]
|
|
)
|
|
->execute();
|
|
}
|
|
|
|
$this->updateVariationProductId($variations, $newIdProduct);
|
|
}
|
|
|
|
if ($productLabels) {
|
|
$this->fixProductPrice($newIdProduct);
|
|
}
|
|
|
|
if ($nameAppend) {
|
|
$this->appendProductTitle($newIdProduct, $productLabelVariations['value']);
|
|
}
|
|
}
|
|
|
|
\Variations::recalcInStore();
|
|
|
|
if (!$productLabels) {
|
|
foreach ($productLabelsVariations as $productLabelVariations) {
|
|
$variations = explode(',', $productLabelVariations['variations']);
|
|
if (count($variations) == 1) {
|
|
$this->updateProductFromVariation($variations[0]);
|
|
} else {
|
|
throw new \Exception('A product without label has more than one variation.');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
return $productsCreated;
|
|
}
|
|
|
|
private function getChoicesLabels($idProduct)
|
|
{
|
|
return sqlQueryBuilder()->select('id_label, list_order')
|
|
->from('products_variations_choices_categorization')
|
|
->where(
|
|
Operator::equals(
|
|
[
|
|
'id_product' => $idProduct,
|
|
]
|
|
)
|
|
)
|
|
->execute()->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* Moves data to a product from its last variation and removes the variation.
|
|
*/
|
|
private function updateProductFromVariation($idVariation)
|
|
{
|
|
$fields = [
|
|
'id_product',
|
|
'code',
|
|
'price',
|
|
'ean',
|
|
'in_store',
|
|
'delivery_time',
|
|
'figure',
|
|
'updated',
|
|
'date_added',
|
|
'width',
|
|
'height',
|
|
'depth',
|
|
];
|
|
|
|
if (findModule(\Modules::PRODUCTS, \Modules::SUB_PRICE_BUY)) {
|
|
$fields[] = 'price_buy';
|
|
}
|
|
if (findModule(\Modules::PRODUCTS, \Modules::SUB_NOTE)) {
|
|
$fields[] = 'note';
|
|
}
|
|
if (findModule(\Modules::BONUS_PROGRAM)) {
|
|
$fields[] = 'bonus_points';
|
|
}
|
|
if (findModule('products', 'weight')) {
|
|
$fields[] = 'weight';
|
|
}
|
|
if (findModule(\Modules::MISSING_PRODUCTS)) {
|
|
$fields[] = 'in_store_min';
|
|
}
|
|
|
|
$variationFields = sqlQueryBuilder()
|
|
->select(implode(',', $fields))
|
|
->from('products_variations')
|
|
->where(Operator::equals(['id' => $idVariation]))
|
|
->execute()->fetch();
|
|
|
|
$idProduct = $variationFields['id_product'];
|
|
unset($variationFields['id_product']);
|
|
|
|
$this->cleanPriceListVariations($idProduct);
|
|
$this->removeVariationForeignKeys($idVariation);
|
|
|
|
sqlQueryBuilder()->delete('products_variations')
|
|
->where(Operator::equals(['id' => $idVariation]))
|
|
->execute();
|
|
|
|
sqlQueryBuilder()->update('products')
|
|
->directValues(
|
|
array_filter(
|
|
$variationFields,
|
|
function ($field) {
|
|
return !empty($field);
|
|
}
|
|
)
|
|
)
|
|
->where(Operator::equals(['id' => $idProduct]))
|
|
->execute();
|
|
}
|
|
|
|
/**
|
|
* Updates product ID of variations to given product ID in multiple places.
|
|
*
|
|
* @param $variations array
|
|
* @param $newIdProduct int
|
|
*/
|
|
private function updateVariationProductId($variations, $newIdProduct)
|
|
{
|
|
$update = function ($table, $idProductField = 'id_product', $idVariationField = 'id_variation') use ($variations, $newIdProduct) {
|
|
sqlQueryBuilder()->update($table)
|
|
->directValues([$idProductField => $newIdProduct])
|
|
->where(Operator::inStringArray($variations, $idVariationField))
|
|
->execute();
|
|
};
|
|
|
|
$update('products_variations', 'id_product', 'id');
|
|
$this->productUtils->updateTablesWithCallback($update);
|
|
}
|
|
|
|
private function cleanPriceListVariations($idProduct): void
|
|
{
|
|
if (findModule(\Modules::PRICELISTS)) {
|
|
sqlQueryBuilder()
|
|
->delete('pricelists_products')
|
|
->andWhere(Operator::equals(['id_product' => $idProduct]))
|
|
->andWhere(Operator::isNull('id_variation'))
|
|
->execute();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets null to all foreign keys of a variation.
|
|
*/
|
|
private function removeVariationForeignKeys($idVariation)
|
|
{
|
|
$update = function ($table, $idProductField = null, $idVariationField = 'id_variation') use ($idVariation) {
|
|
sqlQueryBuilder()->update($table)
|
|
->directValues([$idVariationField => null])
|
|
->where(Operator::equals([$idVariationField => $idVariation]))
|
|
->execute();
|
|
};
|
|
|
|
$this->productUtils->updateTablesWithCallback($update);
|
|
}
|
|
|
|
/**
|
|
* Sets the most frequent price of variations to a product and sets null to the variations with that price.
|
|
* If the variations already have null price before operation, then nothing happens.
|
|
*
|
|
* @param $idProduct int
|
|
*/
|
|
public function fixProductPrice($idProduct)
|
|
{
|
|
$hasNullPriceVariation = sqlQueryBuilder()->select('id')
|
|
->from('products_variations', 'pv')
|
|
->where(Operator::equals(['id_product' => $idProduct]))
|
|
->andWhere(Operator::equalsNullable(['price' => null]))
|
|
->execute()->fetch();
|
|
|
|
if ($hasNullPriceVariation) {
|
|
return;
|
|
}
|
|
|
|
$topPrice = sqlQueryBuilder()->select("price, GROUP_CONCAT(id SEPARATOR ',') variations")
|
|
->from('products_variations', 'pv')
|
|
->where(Operator::equals(['id_product' => $idProduct]))
|
|
->groupBy('price')
|
|
->orderBy('price')
|
|
->setMaxResults(1)
|
|
->execute()->fetch();
|
|
|
|
sqlQueryBuilder()->update('products')
|
|
->directValues(['price' => $topPrice['price']])
|
|
->where(Operator::equals(['id' => $idProduct]))
|
|
->execute();
|
|
|
|
sqlQueryBuilder()->update('products_variations')
|
|
->set('price', 'null')
|
|
->where(Operator::inStringArray(explode(',', $topPrice['variations']), 'id'))
|
|
->execute();
|
|
}
|
|
|
|
/**
|
|
* Appends value to the title of product.
|
|
*
|
|
* @param $idProduct int
|
|
* @param $value string
|
|
*/
|
|
private function appendProductTitle($idProduct, $value)
|
|
{
|
|
sqlQueryBuilder()->update('products')
|
|
->set('title', "CONCAT(title, ' - ', :append)")
|
|
->where(Operator::equals(['id' => $idProduct]))
|
|
->setParameter('append', $value)
|
|
->execute();
|
|
}
|
|
|
|
/**
|
|
* Removes label from a product and its variations.
|
|
*
|
|
* @param $idProduct int
|
|
* @param $idLabel int
|
|
*/
|
|
private function removeLabelFromProduct($idProduct, $idLabel)
|
|
{
|
|
$variations = sqlFetchAll(
|
|
sqlQueryBuilder()->select('id')
|
|
->from('products_variations')
|
|
->where(Operator::equals(['id_product' => $idProduct]))
|
|
->execute(),
|
|
['id' => 'id']
|
|
);
|
|
|
|
sqlQueryBuilder()->delete('products_variations_combination')
|
|
->where(Operator::equals(['id_label' => $idLabel]))
|
|
->andWhere(Operator::inStringArray(array_values($variations), 'id_variation'))
|
|
->execute();
|
|
|
|
sqlQueryBuilder()->delete('products_variations_choices_categorization')
|
|
->where(
|
|
Operator::equals(
|
|
[
|
|
'id_product' => $idProduct,
|
|
'id_label' => $idLabel,
|
|
]
|
|
)
|
|
)
|
|
->execute();
|
|
}
|
|
}
|