vendor/numero2/contao-storelocator/src/Resources/contao/classes/StoreLocator.php line 113

Open in your IDE?
  1. <?php
  2. /**
  3.  * StoreLocator Bundle for Contao Open Source CMS
  4.  *
  5.  * @author    Benny Born <benny.born@numero2.de>
  6.  * @author    Michael Bösherz <michael.boesherz@numero2.de>
  7.  * @license   LGPL-3.0-or-later
  8.  * @copyright Copyright (c) 2024, numero2 - Agentur für digitales Marketing GbR
  9.  */
  10. namespace numero2\StoreLocator;
  11. use Contao\Config;
  12. use Contao\Controller;
  13. use Contao\FrontendTemplate;
  14. use Contao\Input;
  15. use Contao\Module;
  16. use Contao\PageModel;
  17. use Contao\StringUtil;
  18. use Contao\System;
  19. use Contao\Validator;
  20. use Geocoder\Query\GeocodeQuery;
  21. use numero2\StoreLocator\DCAHelper\Stores;
  22. class StoreLocator {
  23.     /**
  24.      * Replace matching Insert Tags
  25.      *
  26.      * @param string $strBuffer
  27.      * @param bool $blnCache
  28.      *
  29.      * @return string|boolean
  30.      */
  31.     public function replaceInsertTags$strBuffer$blnCache=false ) {
  32.         Controller::loadDataContainer(StoresModel::getTable());
  33.         $aParams = [];
  34.         $aParams explode('::'$strBuffer);
  35.         switch( $aParams[0] ) {
  36.             case 'store' :
  37.                 $aDCAFields = [];
  38.                 $aDCAFields array_keys($GLOBALS['TL_DCA'][StoresModel::getTable()]['fields']);
  39.                 // get data from current store
  40.                 if( !empty($aParams[1]) && in_array($aParams[1], $aDCAFields) ) {
  41.                     $alias null;
  42.                     $alias Input::get('auto_item') ? Input::get('auto_item') : Input::get('store');
  43.                     // find store
  44.                     $objStore null;
  45.                     $objStore StoresModel::findByIdOrAlias($alias);
  46.                     if( !$objStore ) {
  47.                         return false;
  48.                     }
  49.                     self::parseStoreData($objStore);
  50.                     return $objStore->{$aParams[1]};
  51.                 // get specific store
  52.                 } else {
  53.                     $oTemplate = new FrontendTemplate('mod_storelocator_inserttag');
  54.                     // find store
  55.                     $objStore null;
  56.                     $objStore StoresModel::findByIdOrAlias($aParams[1]);
  57.                     if( !$objStore ) {
  58.                         return false;
  59.                     }
  60.                     self::parseStoreData($objStore);
  61.                     $oTemplate->store $objStore;
  62.                     $sTemplate $oTemplate->parse();
  63.                     $sTemplate Controller::replaceInsertTags($sTemplate);
  64.                     return $sTemplate;
  65.                 }
  66.                 break;
  67.             // not our insert tag?
  68.             default :
  69.                 return false;
  70.                 break;
  71.         }
  72.         return false;
  73.     }
  74.     /**
  75.      * Parses the given store so we can use it directly to
  76.      * display the details template
  77.      *
  78.      * @param numero2\StoreLocator\StoresModel $store
  79.      * @param Contao\Module $module
  80.      */
  81.     public static function parseStoreDataStoresModel $store, ?Module $module=null ): void {
  82.         $store->name StringUtil::restoreBasicEntities($store->name);
  83.         $store->description StringUtil::restoreBasicEntities($store->description);
  84.         // validate latitude and longitude
  85.         if( !Validator::isNumeric($store->latitude) || !Validator::isNumeric($store->longitude) ) {
  86.             $store->latitude '';
  87.             $store->longitude '';
  88.             System::getContainer()->get('monolog.logger.contao.error')->error('Error parsing geocords ('$store->latitude .','$store->longitude .') for store ID '.$store->id);
  89.         }
  90.         $store->latitude floatval($store->latitude);
  91.         $store->longitude floatval($store->longitude);
  92.         if( $store->latitude < -90 || $store->latitude 90 || $store->longitude < -180 || $store->longitude 180 ) {
  93.             $store->latitude '';
  94.             $store->longitude '';
  95.             System::getContainer()->get('monolog.logger.contao.error')->error('Error parsing geocords not in range ('$store->latitude .','$store->longitude .') for store ID '.$store->id);
  96.         }
  97.         // get opening times
  98.         $aTimes StringUtil::deserialize$store->opening_times );
  99.         $aTimes = !empty($aTimes[0]['from']) ? $aTimes null;
  100.         if( !empty($aTimes) ) {
  101.             $aWeekdays = [];
  102.             $aWeekdays StoreLocator::getWeekdays();
  103.             foreach( $aTimes as $i => $day ) {
  104.                 $aTimes[$i]['label'] = $aWeekdays$day['weekday'] ];
  105.             }
  106.         }
  107.         $store->opening_times $aTimes;
  108.         // set country name
  109.         $aCountryNames = [];
  110.         $aCountryNames Stores::getCountries();
  111.         $store->country_code $store->country;
  112.         $store->country_name $aCountryNames$store->country ];
  113.         // create a clickable link for telephone number
  114.         if( !empty($store->phone) ) {
  115.             $store->phoneLink 'tel:'.preg_replace("|[^\+0-9]|"""$store->phone);
  116.         }
  117.         // create a clickable link for fax number
  118.         if( !empty($store->fax) ) {
  119.             $store->faxLink 'fax:'.preg_replace("|[^\+0-9]|"""$store->fax);
  120.         }
  121.         // create a "pretty" url
  122.         if( !empty($store->url) ) {
  123.             $aURL = [];
  124.             $aURL parse_url($store->url);
  125.             if( !empty($aURL['host']) ) {
  126.                 $store->prettyUrl $aURL['host'];
  127.             }
  128.         }
  129.         // add link to details
  130.         if( $module && $module->jumpTo ) {
  131.             $objLink null;
  132.             $objLink PageModel::findById($module->jumpTo);
  133.             if( $objLink ) {
  134.                 $store->link $objLink->getFrontendUrl((!Config::get('useAutoItem')?'/store/':'/').($store->alias?$store->alias:$store->id));
  135.             }
  136.         }
  137.         // HOOK: add custom logic to parse the store details
  138.         if( isset($GLOBALS['N2SL_HOOKS']['parseStoreData']) && is_array($GLOBALS['N2SL_HOOKS']['parseStoreData']) ) {
  139.             foreach( $GLOBALS['N2SL_HOOKS']['parseStoreData'] as $callback ) {
  140.                 if( is_array($callback) ) {
  141.                     System::importStatic($callback[0])->{$callback[1]}($store$module);
  142.                 }
  143.             }
  144.         }
  145.     }
  146.     /**
  147.      * Returns a list of weekdays
  148.      *
  149.      * @return array
  150.      */
  151.     public static function getWeekdays(): array {
  152.         return [
  153.             'MO' => &$GLOBALS['TL_LANG']['DAYS'][1]
  154.         ,   'TU' => &$GLOBALS['TL_LANG']['DAYS'][2]
  155.         ,   'WE' => &$GLOBALS['TL_LANG']['DAYS'][3]
  156.         ,   'TH' => &$GLOBALS['TL_LANG']['DAYS'][4]
  157.         ,   'FR' => &$GLOBALS['TL_LANG']['DAYS'][5]
  158.         ,   'SA' => &$GLOBALS['TL_LANG']['DAYS'][6]
  159.         ,   'SU' => &$GLOBALS['TL_LANG']['DAYS'][0]
  160.         ];
  161.     }
  162.     /**
  163.      * Find coordinates for given address
  164.      *
  165.      * @param string Street
  166.      * @param string Postal/ZIP Code
  167.      * @param string Name of city
  168.      * @param string 2-letter country code
  169.      * @param string Address string without specific format
  170.      *
  171.      * @return array
  172.      */
  173.     public function getCoordinates$street=null$postal=null$city=null$country=null$fullAddress=null ): array {
  174.         trigger_deprecation('numero2/contao-storelocator''4.3''Using StoreLocator::getCoordinates() has been deprecated and will no longer work in Storelocator 5.0. Use the service "numero2_storelocator.util.store_locator" instead.');
  175.         return System::getContainer()->get('numero2_storelocator.util.store_locator')->getCoordinates(nullnullnullnull$fullAddressfalse);
  176.     }
  177.     /**
  178.      * Gets coordinates for an address without a specific format
  179.      *
  180.      * @param string The address
  181.      *
  182.      * @return array
  183.      */
  184.     public function getCoordinatesByStringstring $fullAddress=null ): array {
  185.         trigger_deprecation('numero2/contao-storelocator''4.3''Using StoreLocator::getCoordinatesByString() has been deprecated and will no longer work in Storelocator 5.0. Use the service "numero2_storelocator.util.store_locator" instead.');
  186.         return System::getContainer()->get('numero2_storelocator.util.store_locator')->getCoordinatesByString($fullAddressfalse);
  187.     }
  188.     /**
  189.      * Parses the given search value into its components.
  190.      *
  191.      * @param string The generated search string
  192.      *
  193.      * @return array
  194.      */
  195.     public static function parseSearchValue$searchVal=null ): array {
  196.         if( !$searchVal ) {
  197.             return [];
  198.         }
  199.         if( strpos($searchVal";") !== false ) {
  200.             $searchVal explode(";"$searchVal);
  201.         }
  202.         $ret = [];
  203.         if( is_array($searchVal) ) {
  204.             if( count($searchVal) == ) {
  205.                 $ret['filter'] = $searchVal[0];
  206.                 $ret['order'] = $searchVal[1];
  207.                 $ret['sort'] = $searchVal[2];
  208.             } else if( count($searchVal) == ) {
  209.                 $ret['filter'] = $searchVal[0];
  210.                 $ret['order'] = $searchVal[1];
  211.                 $ret['sort'] = $searchVal[2];
  212.                 $ret['tags'] = $searchVal[3];
  213.             } else {
  214.                 if( !empty($searchVal[0]) ) $ret['term'] = $searchVal[0];
  215.                 if( !empty($searchVal[1]) ) $ret['category'] = $searchVal[1];
  216.                 if( !empty($searchVal[2]) ) $ret['longitude'] = $searchVal[2];
  217.                 if( !empty($searchVal[3]) ) $ret['latitude'] = $searchVal[3];
  218.                 if( !empty($searchVal[4]) ) $ret['filter'] = $searchVal[4];
  219.                 if( !empty($searchVal[5]) ) $ret['order'] = $searchVal[5];
  220.                 if( !empty($searchVal[6]) ) $ret['sort'] = $searchVal[6];
  221.                 if( !empty($searchVal[7]) ) $ret['tags'] = $searchVal[7];
  222.             }
  223.         } else {
  224.             $ret['term'] = $searchVal;
  225.         }
  226.         foreach( $ret as $i => $d ) {
  227.             $ret[$i] = urldecode($d);
  228.         }
  229.         return $ret;
  230.     }
  231.     /**
  232.      * Generates the search string
  233.      *
  234.      * @param array $arrData
  235.      *
  236.      * @return string
  237.      */
  238.     public static function generateSearchValue$arrData ): string {
  239.         if( !is_array($arrData) ) {
  240.             return '';
  241.         }
  242.         $aData = [];
  243.         if( !empty($arrData['term']) ) {
  244.             $aData[0] = html_entity_decode($arrData['term']);
  245.             if( !empty($arrData['category']) ) {
  246.                 $aData[1] = $arrData['category'];
  247.             }
  248.             if( !empty($arrData['longitude']) && !empty($arrData['latitude']) ) {
  249.                 $aData[1] = !empty($aData[1])?$aData[1]:'';
  250.                 $aData[2] = $arrData['longitude'];
  251.                 $aData[3] = $arrData['latitude'];
  252.                 $aData[4] = '';
  253.             }
  254.         }
  255.         if( !empty($arrData['filter']) || !empty($arrData['order']) || !empty($arrData['sort']) || !empty($arrData['tags']) ) {
  256.             if( count($aData) == ) {
  257.                 $aData[0] = $arrData['filter']??'';
  258.                 $aData[1] = $arrData['order']??'';
  259.                 $aData[2] = $arrData['sort']??'';
  260.                 if( !empty($arrData['tags']) ) {
  261.                     $aData[3] = $arrData['tags'];
  262.                 }
  263.             } else {
  264.                 $aData[0] = $aData[0]??'';
  265.                 $aData[1] = $aData[1]??'';
  266.                 $aData[2] = $aData[2]??'';
  267.                 $aData[3] = $aData[3]??'';
  268.                 $aData[4] = $arrData['filter']??'';
  269.                 $aData[5] = $arrData['order']??'';
  270.                 $aData[6] = $arrData['sort']??'';
  271.                 $aData[7] = $arrData['tags']??'';
  272.             }
  273.         }
  274.         foreach( $aData as $i => $d ) {
  275.             $aData[$i] = urlencode($d);
  276.         }
  277.         $strData = ( count($aData) > ) ? implode(';',$aData) : ($aData[0]??'');
  278.         return $strData;
  279.     }
  280.     /**
  281.      * Create filter where from value and field list and an optional tag id
  282.      *
  283.      * @param string $value
  284.      * @param array $fields
  285.      * @param string $tagId
  286.      *
  287.      * @return string
  288.      */
  289.     public static function createFilterWhereClausestring $searchValue, array $fields, ?string $tagId=null ): string {
  290.         $ret = [];
  291.         if( !empty($searchValue) ) {
  292.             foreach( $fields as $key => $field ) {
  293.                 $ret[] = $field." LIKE '%%".$searchValue."%%'";
  294.             }
  295.         }
  296.         if( !empty($ret) ) {
  297.             $ret '('.implode(" OR "$ret).')';
  298.         } else {
  299.             $ret '';
  300.         }
  301.         if( !empty($tagId) ) {
  302.             if( strlen($ret) ) {
  303.                 $ret .= "AND ";
  304.             }
  305.             $ret .= "id IN ( SELECT s.id
  306.                 FROM tl_storelocator_stores AS s
  307.                 JOIN tl_tags_rel as r on (r.pid = s.id AND r.ptable = 'tl_storelocator_stores' AND r.field = 'tags' AND r.tag_id = $tagId)
  308.             )";
  309.         }
  310.         return $ret;
  311.     }
  312. }