vendor/shopware/storefront/Controller/AddressController.php line 93

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Controller;
  3. use Shopware\Core\Checkout\Cart\Exception\CustomerNotLoggedInException;
  4. use Shopware\Core\Checkout\Cart\Order\Transformer\CustomerTransformer;
  5. use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity;
  6. use Shopware\Core\Checkout\Customer\CustomerEntity;
  7. use Shopware\Core\Checkout\Customer\Exception\AddressNotFoundException;
  8. use Shopware\Core\Checkout\Customer\Exception\CannotDeleteDefaultAddressException;
  9. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractChangeCustomerProfileRoute;
  10. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractDeleteAddressRoute;
  11. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractListAddressRoute;
  12. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractUpsertAddressRoute;
  13. use Shopware\Core\Checkout\Customer\SalesChannel\AccountService;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  16. use Shopware\Core\Framework\Feature;
  17. use Shopware\Core\Framework\Log\Package;
  18. use Shopware\Core\Framework\Routing\Annotation\LoginRequired;
  19. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  20. use Shopware\Core\Framework\Routing\Annotation\Since;
  21. use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
  22. use Shopware\Core\Framework\Uuid\Exception\InvalidUuidException;
  23. use Shopware\Core\Framework\Uuid\Uuid;
  24. use Shopware\Core\Framework\Validation\DataBag\DataBag;
  25. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  26. use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
  27. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  28. use Shopware\Storefront\Framework\Routing\Annotation\NoStore;
  29. use Shopware\Storefront\Page\Address\AddressEditorModalStruct;
  30. use Shopware\Storefront\Page\Address\Detail\AddressDetailPageLoadedHook;
  31. use Shopware\Storefront\Page\Address\Detail\AddressDetailPageLoader;
  32. use Shopware\Storefront\Page\Address\Listing\AddressBookWidgetLoadedHook;
  33. use Shopware\Storefront\Page\Address\Listing\AddressListingPageLoadedHook;
  34. use Shopware\Storefront\Page\Address\Listing\AddressListingPageLoader;
  35. use Symfony\Component\HttpFoundation\RedirectResponse;
  36. use Symfony\Component\HttpFoundation\Request;
  37. use Symfony\Component\HttpFoundation\Response;
  38. use Symfony\Component\Routing\Annotation\Route;
  39. /**
  40.  * @Route(defaults={"_routeScope"={"storefront"}})
  41.  *
  42.  * @deprecated tag:v6.5.0 - reason:becomes-internal - Will be internal
  43.  */
  44. #[Package('storefront')]
  45. class AddressController extends StorefrontController
  46. {
  47.     private const ADDRESS_TYPE_BILLING 'billing';
  48.     private const ADDRESS_TYPE_SHIPPING 'shipping';
  49.     private AccountService $accountService;
  50.     private AddressListingPageLoader $addressListingPageLoader;
  51.     private AddressDetailPageLoader $addressDetailPageLoader;
  52.     private AbstractListAddressRoute $listAddressRoute;
  53.     private AbstractUpsertAddressRoute $updateAddressRoute;
  54.     private AbstractDeleteAddressRoute $deleteAddressRoute;
  55.     private AbstractChangeCustomerProfileRoute $updateCustomerProfileRoute;
  56.     /**
  57.      * @internal
  58.      */
  59.     public function __construct(
  60.         AddressListingPageLoader $addressListingPageLoader,
  61.         AddressDetailPageLoader $addressDetailPageLoader,
  62.         AccountService $accountService,
  63.         AbstractListAddressRoute $listAddressRoute,
  64.         AbstractUpsertAddressRoute $updateAddressRoute,
  65.         AbstractDeleteAddressRoute $deleteAddressRoute,
  66.         AbstractChangeCustomerProfileRoute $updateCustomerProfileRoute
  67.     ) {
  68.         $this->accountService $accountService;
  69.         $this->addressListingPageLoader $addressListingPageLoader;
  70.         $this->addressDetailPageLoader $addressDetailPageLoader;
  71.         $this->listAddressRoute $listAddressRoute;
  72.         $this->updateAddressRoute $updateAddressRoute;
  73.         $this->deleteAddressRoute $deleteAddressRoute;
  74.         $this->updateCustomerProfileRoute $updateCustomerProfileRoute;
  75.     }
  76.     /**
  77.      * @Since("6.0.0.0")
  78.      * @Route("/account/address", name="frontend.account.address.page", options={"seo"="false"}, methods={"GET"}, defaults={"_loginRequired"=true})
  79.      * @NoStore
  80.      */
  81.     public function accountAddressOverview(Request $requestSalesChannelContext $contextCustomerEntity $customer): Response
  82.     {
  83.         $page $this->addressListingPageLoader->load($request$context$customer);
  84.         $this->hook(new AddressListingPageLoadedHook($page$context));
  85.         return $this->renderStorefront('@Storefront/storefront/page/account/addressbook/index.html.twig', ['page' => $page]);
  86.     }
  87.     /**
  88.      * @Since("6.0.0.0")
  89.      * @Route("/account/address/create", name="frontend.account.address.create.page", options={"seo"="false"}, methods={"GET"}, defaults={"_loginRequired"=true})
  90.      * @NoStore
  91.      */
  92.     public function accountCreateAddress(Request $requestRequestDataBag $dataSalesChannelContext $contextCustomerEntity $customer): Response
  93.     {
  94.         $page $this->addressDetailPageLoader->load($request$context$customer);
  95.         $this->hook(new AddressDetailPageLoadedHook($page$context));
  96.         return $this->renderStorefront('@Storefront/storefront/page/account/addressbook/create.html.twig', [
  97.             'page' => $page,
  98.             'data' => $data,
  99.         ]);
  100.     }
  101.     /**
  102.      * @Since("6.0.0.0")
  103.      * @Route("/account/address/{addressId}", name="frontend.account.address.edit.page", options={"seo"="false"}, methods={"GET"}, defaults={"_loginRequired"=true})
  104.      * @NoStore
  105.      */
  106.     public function accountEditAddress(Request $requestSalesChannelContext $contextCustomerEntity $customer): Response
  107.     {
  108.         $page $this->addressDetailPageLoader->load($request$context$customer);
  109.         $this->hook(new AddressDetailPageLoadedHook($page$context));
  110.         return $this->renderStorefront('@Storefront/storefront/page/account/addressbook/edit.html.twig', ['page' => $page]);
  111.     }
  112.     /**
  113.      * @Since("6.0.0.0")
  114.      * @Route("/account/address/default-{type}/{addressId}", name="frontend.account.address.set-default-address", methods={"POST"}, defaults={"_loginRequired"=true})
  115.      */
  116.     public function switchDefaultAddress(string $typestring $addressIdSalesChannelContext $contextCustomerEntity $customer): RedirectResponse
  117.     {
  118.         if (!Uuid::isValid($addressId)) {
  119.             throw new InvalidUuidException($addressId);
  120.         }
  121.         $success true;
  122.         try {
  123.             if ($type === self::ADDRESS_TYPE_SHIPPING) {
  124.                 $this->accountService->setDefaultShippingAddress($addressId$context$customer);
  125.             } elseif ($type === self::ADDRESS_TYPE_BILLING) {
  126.                 $this->accountService->setDefaultBillingAddress($addressId$context$customer);
  127.             } else {
  128.                 $success false;
  129.             }
  130.         } catch (AddressNotFoundException $exception) {
  131.             $success false;
  132.         }
  133.         return new RedirectResponse(
  134.             $this->generateUrl('frontend.account.address.page', ['changedDefaultAddress' => $success])
  135.         );
  136.     }
  137.     /**
  138.      * @Since("6.0.0.0")
  139.      * @Route("/account/address/delete/{addressId}", name="frontend.account.address.delete", options={"seo"="false"}, methods={"POST"}, defaults={"_loginRequired"=true})
  140.      */
  141.     public function deleteAddress(string $addressIdSalesChannelContext $contextCustomerEntity $customer): Response
  142.     {
  143.         $success true;
  144.         if (!$addressId) {
  145.             throw new MissingRequestParameterException('addressId');
  146.         }
  147.         try {
  148.             $this->deleteAddressRoute->delete($addressId$context$customer);
  149.         } catch (InvalidUuidException AddressNotFoundException CannotDeleteDefaultAddressException $exception) {
  150.             $success false;
  151.         }
  152.         return new RedirectResponse($this->generateUrl('frontend.account.address.page', ['addressDeleted' => $success]));
  153.     }
  154.     /**
  155.      * @Since("6.0.0.0")
  156.      * @Route("/account/address/create", name="frontend.account.address.create", options={"seo"="false"}, methods={"POST"}, defaults={"_loginRequired"=true})
  157.      * @Route("/account/address/{addressId}", name="frontend.account.address.edit.save", options={"seo"="false"}, methods={"POST"}, defaults={"_loginRequired"=true})
  158.      */
  159.     public function saveAddress(RequestDataBag $dataSalesChannelContext $contextCustomerEntity $customer): Response
  160.     {
  161.         /** @var RequestDataBag $address */
  162.         $address $data->get('address');
  163.         try {
  164.             $this->updateAddressRoute->upsert(
  165.                 $address->get('id'),
  166.                 $address->toRequestDataBag(),
  167.                 $context,
  168.                 $customer
  169.             );
  170.             return new RedirectResponse($this->generateUrl('frontend.account.address.page', ['addressSaved' => true]));
  171.         } catch (ConstraintViolationException $formViolations) {
  172.         }
  173.         if (!$address->get('id')) {
  174.             return $this->forwardToRoute('frontend.account.address.create.page', ['formViolations' => $formViolations]);
  175.         }
  176.         return $this->forwardToRoute(
  177.             'frontend.account.address.edit.page',
  178.             ['formViolations' => $formViolations],
  179.             ['addressId' => $address->get('id')]
  180.         );
  181.     }
  182.     /**
  183.      * @Since("6.0.0.0")
  184.      * @Route("/widgets/account/address-book", name="frontend.account.addressbook", options={"seo"=true}, methods={"POST"}, defaults={"XmlHttpRequest"=true, "_loginRequired"=true, "_loginRequiredAllowGuest"=true})
  185.      */
  186.     public function addressBook(Request $requestRequestDataBag $dataBagSalesChannelContext $contextCustomerEntity $customer): Response
  187.     {
  188.         $viewData = new AddressEditorModalStruct();
  189.         $params = [];
  190.         try {
  191.             $this->handleChangeableAddresses($viewData$dataBag$context$customer);
  192.             $this->handleAddressCreation($viewData$dataBag$context$customer);
  193.             $this->handleAddressSelection($viewData$dataBag$context$customer);
  194.             $page $this->addressListingPageLoader->load($request$context$customer);
  195.             $this->hook(new AddressBookWidgetLoadedHook($page$context));
  196.             $viewData->setPage($page);
  197.             if (Feature::isActive('FEATURE_NEXT_15957')) {
  198.                 $this->handleCustomerVatIds($dataBag$context$customer);
  199.             }
  200.         } catch (ConstraintViolationException $formViolations) {
  201.             $params['formViolations'] = $formViolations;
  202.             $params['postedData'] = $dataBag->get('address');
  203.         } catch (\Exception $exception) {
  204.             $viewData->setSuccess(false);
  205.             $viewData->setMessages([
  206.                 'type' => self::DANGER,
  207.                 'text' => $this->trans('error.message-default'),
  208.             ]);
  209.         }
  210.         if ($request->get('redirectTo') || $request->get('forwardTo')) {
  211.             return $this->createActionResponse($request);
  212.         }
  213.         $params array_merge($params$viewData->getVars());
  214.         $response $this->renderStorefront(
  215.             '@Storefront/storefront/component/address/address-editor-modal.html.twig',
  216.             $params
  217.         );
  218.         $response->headers->set('x-robots-tag''noindex');
  219.         return $response;
  220.     }
  221.     private function handleAddressCreation(
  222.         AddressEditorModalStruct $viewData,
  223.         RequestDataBag $dataBag,
  224.         SalesChannelContext $context,
  225.         CustomerEntity $customer
  226.     ): void {
  227.         /** @var DataBag|null $addressData */
  228.         $addressData $dataBag->get('address');
  229.         if ($addressData === null) {
  230.             return;
  231.         }
  232.         $response $this->updateAddressRoute->upsert(
  233.             $addressData->get('id'),
  234.             $addressData->toRequestDataBag(),
  235.             $context,
  236.             $customer
  237.         );
  238.         $addressId $response->getAddress()->getId();
  239.         $addressType null;
  240.         if ($viewData->isChangeBilling()) {
  241.             $addressType self::ADDRESS_TYPE_BILLING;
  242.         } elseif ($viewData->isChangeShipping()) {
  243.             $addressType self::ADDRESS_TYPE_SHIPPING;
  244.         }
  245.         // prepare data to set newly created address as customers default
  246.         if ($addressType) {
  247.             $dataBag->set('selectAddress', new RequestDataBag([
  248.                 'id' => $addressId,
  249.                 'type' => $addressType,
  250.             ]));
  251.         }
  252.         $viewData->setAddressId($addressId);
  253.         $viewData->setSuccess(true);
  254.         $viewData->setMessages(['type' => 'success''text' => $this->trans('account.addressSaved')]);
  255.     }
  256.     private function handleChangeableAddresses(
  257.         AddressEditorModalStruct $viewData,
  258.         RequestDataBag $dataBag,
  259.         SalesChannelContext $context,
  260.         CustomerEntity $customer
  261.     ): void {
  262.         $changeableAddresses $dataBag->get('changeableAddresses');
  263.         if ($changeableAddresses === null) {
  264.             return;
  265.         }
  266.         $viewData->setChangeShipping((bool) $changeableAddresses->get('changeShipping'));
  267.         $viewData->setChangeBilling((bool) $changeableAddresses->get('changeBilling'));
  268.         $addressId $dataBag->get('id');
  269.         if (!$addressId) {
  270.             return;
  271.         }
  272.         $viewData->setAddress($this->getById($addressId$context$customer));
  273.     }
  274.     /**
  275.      * @throws CustomerNotLoggedInException
  276.      * @throws InvalidUuidException
  277.      */
  278.     private function handleAddressSelection(
  279.         AddressEditorModalStruct $viewData,
  280.         RequestDataBag $dataBag,
  281.         SalesChannelContext $context,
  282.         CustomerEntity $customer
  283.     ): void {
  284.         $selectedAddress $dataBag->get('selectAddress');
  285.         if ($selectedAddress === null) {
  286.             return;
  287.         }
  288.         $addressType $selectedAddress->get('type');
  289.         $addressId $selectedAddress->get('id');
  290.         if (!Uuid::isValid($addressId)) {
  291.             throw new InvalidUuidException($addressId);
  292.         }
  293.         $success true;
  294.         try {
  295.             if ($addressType === self::ADDRESS_TYPE_SHIPPING) {
  296.                 $address $this->getById($addressId$context$customer);
  297.                 $customer->setDefaultShippingAddress($address);
  298.                 $this->accountService->setDefaultShippingAddress($addressId$context$customer);
  299.             } elseif ($addressType === self::ADDRESS_TYPE_BILLING) {
  300.                 $address $this->getById($addressId$context$customer);
  301.                 $customer->setDefaultBillingAddress($address);
  302.                 $this->accountService->setDefaultBillingAddress($addressId$context$customer);
  303.             } else {
  304.                 $success false;
  305.             }
  306.         } catch (AddressNotFoundException $exception) {
  307.             $success false;
  308.         }
  309.         if ($success) {
  310.             $this->addFlash(self::SUCCESS$this->trans('account.addressDefaultChanged'));
  311.         } else {
  312.             $this->addFlash(self::DANGER$this->trans('account.addressDefaultNotChanged'));
  313.         }
  314.         $viewData->setSuccess($success);
  315.     }
  316.     private function getById(string $addressIdSalesChannelContext $contextCustomerEntity $customer): CustomerAddressEntity
  317.     {
  318.         if (!Uuid::isValid($addressId)) {
  319.             throw new InvalidUuidException($addressId);
  320.         }
  321.         $criteria = new Criteria();
  322.         $criteria->addFilter(new EqualsFilter('id'$addressId));
  323.         $criteria->addFilter(new EqualsFilter('customerId'$customer->getId()));
  324.         $address $this->listAddressRoute->load($criteria$context$customer)->getAddressCollection()->get($addressId);
  325.         if (!$address) {
  326.             throw new AddressNotFoundException($addressId);
  327.         }
  328.         return $address;
  329.     }
  330.     private function handleCustomerVatIds(RequestDataBag $dataBagSalesChannelContext $contextCustomerEntity $customer): void
  331.     {
  332.         if (!$dataBag->has('vatIds')) {
  333.             return;
  334.         }
  335.         $newVatIds $dataBag->get('vatIds')->all();
  336.         $oldVatIds $customer->getVatIds() ?? [];
  337.         if (!array_diff($newVatIds$oldVatIds) && !array_diff($oldVatIds$newVatIds)) {
  338.             return;
  339.         }
  340.         $dataCustomer CustomerTransformer::transform($customer);
  341.         $dataCustomer['vatIds'] = $newVatIds;
  342.         $dataCustomer['accountType'] = $customer->getCompany() === null CustomerEntity::ACCOUNT_TYPE_PRIVATE CustomerEntity::ACCOUNT_TYPE_BUSINESS;
  343.         $newDataBag = new RequestDataBag($dataCustomer);
  344.         $this->updateCustomerProfileRoute->change($newDataBag$context$customer);
  345.     }
  346. }