custom/plugins/AcrisShopSwitchCS/src/Components/ShopSwitchHelperService.php line 119

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Acris\ShopSwitch\Components;
  3. use Acris\ShopSwitch\Components\Exception\ActiveDomainNotFoundException;
  4. use Acris\ShopSwitch\Components\Exception\IpNotFoundException;
  5. use Acris\ShopSwitch\Components\GeoTargeting\Ip2CountryService;
  6. use Acris\ShopSwitch\Components\GeoTargeting\IpService;
  7. use Acris\ShopSwitch\Components\ShopSwitch\ShopSwitchResult;
  8. use Acris\ShopSwitch\Components\ShopSwitch\ShopSwitchRouteResponse;
  9. use Acris\ShopSwitch\Components\Struct\AbstractPossibleStruct;
  10. use Acris\ShopSwitch\Components\Struct\PossibleCombinationCollection;
  11. use Acris\ShopSwitch\Components\Struct\PossibleCombinationStruct;
  12. use Acris\ShopSwitch\Components\Struct\PossibleCountryCollection;
  13. use Acris\ShopSwitch\Components\Struct\PossibleCountryStruct;
  14. use Acris\ShopSwitch\Components\Struct\PossibleLanguageCollection;
  15. use Acris\ShopSwitch\Components\Struct\PossibleLanguageStruct;
  16. use Acris\ShopSwitch\Custom\ShopSwitchRuleDefinition;
  17. use Acris\ShopSwitch\Custom\ShopSwitchRuleDomainEntity;
  18. use Acris\ShopSwitch\Custom\ShopSwitchRuleEntity;
  19. use Acris\ShopSwitch\Storefront\Controller\ShopSwitchController;
  20. use Shopware\Core\Framework\Context;
  21. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  22. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  23. use Shopware\Core\Framework\Struct\Collection;
  24. use Shopware\Core\SalesChannelRequest;
  25. use Shopware\Core\System\SalesChannel\Aggregate\SalesChannelDomain\SalesChannelDomainEntity;
  26. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  27. use Shopware\Core\System\SalesChannel\SalesChannelEntity;
  28. use Symfony\Component\HttpFoundation\Request;
  29. /**
  30.  * Helper Servce for ShopSwitchRoute!
  31.  * Aim: To reduce the amount of code in ShopSwitchroute
  32.  * Why: ShopSwitchRoute becomes too complex to maintain and create features.
  33.  * Dependent on ShopSwitchRoute
  34.  */
  35. class ShopSwitchHelperService
  36. {
  37.     private ShopSwitchRuleService $shopSwitchRuleService;
  38.     private IpService $ipService;
  39.     private Ip2CountryService $ip2CountryService;
  40.     private EntityRepositoryInterface $salesChannelDomainRepository;
  41.     private SalesChannelCountryLanguageService $salesChannelCountryLanguageService;
  42.     public function __construct(
  43.         ShopSwitchRuleService              $shopSwitchRuleService,
  44.         IpService                          $ipService,
  45.         Ip2CountryService                  $ip2CountryService,
  46.         EntityRepositoryInterface          $salesChannelDomainRepository,
  47.         SalesChannelCountryLanguageService $salesChannelCountryLanguageService
  48.     )
  49.     {
  50.         $this->shopSwitchRuleService $shopSwitchRuleService;
  51.         $this->ipService $ipService;
  52.         $this->ip2CountryService $ip2CountryService;
  53.         $this->salesChannelDomainRepository $salesChannelDomainRepository;
  54.         $this->salesChannelCountryLanguageService $salesChannelCountryLanguageService;
  55.     }
  56.     public function getValidResponse(string $activeDomainIdShopSwitchRuleEntity $activeRulestring $activeDomainUrlRequest $requestSalesChannelEntity $activeSalesChannelContext $contextShopSwitchRuleDomainEntity $activeRuleDomain, ?array $languageIds, ?string $countryIso): ShopSwitchRouteResponse
  57.     {
  58.         $activeDomainEntity $this->getDomainById($activeDomainId$context);
  59.         $possibleCountryCollection $this->getPossibleCountries($activeRule$request$activeDomainUrl$context$activeRuleDomain$activeSalesChannel->getId(), $countryIso);
  60.         $possibleLanguageCollection $this->getPossibleLanguages($activeRule$request$activeDomainUrl$context$languageIds);
  61.         if(!empty($possibleCountries) && !empty($possibleLanguages)) {
  62.             $this->removeNotExistingCombinations($possibleCountryCollection$possibleLanguageCollection);
  63.         }
  64.         $possibleCombinationCollection $this->getPossibleCombinations($possibleCountryCollection$possibleLanguageCollection);
  65.         $this->sortPossibleCombinationsByActiveDomain($possibleCombinationCollection$activeDomainUrl);
  66.         if($this->getStayOnPageWithoutAsk($activeRule$possibleCombinationCollection$activeDomainUrl$request$activeSalesChannel) === true) {
  67.             $countryId $this->getCountryId($possibleCombinationCollection$possibleCountryCollection$activeDomainEntity->getUrl(), $activeRuleDomain->getDefaultShippingCountryId(), $context);
  68.             return new ShopSwitchRouteResponse(new ShopSwitchResult($activeDomainId$possibleCountryCollection$possibleLanguageCollection$activeRule$activeDomainUrltruenull$countryId));
  69.         }
  70.         if($activeRule->getSwitchType() === ShopSwitchRuleDefinition::SWITCH_TYPE_DIRECT) {
  71.             $redirectDomainUrl '';
  72.             $firstPossibleCombination $possibleCombinationCollection->first();
  73.             $defaultCountryId $activeRuleDomain->getDefaultShippingCountryId();
  74.             if($firstPossibleCombination instanceof PossibleCombinationStruct) {
  75.                 $redirectDomainUrl $firstPossibleCombination->getDomainUrl();
  76.                 if(empty($firstPossibleCombination->getPossibleStruct()->getRuleDomainEntity()->getDefaultShippingCountryId()) === false) {
  77.                     $defaultCountryId $firstPossibleCombination->getPossibleStruct()->getRuleDomainEntity()->getDefaultShippingCountryId();
  78.                 }
  79.             }
  80.             $stayOnPageWithoutAsk false;
  81.             if(empty($redirectDomainUrl)) {
  82.                 $redirectDomainUrl $activeDomainUrl;
  83.             }
  84.             if($redirectDomainUrl === $activeDomainUrl) {
  85.                 $stayOnPageWithoutAsk true;
  86.             }
  87.             $countryId "";
  88.             // for redirect to external domain we don't want to get the shipping country as a parameter
  89.             if($stayOnPageWithoutAsk !== true && $this->isExternalRedirect($redirectDomainUrl$activeRule) !== true) {
  90.                 $countryId $this->getCountryId($possibleCombinationCollection$possibleCountryCollection$activeDomainEntity->getUrl(), $defaultCountryId$context);
  91.             }
  92.             return new ShopSwitchRouteResponse(new ShopSwitchResult($activeDomainId$possibleCountryCollection$possibleLanguageCollection$activeRule$activeDomainUrl$stayOnPageWithoutAsk$redirectDomainUrl$countryId));
  93.         } elseif($activeRule->getSwitchType() === ShopSwitchRuleDefinition::SWITCH_TYPE_SET_ONLY_COUNTRY) {
  94.             $countryId $this->getCountryId($possibleCombinationCollection$possibleCountryCollection$activeDomainEntity->getUrl(), $activeRuleDomain->getDefaultShippingCountryId(), $context);
  95.             return new ShopSwitchRouteResponse(new ShopSwitchResult($activeDomainId$possibleCountryCollection$possibleLanguageCollection$activeRule$activeDomainUrltrue""$countryId));
  96.         } else {
  97.             $countryId $this->getCountryId($possibleCombinationCollection$possibleCountryCollection$activeDomainEntity->getUrl(), $activeRuleDomain->getDefaultShippingCountryId(), $context);
  98.             return new ShopSwitchRouteResponse(new ShopSwitchResult($activeDomainId$possibleCountryCollection$possibleLanguageCollection$activeRule$activeDomainUrlfalse""$countryId));
  99.         }
  100.     }
  101.     public function getPossibleCountries(ShopSwitchRuleEntity $activeRuleRequest $requeststring $activeDomainUrlContext $contextShopSwitchRuleDomainEntity  $activeRuleDomainstring $salesChannelId, ?string $countryIso): PossibleCountryCollection
  102.     {
  103.         if(($activeRule->getRuleDefinitionType() === ShopSwitchRuleDefinition::RULE_DEFINITION_TYPE_COUNTRY || $activeRule->getRuleDefinitionType() === ShopSwitchRuleDefinition::RULE_DEFINITION_TYPE_COUNTRY_LANGUAGE)
  104.             || $activeRule->getSwitchType() === ShopSwitchRuleDefinition::SWITCH_TYPE_SET_ONLY_COUNTRY) {
  105.             if(empty($countryIso) === true) {
  106.                 $ip $this->ipService->getClientIp($request$salesChannelId);
  107.                 if(empty($ip)) {
  108.                     throw new IpNotFoundException();
  109.                 }
  110.                 $countryIso $this->ip2CountryService->ip2country($ip$salesChannelId);
  111.                 if(!$countryIso && $activeRuleDomain->getDefaultShippingCountry()) {
  112.                     $countryIso $activeRuleDomain->getDefaultShippingCountry()->getIso();
  113.                 }
  114.             }
  115.             if($activeRule->getSwitchType() === ShopSwitchRuleDefinition::SWITCH_TYPE_SET_ONLY_COUNTRY) {
  116.                 $possibleCountryCollection $this->shopSwitchRuleService->getPossibleCountriesOnlySetCountries($activeRule$context);
  117.             } else {
  118.                 $possibleCountryCollection $this->shopSwitchRuleService->getPossibleCountriesOfRule($activeRule$activeDomainUrl$context$request);
  119.             }
  120.             $this->setActiveCountry($possibleCountryCollection$countryIso);
  121.         } else {
  122.             $possibleCountryCollection = new PossibleCountryCollection();
  123.         }
  124.         return $possibleCountryCollection;
  125.     }
  126.     public function getRawPossibleCountries(ShopSwitchRuleEntity $activeRuleRequest $requeststring $activeDomainUrlContext $context): PossibleCountryCollection
  127.     {
  128.         if($activeRule->getSwitchType() === ShopSwitchRuleDefinition::SWITCH_TYPE_SET_ONLY_COUNTRY) {
  129.             $possibleCountryCollection $this->shopSwitchRuleService->getPossibleCountriesOnlySetCountries($activeRule$context);
  130.         } else {
  131.             $possibleCountryCollection $this->shopSwitchRuleService->getPossibleCountriesOfRule($activeRule$activeDomainUrl$context$request);
  132.         }
  133.         return $possibleCountryCollection;
  134.     }
  135.     /**
  136.      * Gets possibleLanguages for ShopSwitchResult.
  137.      * @param ShopSwitchRuleEntity $activeRule
  138.      * @param Request $request
  139.      * @param string $activeDomainUrl
  140.      * @param Context $context
  141.      * @return array
  142.      */
  143.     public function getPossibleLanguages(ShopSwitchRuleEntity $activeRuleRequest $requeststring $activeDomainUrlContext $context, ?array $languageIds = []): PossibleLanguageCollection
  144.     {
  145.         if($activeRule->getRuleDefinitionType() === ShopSwitchRuleDefinition::RULE_DEFINITION_TYPE_LANGUAGE || $activeRule->getRuleDefinitionType() === ShopSwitchRuleDefinition::RULE_DEFINITION_TYPE_COUNTRY_LANGUAGE) {
  146.             $possibleLanguageCollection $this->shopSwitchRuleService->getPossibleLanguagesOfRule($activeRule$activeDomainUrl$context$request);
  147.             if(empty($languageIds) === false) {
  148.                 $this->setActiveLanguage($possibleLanguageCollection$languageIds);
  149.             }
  150.         } else {
  151.             $possibleLanguageCollection = new PossibleLanguageCollection();
  152.         }
  153.         return $possibleLanguageCollection;
  154.     }
  155.     public function getDomainByIdAndSalesChannelContext(string $activeDomainIdSalesChannelContext $salesChannelContext): SalesChannelDomainEntity
  156.     {
  157.         if($salesChannelContext->getSalesChannel()->getDomains() && $salesChannelContext->getSalesChannel()->getDomains()->has($activeDomainId)) {
  158.             return $salesChannelContext->getSalesChannel()->getDomains()->get($activeDomainId);
  159.         }
  160.         return $this->getDomainById($activeDomainId$salesChannelContext->getContext());
  161.     }
  162.     public function getDomainById(string $activeDomainIdContext $context): SalesChannelDomainEntity
  163.     {
  164.         /** @var SalesChannelDomainEntity $domainEntity */
  165.         $domainEntity $this->salesChannelDomainRepository->search(
  166.             (new Criteria([$activeDomainId]))
  167.                 ->addAssociation('salesChannel')
  168.                 ->addAssociation('salesChannel.domains')
  169.             , $context)->first();
  170.         if (empty($domainEntity) === true) {
  171.             throw new ActiveDomainNotFoundException();
  172.         }
  173.         return $domainEntity;
  174.     }
  175.     private function setActiveCountry(PossibleCountryCollection $possibleCountryCollection, ?string $countryIso): void
  176.     {
  177.         if ($countryIso === null) {
  178.             return;
  179.         }
  180.         /**
  181.          * @var string $key
  182.          * @var PossibleCountryStruct $possibleCountryStruct
  183.          */
  184.         foreach ($possibleCountryCollection->getElements() as $key => $possibleCountryStruct) {
  185.             if ($possibleCountryStruct->getIso() === $countryIso) {
  186.                 $possibleCountryStruct->setActive(true);
  187.             } else {
  188.                 $possibleCountryStruct->setActive(false);
  189.             }
  190.         }
  191.     }
  192.     private function setActiveLanguage(PossibleLanguageCollection $possibleLanguageCollection, array $languageIds): void
  193.     {
  194.         /**
  195.          * @var string $key
  196.          * @var PossibleLanguageStruct $possibleLanguageStruct
  197.          */
  198.         foreach ($possibleLanguageCollection->getElements() as $key => $possibleLanguageStruct) {
  199.             $possibleLanguageStruct->setActive(false);
  200.             foreach ($languageIds as $languageId) {
  201.                 if (strtolower($possibleLanguageStruct->getLanguageId()) === $languageId) {
  202.                     $possibleLanguageStruct->setActive(true);
  203.                     break;
  204.                 }
  205.             }
  206.         }
  207.     }
  208.     public function removeNotExistingCombinations(PossibleCountryCollection $possibleCountryCollectionPossibleLanguageCollection $possibleLanguageCollection)
  209.     {
  210.         foreach ($possibleCountryCollection->getElements() as $possibleCountryKey => $possibleCountry) {
  211.             foreach ($possibleLanguageCollection->getElements() as $possibleLanguageKey => $possibleLanguage) {
  212.                 foreach ($possibleCountry->getDomainUrls() as $domainUrlCountry) {
  213.                     foreach ($possibleLanguage->getDomainUrls() as $domainUrlLanguage) {
  214.                         if ($domainUrlCountry === $domainUrlLanguage) {
  215.                             continue 4;
  216.                         }
  217.                     }
  218.                 }
  219.             }
  220.             $possibleCountryCollection->remove($possibleCountryKey);
  221.         }
  222.         foreach ($possibleLanguageCollection->getElements() as $possibleLanguageKey => $possibleLanguage) {
  223.             foreach ($possibleCountryCollection->getElements() as $possibleCountryKey => $possibleCountry) {
  224.                 foreach ($possibleLanguage->getDomainUrls() as $domainUrlLanguage) {
  225.                     foreach ($possibleCountry->getDomainUrls() as $domainUrlCountry) {
  226.                         if ($domainUrlCountry === $domainUrlLanguage) {
  227.                             continue 4;
  228.                         }
  229.                     }
  230.                 }
  231.             }
  232.             $possibleLanguageCollection->remove($possibleLanguageKey);
  233.         }
  234.     }
  235.     public function getPossibleCombinations(PossibleCountryCollection $possibleCountryCollectionPossibleLanguageCollection $possibleLanguageCollection): PossibleCombinationCollection
  236.     {
  237.         $possibleCombinationCollection1 $this->getCombinationOfCollections($possibleCountryCollection$possibleLanguageCollection);
  238.         $possibleCombinationCollection2 $this->getCombinationOfCollections($possibleLanguageCollection$possibleCountryCollection);
  239.         return $this->mergeAndUniqueCombinations($possibleCombinationCollection1$possibleCombinationCollection2);
  240.     }
  241.     private function getCombinationOfCollections(Collection $collection1Collection $collection2): PossibleCombinationCollection
  242.     {
  243.         $possibleCombinationCollection = new PossibleCombinationCollection();
  244.         /** @var AbstractPossibleStruct $item1 */
  245.         foreach ($collection1->getElements() as $item1) {
  246.             if ($item1->isActive() !== true) {
  247.                 continue;
  248.             }
  249.             if ($collection2->count() === 0) {
  250.                 foreach ($item1->getDomainUrls() as $domainUrlItem1) {
  251.                     $possibleCombinationCollection->add(new PossibleCombinationStruct($domainUrlItem1$item1));
  252.                 }
  253.             } else {
  254.                 /** @var AbstractPossibleStruct $item2 */
  255.                 foreach ($collection2->getElements() as $item2) {
  256.                     if ($item2->isActive() !== true) {
  257.                         continue;
  258.                     }
  259.                     foreach ($item2->getDomainUrls() as $domainUrlItem2) {
  260.                         foreach ($item1->getDomainUrls() as $domainUrlItem1) {
  261.                             if ($domainUrlItem1 === $domainUrlItem2) {
  262.                                 $possibleCombinationCollection->add(new PossibleCombinationStruct($domainUrlItem1$item1));
  263.                             }
  264.                         }
  265.                     }
  266.                 }
  267.             }
  268.         }
  269.         return $possibleCombinationCollection;
  270.     }
  271.     private function mergeAndUniqueCombinations(PossibleCombinationCollection $possibleCombinationCollection1PossibleCombinationCollection $possibleCombinationCollection2): PossibleCombinationCollection
  272.     {
  273.         $possibleCombinationCollection $possibleCombinationCollection1;
  274.         foreach ($possibleCombinationCollection2->getElements() as $item2) {
  275.             foreach ($possibleCombinationCollection as $possibleCombinationStruct) {
  276.                 if($possibleCombinationStruct->getDomainUrl() === $item2->getDomainUrl()) {
  277.                     continue 2;
  278.                 }
  279.             }
  280.             $possibleCombinationCollection->add($item2);
  281.         }
  282.         return $possibleCombinationCollection;
  283.     }
  284.     private function getStayOnPageWithoutAsk(ShopSwitchRuleEntity $activeRulePossibleCombinationCollection $possibleCombinationCollectionstring $activeDomainUrlRequest $requestSalesChannelEntity $salesChannelEntity): bool
  285.     {
  286.         return $this->comesFromOwnDomain($request$activeRule) ||
  287.             (($activeRule->isAskIfMatch() === false || $activeRule->getSwitchType() === ShopSwitchRuleDefinition::SWITCH_TYPE_DIRECT)
  288.                 && $possibleCombinationCollection->hasUrl($activeDomainUrl) === true);
  289.     }
  290.     private function comesFromOwnDomain(Request $requestShopSwitchRuleEntity $activeRule): bool
  291.     {
  292.         if ($request->attributes->has(ShopSwitchController::SHOP_SWITCH_AJAX)) {
  293.             return false;
  294.         }
  295.         if (empty($referer $request->server->get('HTTP_REFERER')) === false) {
  296.             $referer rtrim($referer'/');
  297.             $currentDomainId $request->attributes->get(SalesChannelRequest::ATTRIBUTE_DOMAIN_ID);
  298.             foreach ($activeRule->getDomains() as $domainEntity) {
  299.                 if ($domainEntity->getDomainId() === $currentDomainId || empty($domainEntity->getDomain()) || empty($domainEntity->getDomain()->getUrl())) {
  300.                     continue;
  301.                 }
  302.                 if (strpos($referer$domainEntity->getDomain()->getUrl()) === 0
  303.                     || strpos($domainEntity->getDomain()->getUrl(), $referer) === 0) {
  304.                     return true;
  305.                 }
  306.             }
  307.         }
  308.         return false;
  309.     }
  310.     public function getCountryId(PossibleCombinationCollection $possibleCombinationCollectionPossibleCountryCollection $possibleCountryCollectionstring $redirectDomainUrl, ?string $defaultCountryIdContext $context): string
  311.     {
  312.         $countryId $this->getCountryIdFromPossibleCombinations($possibleCombinationCollection$possibleCountryCollection);
  313.         // try to get first active county from
  314.         if (!$countryId) {
  315.             foreach ($possibleCountryCollection->getElements() as $possibleCountryStruct) {
  316.                 if ($possibleCountryStruct->isActive() === true) {
  317.                     if ($this->salesChannelCountryLanguageService->isCountryInSalesChannelDomain($possibleCountryStruct->getCountryId(), $redirectDomainUrl$context) === true) {
  318.                         return $possibleCountryStruct->getCountryId();
  319.                     }
  320.                 }
  321.             }
  322.             $countryId $defaultCountryId;
  323.         }
  324.         if (!$countryId) {
  325.             return "";
  326.         }
  327.         if ($this->salesChannelCountryLanguageService->isCountryInSalesChannelDomain($countryId$redirectDomainUrl$context) === true) {
  328.             return $countryId;
  329.         }
  330.         return "";
  331.     }
  332.     private function getCountryIdFromPossibleCombinations(PossibleCombinationCollection $possibleCombinationCollectionPossibleCountryCollection $possibleCountryCollection)
  333.     {
  334.         // if more countries match on more domains -> prefer the active one
  335.         foreach ($possibleCountryCollection->getElements() as $possibleCountryStruct) {
  336.             if ($possibleCountryStruct->isActive() === true) {
  337.                 foreach ($possibleCombinationCollection->getElements() as $possibleCombinationStruct) {
  338.                     if ($possibleCombinationStruct->getDomainUrl() === $possibleCountryStruct->getDomainUrl()) {
  339.                         return $possibleCountryStruct->getCountryId();
  340.                     }
  341.                 }
  342.             }
  343.         }
  344.         foreach ($possibleCountryCollection->getElements() as $possibleCountryStruct) {
  345.             foreach ($possibleCombinationCollection->getElements() as $possibleCombinationStruct) {
  346.                 if ($possibleCombinationStruct->getDomainUrl() === $possibleCountryStruct->getDomainUrl()) {
  347.                     return $possibleCountryStruct->getCountryId();
  348.                 }
  349.             }
  350.         }
  351.         return "";
  352.     }
  353.     public function sortPossibleCombinationsByActiveDomain(PossibleCombinationCollection $possibleCombinationCollectionstring $activeDomainUrl): void
  354.     {
  355.         $possibleCombinationCollection->sort(function (PossibleCombinationStruct $possibleCombinationStructAPossibleCombinationStruct $possibleCombinationStructB) use ($activeDomainUrl) {
  356.             if ($possibleCombinationStructA->getDomainUrl() === $activeDomainUrl) {
  357.                 return -1;
  358.             } elseif ($possibleCombinationStructB->getDomainUrl() === $activeDomainUrl) {
  359.                 return 1;
  360.             } else {
  361.                 return 0;
  362.             }
  363.         });
  364.     }
  365.     private function isExternalRedirect(?string $redirectDomainUrlShopSwitchRuleEntity $activeRule): bool
  366.     {
  367.         if (empty($redirectDomainUrl)) {
  368.             return false;
  369.         }
  370.         foreach ($activeRule->getDomains() as $domain) {
  371.             if ($domain->isActive() === true && $domain->getRedirectTo() === 'external' && $redirectDomainUrl === $domain->getExternalUrl()) {
  372.                 return true;
  373.             }
  374.         }
  375.         return false;
  376.     }
  377. }