vendor/isotope/isotope-core/system/modules/isotope/library/Isotope/Model/Address.php line 396

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\Model;
  11. use Contao\Controller;
  12. use Contao\MemberModel;
  13. use Contao\Model;
  14. use Contao\Model\Collection;
  15. use Contao\StringUtil;
  16. use Contao\System;
  17. use Database\Result;
  18. use Haste\Util\Format;
  19. use Isotope\Backend;
  20. use Isotope\Interfaces\IsotopeProductCollection;
  21. use Isotope\Interfaces\IsotopeVatNoValidator;
  22. use Isotope\Isotope;
  23. /**
  24.  * Class Address
  25.  *
  26.  * @property int    $id
  27.  * @property int    $pid
  28.  * @property string $ptable
  29.  * @property string $label
  30.  * @property int    $store_id
  31.  * @property string $gender
  32.  * @property string $salutation
  33.  * @property string $firstname
  34.  * @property string $lastname
  35.  * @property int    $dateOfBirth
  36.  * @property string $company
  37.  * @property string $vat_no
  38.  * @property string $street_1
  39.  * @property string $street_2
  40.  * @property string $street_3
  41.  * @property string $postal
  42.  * @property string $city
  43.  * @property string $subdivision
  44.  * @property string $country
  45.  * @property string $phone
  46.  * @property string $email
  47.  * @property bool   $isDefaultShipping
  48.  * @property bool   $isDefaultBilling
  49.  */
  50. class Address extends Model
  51. {
  52.     /**
  53.      * Table
  54.      * @var string
  55.      */
  56.     protected static $strTable 'tl_iso_address';
  57.     /**
  58.      * Construct the model
  59.      *
  60.      * @param Result $objResult
  61.      */
  62.     public function __construct(Result $objResult null)
  63.     {
  64.         parent::__construct($objResult);
  65.         if (!\is_array($GLOBALS['ISO_ADR'] ?? null)) {
  66.             Controller::loadDataContainer(static::$strTable);
  67.             System::loadLanguageFile('addresses');
  68.         }
  69.     }
  70.     /**
  71.      * @return string
  72.      */
  73.     public function __toString()
  74.     {
  75.         try {
  76.             return $this->generate();
  77.         } catch (\Exception $e) {
  78.             return '';
  79.         }
  80.     }
  81.     /**
  82.      * Check if the address has a valid VAT number
  83.      *
  84.      * @param Config $config
  85.      *
  86.      * @return bool
  87.      *
  88.      * @throws \LogicException if a validator does not implement the correct interface
  89.      * @throws \RuntimeException if a validators reports an error about the VAT number
  90.      */
  91.     public function hasValidVatNo(Config $config null)
  92.     {
  93.         if (null === $config) {
  94.             $config Isotope::getConfig();
  95.         }
  96.         $validators StringUtil::deserialize($config->vatNoValidators);
  97.         // if no validators are enabled, the VAT No is always valid
  98.         if (!\is_array($validators) || === \count($validators)) {
  99.             return true;
  100.         }
  101.         foreach ($validators as $class) {
  102.             $service = new $class();
  103.             if (!($service instanceof IsotopeVatNoValidator)) {
  104.                 throw new \LogicException($class ' does not implement IsotopeVatNoValidator interface');
  105.             }
  106.             $result $service->validate($this);
  107.             if (true === $result) {
  108.                 return true;
  109.             }
  110.         }
  111.         return false;
  112.     }
  113.     /**
  114.      * Return formatted address (hCard)
  115.      *
  116.      * @param array $arrFields
  117.      *
  118.      * @return string
  119.      *
  120.      * @throws \Exception on error parsing simple tokens
  121.      */
  122.     public function generate($arrFields null)
  123.     {
  124.         // We need a country to format the address, use default country if none is available
  125.         $strCountry $this->country ?: Isotope::getConfig()->country;
  126.         // Use generic format if no country specific format is available
  127.         $strFormat $GLOBALS['ISO_ADR'][$strCountry] ?? $GLOBALS['ISO_ADR']['generic'];
  128.         $arrTokens  $this->getTokens($arrFields);
  129.         return StringUtil::parseSimpleTokens($strFormat$arrTokens);
  130.     }
  131.     /**
  132.      * Return this address formatted as text
  133.      *
  134.      * @param array $arrFields
  135.      *
  136.      * @return string
  137.      *
  138.      * @deprecated use Address::generate() and strip_tags
  139.      * @throws \Exception on invalid simple tokens
  140.      */
  141.     public function generateText($arrFields null)
  142.     {
  143.         return strip_tags($this->generate($arrFields));
  144.     }
  145.     /**
  146.      * Return an address formatted with HTML (hCard)
  147.      *
  148.      * @param array $arrFields
  149.      *
  150.      * @return string
  151.      *
  152.      * @deprecated use Address::generate()
  153.      * @throws \Exception on invalid simple tokens
  154.      */
  155.     public function generateHtml($arrFields null)
  156.     {
  157.         return $this->generate($arrFields);
  158.     }
  159.     /**
  160.      * Compile the list of hCard tokens for this address
  161.      *
  162.      * @param array $arrFields
  163.      *
  164.      * @return array
  165.      */
  166.     public function getTokens($arrFields null)
  167.     {
  168.         if (!\is_array($arrFields)) {
  169.             $arrFields Isotope::getConfig()->getBillingFieldsConfig();
  170.         }
  171.         $arrTokens = array('outputFormat' => 'html');
  172.         foreach ($arrFields as $arrField) {
  173.             $strField $arrField['value'];
  174.             // Set an empty value for disabled fields, otherwise the token would not be replaced
  175.             if (!$arrField['enabled']) {
  176.                 $arrTokens[$strField] = '';
  177.                 continue;
  178.             }
  179.             if ('subdivision' === $strField && $this->subdivision != '') {
  180.                 [$country$subdivision] = explode('-'$this->subdivision);
  181.                 $arrTokens['subdivision_abbr'] = $subdivision;
  182.                 $arrTokens['subdivision']      = Backend::getLabelForSubdivision($country$this->subdivision);
  183.                 continue;
  184.             }
  185.             $arrTokens[$strField] = Format::dcaValue(static::$strTable$strField$this->$strField);
  186.         }
  187.         /**
  188.          * Generate hCard fields
  189.          * See http://microformats.org/wiki/hcard
  190.          */
  191.         // Set "fn" (full name) to company if no first- and lastname is given
  192.         if ($arrTokens['company'] != '') {
  193.             $fn        $arrTokens['company'];
  194.             $fnCompany ' fn';
  195.         } else {
  196.             $fn        trim($arrTokens['firstname'] . ' ' $arrTokens['lastname']);
  197.             $fnCompany '';
  198.         }
  199.         $street implode('<br>'array_filter([$this->street_1$this->street_2$this->street_3]));
  200.         $arrTokens += [
  201.             'hcard_fn'               => $fn '<span class="fn">' $fn '</span>' '',
  202.             'hcard_n'                => ($arrTokens['firstname'] || $arrTokens['lastname']) ? '1' '',
  203.             'hcard_honorific_prefix' => $arrTokens['salutation'] ? '<span class="honorific-prefix">' $arrTokens['salutation'] . '</span>' '',
  204.             'hcard_given_name'       => $arrTokens['firstname'] ? '<span class="given-name">' $arrTokens['firstname'] . '</span>' '',
  205.             'hcard_family_name'      => $arrTokens['lastname'] ? '<span class="family-name">' $arrTokens['lastname'] . '</span>' '',
  206.             'hcard_org'              => $arrTokens['company'] ? '<div class="org' $fnCompany '">' $arrTokens['company'] . '</div>' '',
  207.             'hcard_email'            => $arrTokens['email'] ? '<a href="mailto:' $arrTokens['email'] . '">' $arrTokens['email'] . '</a>' '',
  208.             'hcard_tel'              => $arrTokens['phone'] ? '<div class="tel">' $arrTokens['phone'] . '</div>' '',
  209.             'hcard_adr'              => ($street || $arrTokens['city'] || $arrTokens['postal'] || $arrTokens['subdivision'] || $arrTokens['country']) ? '1' '',
  210.             'hcard_street_address'   => $street '<div class="street-address">' $street '</div>' '',
  211.             'hcard_locality'         => $arrTokens['city'] ? '<span class="locality">' $arrTokens['city'] . '</span>' '',
  212.             'hcard_region'           => $arrTokens['subdivision'] ? '<span class="region">' $arrTokens['subdivision'] . '</span>' '',
  213.             'hcard_region_abbr'      => !empty($arrTokens['subdivision_abbr']) ? '<abbr class="region" title="' $arrTokens['subdivision'] . '">' $arrTokens['subdivision_abbr'] . '</abbr>' '',
  214.             'hcard_postal_code'      => $arrTokens['postal'] ? '<span class="postal-code">' $arrTokens['postal'] . '</span>' '',
  215.             'hcard_country_name'     => $arrTokens['country'] ? '<div class="country-name">' $arrTokens['country'] . '</div>' '',
  216.         ];
  217.         return $arrTokens;
  218.     }
  219.     /**
  220.      * Find address for member, automatically checking the current store ID and tl_member parent table
  221.      *
  222.      * @param int   $intMember
  223.      * @param array $arrOptions
  224.      *
  225.      * @return Collection|null
  226.      */
  227.     public static function findForMember($intMember, array $arrOptions = array())
  228.     {
  229.         return static::findBy(
  230.             array('pid=?''ptable=?''store_id=?'),
  231.             array($intMember'tl_member'Isotope::getCart()->store_id),
  232.             $arrOptions
  233.         );
  234.     }
  235.     /**
  236.      * Find address by ID and member, automatically checking the current store ID and tl_member parent table
  237.      *
  238.      * @param int   $intId
  239.      * @param int   $intMember
  240.      * @param array $arrOptions
  241.      *
  242.      * @return Address|null
  243.      */
  244.     public static function findOneForMember($intId$intMember, array $arrOptions = array())
  245.     {
  246.         return static::findOneBy(
  247.             array('id=?''pid=?''ptable=?''store_id=?'),
  248.             array($intId$intMember'tl_member'Isotope::getCart()->store_id),
  249.             $arrOptions
  250.         );
  251.     }
  252.     /**
  253.      * Find default billing adddress for a member, automatically checking the current store ID and tl_member parent table
  254.      * @param   int
  255.      * @param   array
  256.      * @return  static|null
  257.      */
  258.     public static function findDefaultBillingForMember($intMember, array $arrOptions = array())
  259.     {
  260.         return static::findOneBy(
  261.             array('pid=?''ptable=?''store_id=?''isDefaultBilling=?'),
  262.             array($intMember'tl_member'Isotope::getCart()->store_id'1'),
  263.             $arrOptions
  264.         );
  265.     }
  266.     /**
  267.      * Find default shipping adddress for a member, automatically checking the current store ID and tl_member parent table
  268.      * @param   int
  269.      * @param   array
  270.      * @return  static|null
  271.      */
  272.     public static function findDefaultShippingForMember($intMember, array $arrOptions = array())
  273.     {
  274.         return static::findOneBy(array('pid=?''ptable=?''store_id=?''isDefaultShipping=?'), array($intMember'tl_member'Isotope::getCart()->store_id'1'), $arrOptions);
  275.     }
  276.     /**
  277.      * Find default billing address for a product collection
  278.      *
  279.      * @param int   $intCollection
  280.      * @param array $arrOptions
  281.      *
  282.      * @return static|null
  283.      */
  284.     public static function findDefaultBillingForProductCollection($intCollection, array $arrOptions = array())
  285.     {
  286.         return static::findOneBy(
  287.             array('pid=?''ptable=?''isDefaultBilling=?'),
  288.             array($intCollection'tl_iso_product_collection''1'),
  289.             $arrOptions
  290.         );
  291.     }
  292.     /**
  293.      * Find default shipping address for a product collection
  294.      *
  295.      * @param int   $intCollection
  296.      * @param array $arrOptions
  297.      *
  298.      * @return static|null
  299.      */
  300.     public static function findDefaultShippingForProductCollection($intCollection, array $arrOptions = array())
  301.     {
  302.         return static::findOneBy(
  303.             array('pid=?''ptable=?''isDefaultShipping=?'),
  304.             array($intCollection'tl_iso_product_collection''1'),
  305.             $arrOptions
  306.         );
  307.     }
  308.     /**
  309.      * Create a new address for a member and automatically set default properties
  310.      *
  311.      * @param int        $intMember
  312.      * @param array|null $arrFill
  313.      *
  314.      * @return static
  315.      */
  316.     public static function createForMember($intMember$arrFill null)
  317.     {
  318.         $objAddress = new static();
  319.         $arrData = array(
  320.             'pid'      => $intMember,
  321.             'ptable'   => 'tl_member',
  322.             'tstamp'   => time(),
  323.             'store_id' => (int) Isotope::getCart()->store_id,
  324.         );
  325.         if (!empty($arrFill) && \is_array($arrFill) && ($objMember MemberModel::findByPk($intMember)) !== null) {
  326.             $arrData array_merge(static::getAddressDataForMember($objMember$arrFill), $arrData);
  327.         }
  328.         $objAddress->setRow($arrData);
  329.         return $objAddress;
  330.     }
  331.     /**
  332.      * Create a new address for a product collection
  333.      *
  334.      * @param IsotopeProductCollection $objCollection
  335.      * @param array|null               $arrFill an array of member fields to inherit
  336.      * @param bool                     $blnDefaultBilling
  337.      * @param bool                     $blnDefaultShipping
  338.      *
  339.      * @return static
  340.      */
  341.     public static function createForProductCollection(
  342.         IsotopeProductCollection $objCollection,
  343.         $arrFill null,
  344.         $blnDefaultBilling false,
  345.         $blnDefaultShipping false
  346.     ) {
  347.         $objAddress = new static();
  348.         $arrData = array(
  349.             'pid'               => $objCollection->getId(),
  350.             'ptable'            => 'tl_iso_product_collection',
  351.             'tstamp'            => time(),
  352.             'store_id'          => $objCollection->getStoreId(),
  353.             'isDefaultBilling'  => $blnDefaultBilling '1' '',
  354.             'isDefaultShipping' => $blnDefaultShipping '1' '',
  355.         );
  356.         if (!empty($arrFill) && \is_array($arrFill) && ($objMember $objCollection->getMember()) !== null) {
  357.             $arrData array_merge(static::getAddressDataForMember($objMember$arrFill), $arrData);
  358.         }
  359.         if (empty($arrData['country']) && null !== ($objConfig $objCollection->getConfig())) {
  360.             if ($blnDefaultBilling) {
  361.                 $arrData['country'] = $objConfig->billing_country ?: $objConfig->country;
  362.             } elseif ($blnDefaultShipping) {
  363.                 $arrData['country'] = $objConfig->shipping_country ?: $objConfig->country;
  364.             }
  365.         }
  366.         $objAddress->setRow($arrData);
  367.         return $objAddress;
  368.     }
  369.     /**
  370.      * Generate address data from tl_member, limit to fields enabled in the shop configuration
  371.      */
  372.     public static function getAddressDataForMember(MemberModel $member, array $fields)
  373.     {
  374.         return array_intersect_key(
  375.             array_merge(
  376.                 $member->row(),
  377.                 array(
  378.                     'street_1'    => $member->street,
  379.                     // Trying to guess subdivision by country and state
  380.                     'subdivision' => strtoupper($member->country '-' $member->state)
  381.                 )
  382.             ),
  383.             array_flip($fields)
  384.         );
  385.     }
  386. }