vendor/isotope/isotope-core/system/modules/isotope/library/Isotope/CheckoutStep/Address.php line 46

Open in your IDE?
  1. <?php
  2. /*
  3.  * Isotope eCommerce for Contao Open Source CMS
  4.  *
  5.  * Copyright (C) 2009 - 2019 terminal42 gmbh & Isotope eCommerce Workgroup
  6.  *
  7.  * @link       https://isotopeecommerce.org
  8.  * @license    https://opensource.org/licenses/lgpl-3.0.html
  9.  */
  10. namespace Isotope\CheckoutStep;
  11. use Contao\Controller;
  12. use Contao\Date;
  13. use Contao\FrontendUser;
  14. use Contao\Input;
  15. use Contao\System;
  16. use Contao\Widget;
  17. use Haste\Generator\RowClass;
  18. use Isotope\Model\Address as AddressModel;
  19. use Isotope\Module\Checkout;
  20. use Isotope\Template;
  21. use Model\Registry;
  22. abstract class Address extends CheckoutStep
  23. {
  24.     /**
  25.      * Cache of address widgets
  26.      * @var array
  27.      */
  28.     private $arrWidgets;
  29.     /**
  30.      * Frontend template instance
  31.      * @var Template|\stdClass
  32.      */
  33.     protected $Template;
  34.     /**
  35.      * Load data container and create template
  36.      *
  37.      * @param Checkout $objModule
  38.      */
  39.     public function __construct(Checkout $objModule)
  40.     {
  41.         parent::__construct($objModule);
  42.         System::loadLanguageFile(AddressModel::getTable());
  43.         Controller::loadDataContainer(AddressModel::getTable());
  44.         $this->Template = new Template('iso_checkout_address');
  45.     }
  46.     /**
  47.      * Generate the checkout step
  48.      *
  49.      * @return string
  50.      */
  51.     public function generate()
  52.     {
  53.         $blnValidate Input::post('FORM_SUBMIT') === $this->objModule->getFormId();
  54.         $this->Template->class     $this->getStepClass();
  55.         $this->Template->tableless = isset($this->objModule->tableless) ? $this->objModule->tableless true;
  56.         $this->Template->options   $this->generateOptions($blnValidate);
  57.         $this->Template->fields    $this->generateFields($blnValidate);
  58.         return $this->Template->parse();
  59.     }
  60.     /**
  61.      * Generate address options and return it as HTML string
  62.      *
  63.      * @param bool $blnValidate
  64.      *
  65.      * @return string
  66.      */
  67.     protected function generateOptions($blnValidate false)
  68.     {
  69.         $strBuffer  '';
  70.         $varValue   '0';
  71.         $arrOptions $this->getAddressOptions();
  72.         if (!== \count($arrOptions)) {
  73.             foreach ($arrOptions as $option) {
  74.                 if ($option['default']) {
  75.                     $varValue $option['value'];
  76.                 }
  77.             }
  78.             $strClass  $GLOBALS['TL_FFL']['radio'];
  79.             /** @var Widget $objWidget */
  80.             $objWidget = new $strClass(
  81.                 [
  82.                     'id'          => $this->getStepClass(),
  83.                     'name'        => $this->getStepClass(),
  84.                     'mandatory'   => true,
  85.                     'options'     => $arrOptions,
  86.                     'value'       => $varValue,
  87.                     'onclick'     => "Isotope.toggleAddressFields(this, '" $this->getStepClass() . "_new');",
  88.                     'storeValues' => true,
  89.                     'tableless'   => true,
  90.                 ]
  91.             );
  92.             // Validate input
  93.             if ($blnValidate) {
  94.                 $objWidget->validate();
  95.                 if ($objWidget->hasErrors()) {
  96.                     $this->blnError true;
  97.                 } else {
  98.                     $varValue = (string) $objWidget->value;
  99.                 }
  100.             } elseif ($objWidget->value != '') {
  101.                 Input::setPost($objWidget->name$objWidget->value);
  102.                 $objValidator = clone $objWidget;
  103.                 $objValidator->validate();
  104.                 if ($objValidator->hasErrors()) {
  105.                     $this->blnError true;
  106.                 }
  107.             }
  108.             $strBuffer .= $objWidget->parse();
  109.         }
  110.         if ($varValue !== '0') {
  111.             $this->Template->style 'display:none;';
  112.         }
  113.         $objAddress $this->getAddressForOption($varValue$blnValidate);
  114.         if (null === $objAddress || !Registry::getInstance()->isRegistered($objAddress)) {
  115.             $this->blnError true;
  116.         }  elseif ($blnValidate) {
  117.             $this->setAddress($objAddress);
  118.         }
  119.         return $strBuffer;
  120.     }
  121.     /**
  122.      * Generate the current step widgets.
  123.      *
  124.      * @param bool $blnValidate
  125.      *
  126.      * @return string|array
  127.      */
  128.     protected function generateFields($blnValidate false)
  129.     {
  130.         $strBuffer  '';
  131.         $arrWidgets $this->getWidgets();
  132.         RowClass::withKey('rowClass')->addCount('row_')->addFirstLast('row_')->addEvenOdd('row_')->applyTo($arrWidgets);
  133.         foreach ($arrWidgets as $objWidget) {
  134.             $strBuffer .= $objWidget->parse();
  135.         }
  136.         return $strBuffer;
  137.     }
  138.     /**
  139.      * Validate input and return address data
  140.      *
  141.      * @param bool $blnValidate
  142.      *
  143.      * @return array
  144.      */
  145.     protected function validateFields($blnValidate)
  146.     {
  147.         $arrAddress = array();
  148.         $arrWidgets $this->getWidgets();
  149.         foreach ($arrWidgets as $strName => $objWidget) {
  150.             // Validate input
  151.             if ($blnValidate) {
  152.                 $objWidget->validate();
  153.                 $varValue = (string) $objWidget->value;
  154.                 // Convert date formats into timestamps
  155.                 if ('' !== $varValue && \in_array(($objWidget->dca_config['eval']['rgxp'] ?? null), array('date''time''datim'), true)) {
  156.                     try {
  157.                         $objDate = new Date($varValue$GLOBALS['TL_CONFIG'][$objWidget->dca_config['eval']['rgxp'] . 'Format']);
  158.                         $varValue $objDate->tstamp;
  159.                     } catch (\OutOfBoundsException $e) {
  160.                         $objWidget->addError(
  161.                             sprintf(
  162.                                 $GLOBALS['TL_LANG']['ERR'][$objWidget->dca_config['eval']['rgxp']],
  163.                                 $GLOBALS['TL_CONFIG'][$objWidget->dca_config['eval']['rgxp'] . 'Format']
  164.                             )
  165.                         );
  166.                     }
  167.                 }
  168.                 // Do not submit if there are errors
  169.                 if ($objWidget->hasErrors()) {
  170.                     $this->blnError true;
  171.                 } // Store current value
  172.                 elseif ($objWidget->submitInput()) {
  173.                     $arrAddress[$strName] = $varValue;
  174.                 }
  175.             } else {
  176.                 Input::setPost($objWidget->name$objWidget->value);
  177.                 $objValidator = clone $objWidget;
  178.                 $objValidator->validate();
  179.                 if ($objValidator->hasErrors()) {
  180.                     $this->blnError true;
  181.                 }
  182.             }
  183.         }
  184.         return $arrAddress;
  185.     }
  186.     /**
  187.      * Get widget objects for address fields
  188.      * @return  Widget[]
  189.      */
  190.     protected function getWidgets()
  191.     {
  192.         if (null === $this->arrWidgets) {
  193.             $this->arrWidgets = array();
  194.             $objAddress       $this->getDefaultAddress();
  195.             $arrFields        $this->mergeFieldsWithDca($this->getAddressFields());
  196.             // !HOOK: modify address fields in checkout process
  197.             if (isset($GLOBALS['ISO_HOOKS']['modifyAddressFields'])
  198.                 && \is_array($GLOBALS['ISO_HOOKS']['modifyAddressFields'])
  199.             ) {
  200.                 foreach ($GLOBALS['ISO_HOOKS']['modifyAddressFields'] as $callback) {
  201.                     $arrFields System::importStatic($callback[0])->{$callback[1]}($arrFields$objAddress$this->getStepClass());
  202.                 }
  203.             }
  204.             foreach ($arrFields as $field) {
  205.                 if (!\is_array($field['dca'])
  206.                     || !($field['enabled'] ?? null)
  207.                     || !($field['dca']['eval']['feEditable'] ?? null)
  208.                     || (($field['dca']['eval']['membersOnly'] ?? null) && FE_USER_LOGGED_IN !== true)
  209.                 ) {
  210.                     continue;
  211.                 }
  212.                 // Continue if the class is not defined
  213.                 if (!\array_key_exists($field['dca']['inputType'], $GLOBALS['TL_FFL'])
  214.                     || !class_exists($GLOBALS['TL_FFL'][$field['dca']['inputType']])
  215.                 ) {
  216.                     continue;
  217.                 }
  218.                 /** @var Widget $strClass */
  219.                 $strClass $GLOBALS['TL_FFL'][$field['dca']['inputType']];
  220.                 if ('country' === $field['value']) {
  221.                     // Special field "country"
  222.                     $arrCountries $this->getAddressCountries();
  223.                     $field['dca']['reference'] = $field['dca']['options'];
  224.                     $field['dca']['options'] = array_values(array_intersect(array_keys($field['dca']['options']), $arrCountries));
  225.                 } elseif (!empty($field['dca']['eval']['conditionField'])) {
  226.                     // Special field type "conditionalselect"
  227.                     $field['dca']['eval']['conditionField'] = $this->getStepClass() . '_' $field['dca']['eval']['conditionField'];
  228.                 }
  229.                 $objWidget = new $strClass(
  230.                     $strClass::getAttributesFromDca(
  231.                         $field['dca'],
  232.                         $this->getStepClass() . '_' $field['value'],
  233.                         $objAddress->{$field['value']}
  234.                     )
  235.                 );
  236.                 $objWidget->mandatory   $field['mandatory'] ? true false;
  237.                 $objWidget->required    $objWidget->mandatory;
  238.                 $objWidget->tableless   = isset($this->objModule->tableless) ? $this->objModule->tableless true;
  239.                 $objWidget->storeValues true;
  240.                 $objWidget->dca_config  $field['dca'];
  241.                 $this->arrWidgets[$field['value']] = $objWidget;
  242.             }
  243.         }
  244.         return $this->arrWidgets;
  245.     }
  246.     /**
  247.      * Get options for all addresses in the user's address book
  248.      *
  249.      * @param array $arrFields
  250.      *
  251.      * @return array
  252.      */
  253.     protected function getAddressOptions($arrFields null)
  254.     {
  255.         $arrOptions = array();
  256.         if (FE_USER_LOGGED_IN === true) {
  257.             /** @var AddressModel[] $arrAddresses */
  258.             $arrAddresses $this->getAddresses();
  259.             $arrCountries $this->getAddressCountries();
  260.             if (!== \count($arrAddresses) && !== \count($arrCountries)) {
  261.                 $objDefault $this->getAddress();
  262.                 foreach ($arrAddresses as $objAddress) {
  263.                     if (!\in_array($objAddress->country$arrCountriestrue)) {
  264.                         continue;
  265.                     }
  266.                     $arrOptions[] = [
  267.                         'value'   => $objAddress->id,
  268.                         'label'   => $objAddress->generate($arrFields),
  269.                         'default' => $objAddress->id == $objDefault->id '1' '',
  270.                     ];
  271.                 }
  272.             }
  273.         }
  274.         return $arrOptions;
  275.     }
  276.     /**
  277.      * Get address object for a selected option
  278.      *
  279.      * @param mixed $varValue
  280.      * @param bool  $blnValidate
  281.      *
  282.      * @return AddressModel
  283.      */
  284.     protected function getAddressForOption($varValue$blnValidate)
  285.     {
  286.         $arrAddresses $this->getAddresses();
  287.         foreach ($arrAddresses as $objAddress) {
  288.             if ($objAddress->id == $varValue) {
  289.                 return $objAddress;
  290.             }
  291.         }
  292.         return null;
  293.     }
  294.     /**
  295.      * Get addresses for the current member
  296.      *
  297.      * @return AddressModel[]
  298.      */
  299.     protected function getAddresses()
  300.     {
  301.         $objAddresses AddressModel::findForMember(
  302.             FrontendUser::getInstance()->id,
  303.             array(
  304.                 'order' => 'isDefaultBilling DESC, isDefaultShipping DESC'
  305.             )
  306.         );
  307.         return null === $objAddresses ? array() : $objAddresses->getModels();
  308.     }
  309.     /**
  310.      * Get default address for this collection and address type
  311.      *
  312.      * @return AddressModel
  313.      */
  314.     abstract protected function getDefaultAddress();
  315.     /**
  316.      * Get field configuration for this address type
  317.      *
  318.      * @return array
  319.      */
  320.     abstract protected function getAddressFields();
  321.     /**
  322.      * Get allowed countries for this address type
  323.      *
  324.      * @return array
  325.      */
  326.     abstract protected function getAddressCountries();
  327.     /**
  328.      * Get the current address (from Cart) for this address type
  329.      *
  330.      * @return AddressModel
  331.      */
  332.     abstract protected function getAddress();
  333.     /**
  334.      * Set new address in cart
  335.      *
  336.      * @param AddressModel $objAddress
  337.      */
  338.     abstract protected function setAddress(AddressModel $objAddress);
  339.     /**
  340.      * Append DCA configuration to fields so it can be changed in hook.
  341.      *
  342.      * @param array $fieldConfig
  343.      *
  344.      * @return array
  345.      */
  346.     private function mergeFieldsWithDca(array $fieldConfig)
  347.     {
  348.         $fields = [];
  349.         foreach ($fieldConfig as $field) {
  350.             // Do not use reference, otherwise the billing address fields would affect shipping address fields
  351.             $dca $GLOBALS['TL_DCA'][AddressModel::getTable()]['fields'][$field['value']];
  352.             if (\is_array($dca)) {
  353.                 $field['dca'] = $dca;
  354.             }
  355.             $fields[$field['value']] = $field;
  356.         }
  357.         return $fields;
  358.     }
  359. }