core/lib/Thelia/Action/Product.php line 788
- <?php
- /*
- * This file is part of the Thelia package.
- * http://www.thelia.net
- *
- * (c) OpenStudio <info@thelia.net>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace Thelia\Action;
- use Propel\Runtime\ActiveQuery\Criteria;
- use Propel\Runtime\Exception\PropelException;
- use Propel\Runtime\Propel;
- use Symfony\Component\EventDispatcher\EventDispatcherInterface;
- use Symfony\Component\EventDispatcher\EventSubscriberInterface;
- use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
- use Thelia\Core\Event\Feature\FeatureAvCreateEvent;
- use Thelia\Core\Event\Feature\FeatureAvDeleteEvent;
- use Thelia\Core\Event\FeatureProduct\FeatureProductDeleteEvent;
- use Thelia\Core\Event\FeatureProduct\FeatureProductUpdateEvent;
- use Thelia\Core\Event\File\FileDeleteEvent;
- use Thelia\Core\Event\Product\ProductAddAccessoryEvent;
- use Thelia\Core\Event\Product\ProductAddCategoryEvent;
- use Thelia\Core\Event\Product\ProductAddContentEvent;
- use Thelia\Core\Event\Product\ProductCloneEvent;
- use Thelia\Core\Event\Product\ProductCreateEvent;
- use Thelia\Core\Event\Product\ProductDeleteAccessoryEvent;
- use Thelia\Core\Event\Product\ProductDeleteCategoryEvent;
- use Thelia\Core\Event\Product\ProductDeleteContentEvent;
- use Thelia\Core\Event\Product\ProductDeleteEvent;
- use Thelia\Core\Event\Product\ProductSetTemplateEvent;
- use Thelia\Core\Event\Product\ProductToggleVisibilityEvent;
- use Thelia\Core\Event\Product\ProductUpdateEvent;
- use Thelia\Core\Event\ProductSaleElement\ProductSaleElementDeleteEvent;
- use Thelia\Core\Event\Template\TemplateDeleteAttributeEvent;
- use Thelia\Core\Event\Template\TemplateDeleteFeatureEvent;
- use Thelia\Core\Event\TheliaEvents;
- use Thelia\Core\Event\UpdatePositionEvent;
- use Thelia\Core\Event\UpdateSeoEvent;
- use Thelia\Core\Event\ViewCheckEvent;
- use Thelia\Model\Accessory;
- use Thelia\Model\AccessoryQuery;
- use Thelia\Model\AttributeTemplateQuery;
- use Thelia\Model\Currency as CurrencyModel;
- use Thelia\Model\FeatureAvI18n;
- use Thelia\Model\FeatureAvI18nQuery;
- use Thelia\Model\FeatureAvQuery;
- use Thelia\Model\FeatureProduct;
- use Thelia\Model\FeatureProductQuery;
- use Thelia\Model\FeatureTemplateQuery;
- use Thelia\Model\Map\AttributeTemplateTableMap;
- use Thelia\Model\Map\FeatureTemplateTableMap;
- use Thelia\Model\Map\ProductSaleElementsTableMap;
- use Thelia\Model\Map\ProductTableMap;
- use Thelia\Model\Product as ProductModel;
- use Thelia\Model\ProductAssociatedContent;
- use Thelia\Model\ProductAssociatedContentQuery;
- use Thelia\Model\ProductCategory;
- use Thelia\Model\ProductCategoryQuery;
- use Thelia\Model\ProductDocument;
- use Thelia\Model\ProductDocumentQuery;
- use Thelia\Model\ProductI18n;
- use Thelia\Model\ProductI18nQuery;
- use Thelia\Model\ProductImage;
- use Thelia\Model\ProductImageQuery;
- use Thelia\Model\ProductPrice;
- use Thelia\Model\ProductPriceQuery;
- use Thelia\Model\ProductQuery;
- use Thelia\Model\ProductSaleElementsQuery;
- use Thelia\Model\TaxRuleQuery;
- class Product extends BaseAction implements EventSubscriberInterface
- {
- /** @var EventDispatcherInterface */
- protected $eventDispatcher;
- public function __construct(EventDispatcherInterface $eventDispatcher)
- {
- $this->eventDispatcher = $eventDispatcher;
- }
- /**
- * Create a new product entry.
- */
- public function create(ProductCreateEvent $event): void
- {
- $defaultTaxRuleId = null;
- if (null !== $defaultTaxRule = TaxRuleQuery::create()->findOneByIsDefault(true)) {
- $defaultTaxRuleId = $defaultTaxRule->getId();
- }
- $product = new ProductModel();
- $product
- ->setRef($event->getRef())
- ->setLocale($event->getLocale())
- ->setTitle($event->getTitle())
- ->setVisible($event->getVisible() ? 1 : 0)
- ->setVirtual($event->getVirtual() ? 1 : 0)
- ->setTemplateId($event->getTemplateId())
- ->create(
- $event->getDefaultCategory(),
- $event->getBasePrice(),
- $event->getCurrencyId(),
- // Set the default tax rule if not defined
- $event->getTaxRuleId() ?: $defaultTaxRuleId,
- $event->getBaseWeight(),
- $event->getBaseQuantity()
- )
- ;
- $event->setProduct($product);
- }
- /*******************
- * CLONING PROCESS *
- *******************/
- /**
- * @throws \Exception
- */
- public function cloneProduct(ProductCloneEvent $event): void
- {
- $con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
- $con->beginTransaction();
- try {
- // Get important datas
- $lang = $event->getLang();
- $originalProduct = $event->getOriginalProduct();
- if (null === $originalProductDefaultI18n = ProductI18nQuery::create()
- ->findPk([$originalProduct->getId(), $lang])) {
- // No i18n entry for the current language. Try to find one for creating the product.
- // It will be updated later by updateClone()
- $originalProductDefaultI18n = ProductI18nQuery::create()
- ->findOneById($originalProduct->getId())
- ;
- }
- $originalProductDefaultPrice = ProductPriceQuery::create()
- ->findOneByProductSaleElementsId($originalProduct->getDefaultSaleElements()->getId());
- // Cloning process
- $this->createClone($event, $originalProductDefaultI18n, $originalProductDefaultPrice);
- $this->updateClone($event, $originalProductDefaultPrice);
- $this->cloneFeatureCombination($event);
- $this->cloneAssociatedContent($event);
- $this->cloneAccessories($event);
- $this->cloneAdditionalCategories($event);
- // Dispatch event for file cloning
- $this->eventDispatcher->dispatch($event, TheliaEvents::FILE_CLONE);
- // Dispatch event for PSE cloning
- $this->eventDispatcher->dispatch($event, TheliaEvents::PSE_CLONE);
- $con->commit();
- } catch (\Exception $e) {
- $con->rollBack();
- throw $e;
- }
- }
- public function createClone(ProductCloneEvent $event, ProductI18n $originalProductDefaultI18n, ProductPrice $originalProductDefaultPrice): void
- {
- // Build event and dispatch creation of the clone product
- $createCloneEvent = new ProductCreateEvent();
- $createCloneEvent
- ->setTitle($originalProductDefaultI18n->getTitle())
- ->setRef($event->getRef())
- ->setLocale($event->getLang())
- ->setVisible(0)
- ->setQuantity(0)
- ->setVirtual($event->getOriginalProduct()->getVirtual())
- ->setTaxRuleId($event->getOriginalProduct()->getTaxRuleId())
- ->setDefaultCategory($event->getOriginalProduct()->getDefaultCategoryId())
- ->setBasePrice($originalProductDefaultPrice->getPrice())
- ->setCurrencyId($originalProductDefaultPrice->getCurrencyId())
- ->setBaseWeight($event->getOriginalProduct()->getDefaultSaleElements()->getWeight());
- $this->eventDispatcher->dispatch($createCloneEvent, TheliaEvents::PRODUCT_CREATE);
- $event->setClonedProduct($createCloneEvent->getProduct());
- }
- public function updateClone(ProductCloneEvent $event, ProductPrice $originalProductDefaultPrice): void
- {
- // Get original product's I18ns
- $originalProductI18ns = ProductI18nQuery::create()
- ->findById($event->getOriginalProduct()->getId());
- $clonedProductUpdateEvent = new ProductUpdateEvent($event->getClonedProduct()->getId());
- /** @var ProductI18n $originalProductI18n */
- foreach ($originalProductI18ns as $originalProductI18n) {
- $clonedProductUpdateEvent
- ->setRef($event->getClonedProduct()->getRef())
- ->setVisible($event->getClonedProduct()->getVisible())
- ->setVirtual($event->getClonedProduct()->getVirtual())
- ->setLocale($originalProductI18n->getLocale())
- ->setTitle($originalProductI18n->getTitle())
- ->setChapo($originalProductI18n->getChapo())
- ->setDescription($originalProductI18n->getDescription())
- ->setPostscriptum($originalProductI18n->getPostscriptum())
- ->setBasePrice($originalProductDefaultPrice->getPrice())
- ->setCurrencyId($originalProductDefaultPrice->getCurrencyId())
- ->setBaseWeight($event->getOriginalProduct()->getDefaultSaleElements()->getWeight())
- ->setTaxRuleId($event->getOriginalProduct()->getTaxRuleId())
- ->setBrandId($event->getOriginalProduct()->getBrandId())
- ->setDefaultCategory($event->getOriginalProduct()->getDefaultCategoryId());
- $this->eventDispatcher->dispatch($clonedProductUpdateEvent, TheliaEvents::PRODUCT_UPDATE);
- // SEO info
- $clonedProductUpdateSeoEvent = new UpdateSeoEvent($event->getClonedProduct()->getId());
- $clonedProductUpdateSeoEvent
- ->setLocale($originalProductI18n->getLocale())
- ->setMetaTitle($originalProductI18n->getMetaTitle())
- ->setMetaDescription($originalProductI18n->getMetaDescription())
- ->setMetaKeywords($originalProductI18n->getMetaKeywords())
- ->setUrl(null);
- $this->eventDispatcher->dispatch($clonedProductUpdateSeoEvent, TheliaEvents::PRODUCT_UPDATE_SEO);
- }
- $event->setClonedProduct($clonedProductUpdateEvent->getProduct());
- // Set clone's template
- $clonedProductUpdateTemplateEvent = new ProductSetTemplateEvent(
- $event->getClonedProduct(),
- $event->getOriginalProduct()->getTemplateId(),
- $originalProductDefaultPrice->getCurrencyId()
- );
- $this->eventDispatcher->dispatch($clonedProductUpdateTemplateEvent, TheliaEvents::PRODUCT_SET_TEMPLATE);
- }
- public function cloneFeatureCombination(ProductCloneEvent $event): void
- {
- // Get original product FeatureProduct list
- $originalProductFeatureList = FeatureProductQuery::create()
- ->findByProductId($event->getOriginalProduct()->getId());
- // Set clone product FeatureProducts
- /** @var FeatureProduct $originalProductFeature */
- foreach ($originalProductFeatureList as $originalProductFeature) {
- // Get original FeatureAvI18n list
- $originalProductFeatureAvI18nList = FeatureAvI18nQuery::create()
- ->findById($originalProductFeature->getFeatureAvId());
- /** @var FeatureAvI18n $originalProductFeatureAvI18n */
- foreach ($originalProductFeatureAvI18nList as $originalProductFeatureAvI18n) {
- // Create a FeatureProduct for each FeatureAv (not for each FeatureAvI18n)
- $clonedProductCreateFeatureEvent = new FeatureProductUpdateEvent(
- $event->getClonedProduct()->getId(),
- $originalProductFeature->getFeatureId(),
- $originalProductFeature->getFeatureAvId()
- );
- $clonedProductCreateFeatureEvent->setLocale($originalProductFeatureAvI18n->getLocale());
- // If it's a free text value, pass the FeatureAvI18n's title as featureValue to the event
- if ($originalProductFeature->getIsFreeText()) {
- $clonedProductCreateFeatureEvent->setFeatureValue($originalProductFeatureAvI18n->getTitle());
- $clonedProductCreateFeatureEvent->setIsTextValue(true);
- }
- $this->eventDispatcher->dispatch($clonedProductCreateFeatureEvent, TheliaEvents::PRODUCT_FEATURE_UPDATE_VALUE);
- }
- }
- }
- public function cloneAssociatedContent(ProductCloneEvent $event): void
- {
- // Get original product associated contents
- $originalProductAssocConts = ProductAssociatedContentQuery::create()
- ->findByProductId($event->getOriginalProduct()->getId());
- // Set clone product associated contents
- /** @var ProductAssociatedContent $originalProductAssocCont */
- foreach ($originalProductAssocConts as $originalProductAssocCont) {
- $clonedProductCreatePAC = new ProductAddContentEvent($event->getClonedProduct(), $originalProductAssocCont->getContentId());
- $this->eventDispatcher->dispatch($clonedProductCreatePAC, TheliaEvents::PRODUCT_ADD_CONTENT);
- }
- }
- public function cloneAccessories(ProductCloneEvent $event): void
- {
- // Get original product accessories
- $originalProductAccessoryList = AccessoryQuery::create()
- ->findByProductId($event->getOriginalProduct()->getId());
- // Set clone product accessories
- /** @var Accessory $originalProductAccessory */
- foreach ($originalProductAccessoryList as $originalProductAccessory) {
- $clonedProductAddAccessoryEvent = new ProductAddAccessoryEvent($event->getClonedProduct(), $originalProductAccessory->getAccessory());
- $this->eventDispatcher->dispatch($clonedProductAddAccessoryEvent, TheliaEvents::PRODUCT_ADD_ACCESSORY);
- }
- }
- public function cloneAdditionalCategories(ProductCloneEvent $event): void
- {
- // Get original product additional categories
- $originalProductAdditionalCategoryList = ProductCategoryQuery::create()
- ->filterByProductId($event->getOriginalProduct()->getId())
- ->filterByDefaultCategory(false)
- ->find();
- // Set clone product additional categories
- /** @var ProductCategory $originalProductCategory */
- foreach ($originalProductAdditionalCategoryList as $originalProductCategory) {
- $clonedProductAddCategoryEvent = new ProductAddCategoryEvent($event->getClonedProduct(), $originalProductCategory->getCategoryId());
- $this->eventDispatcher->dispatch($clonedProductAddCategoryEvent, TheliaEvents::PRODUCT_ADD_CATEGORY);
- }
- }
- /***************
- * END CLONING *
- ***************/
- /**
- * Change a product.
- *
- * @throws PropelException
- * @throws \Exception
- */
- public function update(ProductUpdateEvent $event): void
- {
- if (null !== $product = ProductQuery::create()->findPk($event->getProductId())) {
- $con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
- $con->beginTransaction();
- try {
- $prevRef = $product->getRef();
- $product
- ->setRef($event->getRef())
- ->setLocale($event->getLocale())
- ->setTitle($event->getTitle())
- ->setDescription($event->getDescription())
- ->setChapo($event->getChapo())
- ->setPostscriptum($event->getPostscriptum())
- ->setVisible($event->getVisible() ? 1 : 0)
- ->setVirtual($event->getVirtual() ? 1 : 0)
- ->setBrandId($event->getBrandId() <= 0 ? null : $event->getBrandId())
- ->save($con)
- ;
- // Update default PSE (if product has no attributes and the product's ref change)
- $defaultPseRefChange = $prevRef !== $product->getRef()
- && 0 === $product->getDefaultSaleElements()->countAttributeCombinations();
- if ($defaultPseRefChange) {
- $defaultPse = $product->getDefaultSaleElements();
- $defaultPse->setRef($product->getRef())->save();
- }
- // Update default category (if required)
- $product->setDefaultCategory($event->getDefaultCategory());
- $event->setProduct($product);
- $con->commit();
- } catch (PropelException $e) {
- $con->rollBack();
- throw $e;
- }
- }
- }
- public function updateSeo(UpdateSeoEvent $event, $eventName, EventDispatcherInterface $dispatcher)
- {
- return $this->genericUpdateSeo(ProductQuery::create(), $event, $dispatcher);
- }
- /**
- * Delete a product entry.
- *
- * @throws \Exception
- */
- public function delete(ProductDeleteEvent $event): void
- {
- if (null !== $product = ProductQuery::create()->findPk($event->getProductId())) {
- $con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
- $con->beginTransaction();
- try {
- $fileList = ['images' => [], 'documentList' => []];
- // Get product's files to delete after product deletion
- $fileList['images']['list'] = ProductImageQuery::create()
- ->findByProductId($event->getProductId());
- $fileList['images']['type'] = TheliaEvents::IMAGE_DELETE;
- $fileList['documentList']['list'] = ProductDocumentQuery::create()
- ->findByProductId($event->getProductId());
- $fileList['documentList']['type'] = TheliaEvents::DOCUMENT_DELETE;
- // Delete product
- $product
- ->delete($con)
- ;
- $event->setProduct($product);
- // Dispatch delete product's files event
- foreach ($fileList as $fileTypeList) {
- foreach ($fileTypeList['list'] as $fileToDelete) {
- $fileDeleteEvent = new FileDeleteEvent($fileToDelete);
- $this->eventDispatcher->dispatch($fileDeleteEvent, $fileTypeList['type']);
- }
- }
- $con->commit();
- } catch (\Exception $e) {
- $con->rollBack();
- throw $e;
- }
- }
- }
- /**
- * Toggle product visibility. No form used here.
- */
- public function toggleVisibility(ProductToggleVisibilityEvent $event): void
- {
- $product = $event->getProduct();
- $product
- ->setVisible($product->getVisible() ? false : true)
- ->save()
- ;
- $event->setProduct($product);
- }
- /**
- * Changes position, selecting absolute ou relative change.
- */
- public function updatePosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher): void
- {
- $this->genericUpdateDelegatePosition(
- ProductCategoryQuery::create()
- ->filterByProductId($event->getObjectId())
- ->filterByCategoryId($event->getReferrerId()),
- $event,
- $dispatcher
- );
- }
- public function addContent(ProductAddContentEvent $event): void
- {
- if (ProductAssociatedContentQuery::create()
- ->filterByContentId($event->getContentId())
- ->filterByProduct($event->getProduct())->count() <= 0) {
- $content = new ProductAssociatedContent();
- $content
- ->setProduct($event->getProduct())
- ->setContentId($event->getContentId())
- ->save()
- ;
- }
- }
- public function removeContent(ProductDeleteContentEvent $event): void
- {
- $content = ProductAssociatedContentQuery::create()
- ->filterByContentId($event->getContentId())
- ->filterByProduct($event->getProduct())->findOne()
- ;
- if ($content !== null) {
- $content
- ->delete()
- ;
- }
- }
- public function addCategory(ProductAddCategoryEvent $event): void
- {
- if (ProductCategoryQuery::create()
- ->filterByProduct($event->getProduct())
- ->filterByCategoryId($event->getCategoryId())
- ->count() <= 0) {
- $productCategory = (new ProductCategory())
- ->setProduct($event->getProduct())
- ->setCategoryId($event->getCategoryId())
- ->setDefaultCategory(false);
- $productCategory
- ->setPosition($productCategory->getNextPosition())
- ->save();
- }
- }
- public function removeCategory(ProductDeleteCategoryEvent $event): void
- {
- $productCategory = ProductCategoryQuery::create()
- ->filterByProduct($event->getProduct())
- ->filterByCategoryId($event->getCategoryId())
- ->findOne();
- if ($productCategory != null) {
- $productCategory->delete();
- }
- }
- public function addAccessory(ProductAddAccessoryEvent $event): void
- {
- if (AccessoryQuery::create()
- ->filterByAccessory($event->getAccessoryId())
- ->filterByProductId($event->getProduct()->getId())->count() <= 0) {
- $accessory = new Accessory();
- $accessory
- ->setProductId($event->getProduct()->getId())
- ->setAccessory($event->getAccessoryId())
- ->save()
- ;
- }
- }
- public function removeAccessory(ProductDeleteAccessoryEvent $event): void
- {
- $accessory = AccessoryQuery::create()
- ->filterByAccessory($event->getAccessoryId())
- ->filterByProductId($event->getProduct()->getId())->findOne()
- ;
- if ($accessory !== null) {
- $accessory
- ->delete()
- ;
- }
- }
- public function setProductTemplate(ProductSetTemplateEvent $event): void
- {
- $con = Propel::getWriteConnection(ProductTableMap::DATABASE_NAME);
- $con->beginTransaction();
- try {
- $product = $event->getProduct();
- // Check differences between current coobination and the next one, and clear obsoletes values.
- $nextTemplateId = $event->getTemplateId();
- $currentTemplateId = $product->getTemplateId();
- // 1. Process product features.
- $currentFeatures = FeatureTemplateQuery::create()
- ->filterByTemplateId($currentTemplateId)
- ->select([FeatureTemplateTableMap::COL_FEATURE_ID])
- ->find($con);
- $nextFeatures = FeatureTemplateQuery::create()
- ->filterByTemplateId($nextTemplateId)
- ->select([FeatureTemplateTableMap::COL_FEATURE_ID])
- ->find($con);
- // Find features values we shoud delete. To do this, we have to
- // find all features in $currentFeatures that are not present in $nextFeatures
- $featuresToDelete = array_diff($currentFeatures->getData(), $nextFeatures->getData());
- // Delete obsolete features values
- foreach ($featuresToDelete as $featureId) {
- $this->eventDispatcher->dispatch(
- new FeatureProductDeleteEvent($product->getId(), $featureId),
- TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE
- );
- }
- // 2. Process product Attributes
- $currentAttributes = AttributeTemplateQuery::create()
- ->filterByTemplateId($currentTemplateId)
- ->select([AttributeTemplateTableMap::COL_ATTRIBUTE_ID])
- ->find($con);
- $nextAttributes = AttributeTemplateQuery::create()
- ->filterByTemplateId($nextTemplateId)
- ->select([AttributeTemplateTableMap::COL_ATTRIBUTE_ID])
- ->find($con);
- // Find attributes values we shoud delete. To do this, we have to
- // find all attributes in $currentAttributes that are not present in $nextAttributes
- $attributesToDelete = array_diff($currentAttributes->getData(), $nextAttributes->getData());
- // Find PSE which includes $attributesToDelete for the current product/
- $pseToDelete = ProductSaleElementsQuery::create()
- ->filterByProductId($product->getId())
- ->useAttributeCombinationQuery()
- ->filterByAttributeId($attributesToDelete, Criteria::IN)
- ->endUse()
- ->select([ProductSaleElementsTableMap::COL_ID])
- ->find();
- // Delete obsolete PSEs
- foreach ($pseToDelete->getData() as $pseId) {
- $this->eventDispatcher->dispatch(
- new ProductSaleElementDeleteEvent(
- $pseId,
- CurrencyModel::getDefaultCurrency()->getId()
- ),
- TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT
- );
- }
- // Update the product template
- $template_id = $event->getTemplateId();
- // Set it to null if it's zero.
- if ($template_id <= 0) {
- $template_id = null;
- }
- $product->setTemplateId($template_id)->save($con);
- $product->clearProductSaleElementss();
- $event->setProduct($product);
- // Store all the stuff !
- $con->commit();
- } catch (\Exception $ex) {
- $con->rollBack();
- throw $ex;
- }
- }
- /**
- * Changes accessry position, selecting absolute ou relative change.
- *
- * @return object
- */
- public function updateAccessoryPosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
- {
- return $this->genericUpdatePosition(AccessoryQuery::create(), $event, $dispatcher);
- }
- /**
- * Changes position, selecting absolute ou relative change.
- *
- * @return object
- */
- public function updateContentPosition(UpdatePositionEvent $event, $eventName, EventDispatcherInterface $dispatcher)
- {
- return $this->genericUpdatePosition(ProductAssociatedContentQuery::create(), $event, $dispatcher);
- }
- /**
- * Update the value of a product feature.
- */
- public function updateFeatureProductValue(FeatureProductUpdateEvent $event): void
- {
- // Prepare the FeatureAv's ID
- $featureAvId = $event->getFeatureValue();
- // Search for existing FeatureProduct
- $featureProductQuery = FeatureProductQuery::create()
- ->filterByProductId($event->getProductId())
- ->filterByFeatureId($event->getFeatureId())
- ;
- // If it's not a free text value, we can filter by the event's featureValue (which is an ID)
- if ($event->getFeatureValue() !== null && $event->getIsTextValue() === false) {
- $featureProductQuery->filterByFeatureAvId($featureAvId);
- }
- $featureProduct = $featureProductQuery->findOne();
- // If the FeatureProduct does not exist, create it
- if ($featureProduct === null) {
- $featureProduct = new FeatureProduct();
- $featureProduct
- ->setProductId($event->getProductId())
- ->setFeatureId($event->getFeatureId())
- ;
- // If it's a free text value, create a FeatureAv to handle i18n
- if ($event->getIsTextValue() === true) {
- $featureProduct->setIsFreeText(true);
- $createFeatureAvEvent = new FeatureAvCreateEvent();
- $createFeatureAvEvent
- ->setFeatureId($event->getFeatureId())
- ->setLocale($event->getLocale())
- ->setTitle($event->getFeatureValue());
- $this->eventDispatcher->dispatch($createFeatureAvEvent, TheliaEvents::FEATURE_AV_CREATE);
- $featureAvId = $createFeatureAvEvent->getFeatureAv()->getId();
- }
- } // Else if the FeatureProduct exists and is a free text value
- elseif ($featureProduct !== null && $event->getIsTextValue() === true) {
- // Get the FeatureAv
- $freeTextFeatureAv = FeatureAvQuery::create()
- ->filterByFeatureProduct($featureProduct)
- ->findOneByFeatureId($event->getFeatureId());
- // Get the FeatureAvI18n by locale
- $freeTextFeatureAvI18n = FeatureAvI18nQuery::create()
- ->filterById($freeTextFeatureAv->getId())
- ->findOneByLocale($event->getLocale());
- // Nothing found for this lang and the new value is not empty : create FeatureAvI18n
- if ($freeTextFeatureAvI18n === null && !empty($featureAvId)) {
- $featureAvI18n = new FeatureAvI18n();
- $featureAvI18n
- ->setId($freeTextFeatureAv->getId())
- ->setLocale($event->getLocale())
- ->setTitle($event->getFeatureValue())
- ->save();
- $featureAvId = $featureAvI18n->getId();
- } // Else if i18n exists but new value is empty : delete FeatureAvI18n
- elseif ($freeTextFeatureAvI18n !== null && empty($featureAvId)) {
- $freeTextFeatureAvI18n->delete();
- // Check if there are still some FeatureAvI18n for this FeatureAv
- $freeTextFeatureAvI18ns = FeatureAvI18nQuery::create()
- ->findById($freeTextFeatureAv->getId());
- // If there are no more FeatureAvI18ns for this FeatureAv, remove the corresponding FeatureProduct & FeatureAv
- if (\count($freeTextFeatureAvI18ns) == 0) {
- $deleteFeatureProductEvent = new FeatureProductDeleteEvent($event->getProductId(), $event->getFeatureId());
- $this->eventDispatcher->dispatch($deleteFeatureProductEvent, TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE);
- $deleteFeatureAvEvent = new FeatureAvDeleteEvent($freeTextFeatureAv->getId());
- $this->eventDispatcher->dispatch($deleteFeatureAvEvent, TheliaEvents::FEATURE_AV_DELETE);
- }
- return;
- } // Else if a FeatureAvI18n is found and the new value is not empty : update existing FeatureAvI18n
- elseif ($freeTextFeatureAvI18n !== null && !empty($featureAvId)) {
- $freeTextFeatureAvI18n->setTitle($featureAvId);
- $freeTextFeatureAvI18n->save();
- $featureAvId = $freeTextFeatureAvI18n->getId();
- } // To prevent Integrity constraint violation
- elseif (empty($featureAvId)) {
- return;
- }
- }
- $featureProduct->setFeatureAvId($featureAvId);
- $featureProduct->save();
- $event->setFeatureProduct($featureProduct);
- }
- /**
- * Delete a product feature value.
- */
- public function deleteFeatureProductValue(FeatureProductDeleteEvent $event): void
- {
- FeatureProductQuery::create()
- ->filterByProductId($event->getProductId())
- ->filterByFeatureId($event->getFeatureId())
- ->delete()
- ;
- }
- public function deleteImagePSEAssociations(FileDeleteEvent $event): void
- {
- $model = $event->getFileToDelete();
- if ($model instanceof ProductImage) {
- $model->getProductSaleElementsProductImages()->delete();
- }
- }
- public function deleteDocumentPSEAssociations(FileDeleteEvent $event): void
- {
- $model = $event->getFileToDelete();
- if ($model instanceof ProductDocument) {
- $model->getProductSaleElementsProductDocuments()->delete();
- }
- }
- /**
- * When a feature is removed from a template, the products which are using this feature should be updated.
- */
- public function deleteTemplateFeature(TemplateDeleteFeatureEvent $event, string $eventName, EventDispatcherInterface $dispatcher): void
- {
- // Detete the removed feature in all products which are using this template
- $products = ProductQuery::create()
- ->filterByTemplateId($event->getTemplate()->getId())
- ->find()
- ;
- foreach ($products as $product) {
- $dispatcher->dispatch(
- new FeatureProductDeleteEvent($product->getId(), $event->getFeatureId()),
- TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE
- );
- }
- }
- /**
- * When an attribute is removed from a template, the conbinations and PSE of products which are using this template
- * should be updated.
- */
- public function deleteTemplateAttribute(TemplateDeleteAttributeEvent $event, string $eventName, EventDispatcherInterface $dispatcher): void
- {
- // Detete the removed attribute in all products which are using this template
- $pseToDelete = ProductSaleElementsQuery::create()
- ->useProductQuery()
- ->filterByTemplateId($event->getTemplate()->getId())
- ->endUse()
- ->useAttributeCombinationQuery()
- ->filterByAttributeId($event->getAttributeId())
- ->endUse()
- ->select([ProductSaleElementsTableMap::COL_ID])
- ->find();
- $currencyId = CurrencyModel::getDefaultCurrency()->getId();
- foreach ($pseToDelete->getData() as $pseId) {
- $dispatcher->dispatch(
- new ProductSaleElementDeleteEvent(
- $pseId,
- $currencyId
- ),
- TheliaEvents::PRODUCT_DELETE_PRODUCT_SALE_ELEMENT
- );
- }
- }
- /**
- * Check if is a product view and if product_id is visible.
- *
- * @param string $eventName
- */
- public function viewCheck(ViewCheckEvent $event, $eventName, EventDispatcherInterface $dispatcher): void
- {
- if ($event->getView() == 'product') {
- $product = ProductQuery::create()
- ->filterById($event->getViewId())
- ->filterByVisible(1)
- ->count();
- if ($product == 0) {
- $dispatcher->dispatch($event, TheliaEvents::VIEW_PRODUCT_ID_NOT_VISIBLE);
- }
- }
- }
- /**
- * @throws NotFoundHttpException
- */
- public function viewProductIdNotVisible(ViewCheckEvent $event): void
- {
- throw new NotFoundHttpException();
- }
- /**
- * {@inheritDoc}
- */
- public static function getSubscribedEvents()
- {
- return [
- TheliaEvents::PRODUCT_CREATE => ['create', 128],
- TheliaEvents::PRODUCT_CLONE => ['cloneProduct', 128],
- TheliaEvents::PRODUCT_UPDATE => ['update', 128],
- TheliaEvents::PRODUCT_DELETE => ['delete', 128],
- TheliaEvents::PRODUCT_TOGGLE_VISIBILITY => ['toggleVisibility', 128],
- TheliaEvents::PRODUCT_UPDATE_POSITION => ['updatePosition', 128],
- TheliaEvents::PRODUCT_UPDATE_SEO => ['updateSeo', 128],
- TheliaEvents::PRODUCT_ADD_CONTENT => ['addContent', 128],
- TheliaEvents::PRODUCT_REMOVE_CONTENT => ['removeContent', 128],
- TheliaEvents::PRODUCT_UPDATE_CONTENT_POSITION => ['updateContentPosition', 128],
- TheliaEvents::PRODUCT_ADD_ACCESSORY => ['addAccessory', 128],
- TheliaEvents::PRODUCT_REMOVE_ACCESSORY => ['removeAccessory', 128],
- TheliaEvents::PRODUCT_UPDATE_ACCESSORY_POSITION => ['updateAccessoryPosition', 128],
- TheliaEvents::PRODUCT_ADD_CATEGORY => ['addCategory', 128],
- TheliaEvents::PRODUCT_REMOVE_CATEGORY => ['removeCategory', 128],
- TheliaEvents::PRODUCT_SET_TEMPLATE => ['setProductTemplate', 128],
- TheliaEvents::PRODUCT_FEATURE_UPDATE_VALUE => ['updateFeatureProductValue', 128],
- TheliaEvents::PRODUCT_FEATURE_DELETE_VALUE => ['deleteFeatureProductValue', 128],
- TheliaEvents::TEMPLATE_DELETE_ATTRIBUTE => ['deleteTemplateAttribute', 128],
- TheliaEvents::TEMPLATE_DELETE_FEATURE => ['deleteTemplateFeature', 128],
- // Those two have to be executed before
- TheliaEvents::IMAGE_DELETE => ['deleteImagePSEAssociations', 192],
- TheliaEvents::DOCUMENT_DELETE => ['deleteDocumentPSEAssociations', 192],
- TheliaEvents::VIEW_CHECK => ['viewCheck', 128],
- TheliaEvents::VIEW_PRODUCT_ID_NOT_VISIBLE => ['viewProductIdNotVisible', 128],
- ];
- }
- }