YandexMaps2 с напильником

На один сайт нужно яндекс карту совместимую с mFilter2. Приобрели YandexMaps2. Как оказалось, компонент не совсем подходит по наши нужды. Во первых, нужно в поиске чтоб высвечивались не только точки на карте, но и были ниже карты сами результаты поиска. Во вторых, нужно на карте выводить подпись и балун общие для всех точек. Так как это сделано на предыдущей версии сайта.

К счастью, нужный функционал оказалось не сложно допилить напильником. Под катом описание.

1. Нужно в поиске чтоб высвечивались не только точки на карте, но и были ниже карты сами результаты поиска.
В Работа с mFilter2 на шаге 3 меняют чанк tpl.mSearch2.row
{'!YandexMaps2' | snippet : [
    'parent' => $id,
    'scripts' => false,
    'tpl' => '@INLINE {($objects | toJSON) | replace : "[" : "[ "}',
]}
То есть, в результаты поиска попадают только координаты точек на карте.
И в ява-скрипте assets/components/yandexmaps2/js/web/default.js в строке
$(document).on('mse2_load', function (e, response) {
ловиться событие mse2_load mFilter2
И в строке
var results = JSON.parse('[' + response.data['results'] + ']');

и ниже эти метки добавляются на карту.
Решение
Меняем чанк tpl.mSearch2.row так
<div class="mse2-row">
	[[+idx]]. <a href="[[+uri]]" class="search-link">[[+pagetitle]]</a>[[+weight]]
	[[+intro]]
</div>

<!--msearch2_weight  ([[%mse2_weight]]: [[+weight]])-->
<!--msearch2_intro <p>[[+intro]]</p>-->
<!-- 
YandexMaps2Start{'!YandexMaps2' | snippet : [
    'parent' => $id,
    'scripts' => false,
    'tpl' => '@INLINE {($objects | toJSON) | replace : "[" : "[ "}',
]}YandexMaps2End
-->
И затем объекты карты вылавливаем из результатов поиска с помощью регулярок (YandexMaps2Start и YandexMaps2End пишем слитно без переносов строки. а то регулярки не сработают).
Меняем в ява-скрипте assets/components/yandexmaps2/js/web/default.js с 61 строки до if (typeof(results) == 'object') { на следующий код
$(document).on('mse2_load', function (e, response) {
                        if (response['success']) {
                             //console.log('YandexMaps2 mse2_load response', response);
                            var s = response.data['results'];
                            var results1 = s.match(/(?<=YandexMaps2Start)(.*?)(?=YandexMaps2End)/g);

                            //console.log(s);
                            //console.log(results1);
                            if(results1 === null){
                                results1 ="";
                            }else{
                                results1 = results1.join();
                            } 
                            var results = JSON.parse('[' + results1 + ']');
                            // var results = JSON.parse('[' + response.data['results'] + ']');
                            if (typeof(results) == 'object') {
В чанке tpl.mFilter2.outer в нужном месте выводим карту с объектами
<div class="row msearch2" id="mse2_mfilter">
	<div class="span3 col-md-3">
		<form action="[[~[[*id]]]]" method="post" id="mse2_filters">
			[[+filters]]
			
			[[+filters:isnot=``:then=`
				<button type="reset" class="btn btn-default hidden">[[%mse2_reset]]</button>
				<button type="submit" class="btn btn-success pull-right hidden">[[%mse2_submit]]</button>
				<div class="clearfix"></div>
			`]]
		</form>

		

		<div>[[%mse2_limit]]
			<select name="mse_limit" id="mse2_limit">
				<option value="10" [[+limit:is=`10`:then=`selected`]]>10</option>
				<option value="25" [[+limit:is=`25`:then=`selected`]]>25</option>
				<option value="50" [[+limit:is=`50`:then=`selected`]]>50</option>
				<option value="100" [[+limit:is=`100`:then=`selected`]]>100</option>
			</select>
		</div>
	</div>
    <div class="span9 col-md-9">
        <!-- вывод карты -->
        {var $objects = 'YM2GetObjectsFromMF2Result' | snippet : ['results' => $results]}
        {'!YandexMaps2' | snippet : [
            'objects' => $objects,
            'mode' => 'mfilter2',
        ]}
    </div>
	<div class="span9 col-md-9">
		<h3>[[%mse2_filter_total]] <span id="mse2_total">[[+total:default=`0`]]</span></h3>

		<div class="row">
			<div id="mse2_sort" class="span5 col-md-5">
				[[%mse2_sort]]
				<a href="#" data-sort="resource|publishedon" data-dir="[[+mse2_sort:is=`resource|publishedon:desc`:then=`desc`]]" data-default="desc" class="sort">[[%mse2_sort_publishedon]] <span></span></a>
			</div>

			[[+tpls:notempty=`
			<div id="mse2_tpl" class="span4 col-md-4">
				<a href="#" data-tpl="0" class="[[+tpl0]]">[[%mse2_chunk_default]]</a> /
				<a href="#" data-tpl="1" class="[[+tpl1]]">[[%mse2_chunk_alternate]]</a>
			</div>
			`]]
		</div>

		<div id="mse2_selected_wrapper">
			<div id="mse2_selected">[[%mse2_selected]]:
				<span></span>
			</div>
		</div>

		<div id="mse2_results">
			[[+results]]
		</div>

		<div class="mse2_pagination">
			[[!+page.nav]]
		</div>

	</div>
</div>
Сниппет YM2GetObjectsFromMF2Result вылавливает из результатов поиска объекты карты:
<?php
preg_match_all('|(?<=YandexMaps2Start)(.*?)(?=YandexMaps2End)|', $results, $matches);
//$modx->log(1,$results);
//$modx->log(1,print_r($matches,1));
if(isset($matches[1])){
    return implode(",",$matches[1]);
}else{
    return "";
}
Вывод mFilter2 в нужном месте
[[!mFilter2?
&parents=`[[*id]]`
&limit=`0`
&filters=`
tv|city,
tv|metro,
`
&showLog=`0`
&_filterOptions=`{«autoLoad»:0}`
&tplOuter=`tpl.mFilter2.outer2`
]]
tv|city,tv|metro подставте ваши фильтры.

Нужно на карте выводить подпись и балун общие для всех точек
Решение
Меняем сниппет YandexMaps2 добавляем параметры для подписей и балунов.
<?php
/** @var modX $modx */
/** @var YandexMaps2 $ym2 */
/** @var array $scriptProperties */
$sp = &$scriptProperties;
$modelPath = MODX_CORE_PATH . 'components/yandexmaps2/model/yandexmaps2/';
if (!$ym2 = $modx->getService('yandexmaps2', 'YandexMaps2', $modelPath)) {
    return 'Could not load YandexMaps2 class!';
}
$ym2->initialize($modx->context->key);

//
$tpl = $sp['tpl'] ?: 'tpl.YandexMaps2';
$sp['parent'] = isset($sp['parent']) ? $sp['parent'] : 0;
$sp['class'] = isset($sp['class']) ? $sp['class'] : '';
$sp['list'] = !empty($sp['list']) ? $sp['list'] : 'default';
$sp['map'] = !empty($sp['map']) ? $sp['map'] : 'ym2map';
$sp['center'] = is_array($sp['center']) ? $sp['center'] : ($ym2->tools->isJSON($sp['center']) ? $sp['center'] : ('[' . $sp['center'] . ']'));
$sp['center'] = is_array($sp['center']) ? $sp['center'] : $modx->fromJSON($sp['center']);
$sp['center'] = !empty($sp['center']) ? $sp['center'] : '[55.72, 37.64]';
$sp['zoom'] = !empty($sp['zoom']) ? $sp['zoom'] : 10;
$sp['scrollZoom'] = isset($sp['scrollZoom']) ? $sp['scrollZoom'] : true;
$sp['mode'] = strtolower(!empty($sp['mode']) ? $sp['mode'] : 'default');
$properties['iconContent'] = isset($sp['tpl.iconContent']) ? $sp['tpl.iconContent'] : '';
$properties['iconCaption'] = isset($sp['tpl.iconCaption']) ? $sp['tpl.iconCaption'] : '';
$properties['balloonContent'] = isset($sp['tpl.balloonContent']) ? $sp['tpl.balloonContent'] : '';
$includeTVs = isset($sp['includeTVs']) ? $sp['includeTVs'] : '';
unset($sp['tpl']);


//
if (isset($sp['objects'])) {
    $objects = array();
    if (!empty($sp['objects']) && !is_array($sp['objects']) && !$ym2->tools->isJSON(!$sp['objects'])) {
        $tmp = $modx->fromJSON('[' . $sp['objects'] . ']');
        foreach ($tmp as $v) {
            $objects = array_merge($objects, $v);
        }
    }
    unset($tmp);

    $objects = !empty($objects) ? $objects : array();
    $objects = is_array($objects) ? $objects : $modx->fromJSON($objects);
    $sp['objects'] = is_array($objects) ? $objects : array();
} else {
    $sp['objects'] = $ym2->getObjects(array(
        'parent' => $sp['parent'],
        'class' => $sp['class'],
        'list' => $sp['list'],
    ),$properties,$includeTVs);
}

// if ($sp['objectsPlaceholder']) {
//     //
//     if (!empty($sp['objects'])) {
//         $objects = $modx->getPlaceholder($sp['objectsPlaceholder']);
//         $objects = is_array($objects) ? $objects : ($ym2->tools->isJSON($objects) ? $modx->fromJSON($objects) : array());
//         $objects = array_merge($objects, $sp['objects']);
//         $modx->setPlaceholder($sp['objectsPlaceholder'], $modx->toJSON($objects));
//     }
//     return;
// }

if ($sp['scripts']) {
    $ym2->loadFrontendScripts('YandexMaps2', $sp);
}

//
return $ym2->tools->getChunk($tpl, $sp);
В файле core/components/yandexmaps2/model/yandexmaps2/yandexmaps2.class.php меняем функцию getObjects
public function getObjects(array $data, $properties, $includeTVs)
    {
        foreach (array('parent', 'class') as $k) {
            if (empty($data[$k])) {
                unset($data[$k]);
            }
        }

        $objects = array();
        $q = $this->modx->newQuery('ym2Map', $data);
        $q->select(array('parent as parent, objects as objects'));
        if ($q->prepare() && $q->stmt->execute()) {
            if ($rows = $q->stmt->fetchAll(PDO::FETCH_ASSOC)) {
                $this->getTools();
                foreach ($rows as $row) {
                    //$this->modx->log(1,"getObjects ".print_r($row,1));
                    $row['objects'] = !is_array($row['objects']) ? $this->modx->fromJSON($row['objects']) : $row['objects'];
                    foreach ($row['objects'] as &$o) {
                        //$this->modx->log(1,"getObjects o ".print_r($o['properties'],1));
                        if (!empty($o['properties'])) {
                            //$this->modx->log(1,"getObjects o2 ".print_r($o['properties'],1));
                            foreach (
                                array(
                                    'iconContent',
                                    'iconCaption',
                                    'balloonContent',
                                ) as $k
                            ) {
                                if (!empty($o['properties'][$k])) {
                                    $o['properties'][$k] = $this->tools->getChunk('@INLINE:' . $o['properties'][$k], array(
                                        'data' => array_merge($data, array(
                                            'parent' => $row['parent'],
                                        )),
                                    ));
                                }
                            }
                        }else{
                            //$this->modx->log(1,"getObjects 1 ".print_r($properties,1));
                            //Добавлено это
                            foreach (
                                array(
                                    'iconContent',
                                    'iconCaption',
                                    'balloonContent',
                                ) as $k
                            ) {
                                if (!empty($properties[$k])) {
                                    $this->modx->log(1,"getObjects ".print_r($properties,1));
                                    $o['properties'][$k] = $this->modx->runSnippet('pdoResources',array(
                                                               'resources' => $row['parent'],
                                                               'tpl' => $properties[$k],
                                                               'includeTVs'=>$includeTVs,
                                                            ));
                                    //$this->modx->log(1,"getObjects o ".print_r($o['properties'],1));
                                    /*$o['properties'][$k] = $this->tools->getChunk($properties[$k], array(
                                        'data' => array_merge($data, array(
                                            'parent' => $row['parent'],
                                        )),
                                    ));*/
                                }
                            }
                        }
                    }
                    unset($o);
                    $objects = array_merge($objects, $row['objects']);
                }
            }
        }

        return $objects;
    }
}
В чанке tpl.mSearch2.row прописываем параметры балуна и tv:
<div class="mse2-row">
	[[+idx]]. <a href="[[+uri]]" class="search-link">[[+pagetitle]]</a>[[+weight]]
	[[+intro]]
</div>

<!--msearch2_weight  ([[%mse2_weight]]: [[+weight]])-->
<!--msearch2_intro <p>[[+intro]]</p>-->
<!-- 
YandexMaps2Start{'!YandexMaps2' | snippet : [
    'parent' => $id,
    'scripts' => false,
    'tpl' => '@INLINE {($objects | toJSON) | replace : "[" : "[ "}',
    'tpl.balloonContent' =>'tpl.balloonContent',
    'tpl.iconCaption' => 'tpl.iconCaption',
    'includeTVs'=>'metro',
]}YandexMaps2End
-->
Чанк tpl.balloonContent:
<p>id {$id}</p>
<p>pagetitle {$pagetitle}</p>
<p>metro [[+tv.metro]]</p>
Страница поиска пока выглядит так
Александр Туниеков
26 октября 2018, 09:05
modx.pro
17
1 879
+17
Поблагодарить автора Отправить деньги

Комментарии: 11

Spirit_Ninja
27 октября 2018, 14:13
0
Круто!
    Павел Гвоздь
    18 ноября 2018, 09:48
    +1
    Более не актуально.

    Нужно на карте выводить подпись и балун общие для всех точек
    1.1.0-beta (16.11.2018)
    ==============
    - Добавлены параметры defaultIconContent, defaultIconCaption и defaultBalloonContent в сниппет YandexMaps2

    и

    Нужно в поиске чтоб высвечивались не только точки на карте, но и были ниже карты сами результаты поиска.
    1.1.2-beta (18.11.2018)
    ==============
    - Добавлен новый метод работы с mFilter2, поддерживающий вывод результатов в текстовом виде

    Важно! Работает иначе. Как настроить, читайте в доке (отправил PR Василию, думаю скоро примет изменения в документацию).
      Александр Туниеков
      18 ноября 2018, 19:26
      +1
      Компонент дорабатывается и это хорошо :). Единственно со времени публикации статьи было еще несколько важных правок. Только времени описать их не было. Опишу на скорую руку что помню, надеюсь, это вам поможет в совершенствовании YandexMaps2.
      1. Вызов
      $o['properties'][$k] = $this->modx->runSnippet('pdoResources',array(
                                                                     'resources' => $row['parent'],
                                                                     'tpl' => $properties[$k],
                                                                     'includeTVs'=>$includeTVs,
                                                                  ));
      не очень хорошая идея. Как выяснилось на каждый объект в результатах поиска приходиться более десятка вызовов в базу данных. Что, конечно, тормозит работу сайта. Более разумное решение присоединить все что можно к основному запросу и потом вытащить из него нужные данные.

      [[!mFilter2?
      &paginator=`pdoPage`
      &ajaxMode=`button`
      &parents=`[[*id:is=`38`:then=`4`:else=`[[*id]]`]]`
      &element=`pdoResources`
      &hideContainers=`1`
      &tpl=`my.tpl.mSearch2.row`
      &includeTVs=`price_from,city,district,county,metro,room_facilities,hot_offer,region`
      &tvPrefix=``
      &class=`modResource`
      &loadModels=`ms2Gallery,yandexmaps2,citystruct,easycomm`
      &leftJoin=`{
      	"ym2Map": {
      		"class":"ym2Map",
      		"alias":"ym2Map",
      		"on": "ym2Map.parent = modResource.id AND ym2Map.class = 'modDocument'"
      	},
      	"medium1":{
      	    "class":"msResourceFile",
      	    "alias":"medium1",
      	    "on":"medium1.resource_id = modResource.id AND medium1.parent != 0 AND medium1.path LIKE '%\/small\/%' AND medium1.active = 1 AND medium1.rank = 0"
      	},
      	"ecThread": {
      		"class":"ecThread",
      		"alias":"ecThread",
      		"on": "ecThread.name = CONCAT('resource-',modResource.id)"
      	},
      	"CSDistrict": {
      		"class":"CSDistrict",
      		"alias":"CSDistrict",
      		"on": "CSDistrict.id = TVdistrict.value"
      	},
      	"CSCounty": {
      		"class":"CSCounty",
      		"alias":"CSCounty",
      		"on": "CSCounty.id = TVcounty.value"
      	},
      	"CSRegion": {
      		"class":"CSRegion",
      		"alias":"CSRegion",
      		"on": "CSRegion.id = TVregion.value"
      	}
      }`
      &select=`{
      	"modResource":"*",
      	"medium1":"medium1.url as medium,medium1.name as medium_name,medium1.alt as medium_alt",
      	"ym2Map":"objects",
      	"ecThread":"rating_simple",
      	"CSDistrict":"CSDistrict.name as district_name",
      	"CSCounty":"CSCounty.name as county_name",
      	"CSRegion":"CSRegion.name as region_name"
      }`
      &groupby=`modResource.id`
      &limit=`30`
      &filters=`
      tv|price_from:number,
      parent:categories,
      tv|city,
      tv|region,
      tv|district,
      tv|county,
      tv|metro,
      tv|room_facilities,
      `
      &aliases=`
              tv|price_from==price,
              tv|city==city,
              tv|region==region,
              tv|district==district,
              tv|county==county,
              tv|metro==metro,
              tv|room_facilities==room_facilities,
          `
      &tplFilter.outer.default=`my.tpl.mFilter2.filter.outer`
      &tplFilter.row.default=`my.tpl.mFilter2.filter.checkbox`
      
      &tplFilter.outer.price=`my.tpl.mFilter2.filter.slider`
      &tplFilter.row.price=`tpl.mFilter2.filter.number`
      
      &tplFilter.row.city=`tpl.mFilter2.filter.city`
      &tplFilter.row.region=`tpl.mFilter2.filter.region`
      &tplFilter.row.metro=`tpl.mFilter2.filter.metro`
      &tplFilter.row.district=`tpl.mFilter2.filter.district`
      &tplFilter.row.county=`tpl.mFilter2.filter.county`
      &tplOuter=`tpl.mFilter2.outer3`
      ]]
      Присоединяем
      "ym2Map": {
      		"class":"ym2Map",
      		"alias":"ym2Map",
      		"on": "ym2Map.parent = modResource.id AND ym2Map.class = 'modDocument'"
      	},
      и выбираем колонку с данными объектов карты «ym2Map»:«objects».
      Чанк my.tpl.mSearch2.row
      <div class="mse2-row">
          <!-- Блок номера -->
          <div class="block">
              <h2><a href="{$uri}">{$pagetitle}</a></h2>
              <a href="{$uri}">
              <div class="img">
                  {if $hot_offer}
                  <div class="fire">
                      <svg width="134pt" viewBox="0.031285762786865234 0 89.66130065917969 134.0590057373047" height="134pt" xmlns="http://www.w3.org/2000/svg" data-type="shape" role="img" preserveAspectRatio="xMidYMid meet" style="stroke-width: 0px;">
                          <g>
                              <path d="M23.348 134.059C8.445 84.953 39.934 67.023 39.934 67.023 37.73 93.227 52.62 113.641 52.62 113.641c5.477-1.653 15.93-9.375 15.93-9.375 0 9.375-5.516 29.78-5.516 29.78s19.309-14.929 25.387-39.726c6.07-24.797-11.563-49.691-11.563-49.691a66.494 66.494 0 0 1-16.507 48 9.514 9.514 0 0 0 1.445-2.227c2.09-4.18 5.445-15.043 3.48-40.199C62.512 14.891 30.516 0 30.516 0c2.757 21.516-5.512 26.473-24.883 67.313-19.371 40.832 17.715 66.746 17.715 66.746zm0 0"></path>
                          </g>
                      </svg>
                  </div>
                  {/if}
                  
                      <img src="{if $medium}{$medium}{else}{('assets_url' | option) ~ 'components/ms2gallery/img/web/ms2_medium.png'}{/if}" 
                      alt="{$medium_alt}" title="{$medium_name}"/>
                  
              </div>
              </a>
              <div class="right">
                  <h2>{$pagetitle}</h2>
                  {var $rating_simple_pr = $rating_simple/5*100}
                  <div class="ec-stars" title="{$rating_simple}" itemscope itemtype="http://schema.org/AggregateRating">
                      <span style="width: {$rating_simple_pr}%"></span>
                  </div>
                  {var $metroes = 'getMetroesName' | snippet : ['metro'=>$metro,'tpl'=>'getMetroesName']}
                  <ul>
                      {if $metroes}
                      <li><span>Метро:</span>
                          
                          {$metroes}
                      </li>
                      {/if}
                      {if $region && $region != 1}
                      <li><span>Область:</span> 
                          
                              <a href="{$_modx->makeUrl($_modx->resource.id,'',['region'=>$region])}">{$region_name}</a>
                          
                      </li>
                      {/if}
                      {if $county}
                      <li><span>Округ:</span> 
                          
                              <a href="{$_modx->makeUrl($_modx->resource.id,'',['county'=>$county])}">{$county_name}</a>
                          
                      </li>
                      {/if}
                      {if $district}
                      <li><span>Район:</span>
                          
                              <a href="{$_modx->makeUrl($_modx->resource.id,'',['district'=>$district])}">{$district_name}</a>
                          
                      </li>
                      {/if}
                  </ul>
                  <div class="price">{$price_from} &#8381;/сутки</div>
                  <div class="btn modal_booking_hotel" data-hotel_id="{$id}" data-hotel_pagetitle="{$pagetitle}">Бронировать</div>
              </div>
          </div>
      </div>
      
      {if count($objects) > 0}
          {foreach $objects as $k => $o}
              {var $objects.$k.properties.balloonContent = $_modx->parseChunk('tpl.balloonContent', [
                  'id' => $id,
                  'uri' => $uri,
                  'pagetitle' => $pagetitle,
                  'metroes' => $metroes,
                  'district' => $district,
                  'district_name' => $district_name,
                  'county' => $county,
                  'county_name' => $county_name,
                  'price_from' => $price_from,
                  'hot_offer' => $hot_offer,
                  'rating_simple' => $rating_simple,
                  'region' => $region,
                  'region_name' => $region_name,
              ])}
              {if $price_from}
                  {var $objects.$k.options.price_from = $price_from}
              {/if}
          {/foreach}
      <!-- 
      YandexMaps2Start{($objects | toJSON) | replace : "[" : "[ "}YandexMaps2End
      -->
      {/if}
      {if count($objects) > 0}
          {foreach $objects as $k => $o}
              {var $objects.$k.properties.balloonContent = $_modx->parseChunk('tpl.balloonContent', [
                  'id' => $id,
                  'uri' => $uri,
                  'pagetitle' => $pagetitle,
                  'metroes' => $metroes,
                  'district' => $district,
                  'district_name' => $district_name,
                  'county' => $county,
                  'county_name' => $county_name,
                  'price_from' => $price_from,
                  'hot_offer' => $hot_offer,
                  'rating_simple' => $rating_simple,
                  'region' => $region,
                  'region_name' => $region_name,
              ])}
              {if $price_from}
                  {var $objects.$k.options.price_from = $price_from}
              {/if}
          {/foreach}
      <!-- 
      YandexMaps2Start{($objects | toJSON) | replace : "[" : "[ "}YandexMaps2End
      -->
      {/if}
      дебаем балун и выводим объекты карты в результаты поиска

      Чанк tpl.mFilter2.outer3
      <div class="row msearch2" id="mse2_mfilter">
              <div id="main_image">
              {var $objects = 'YM2GetObjectsFromMF2Result' | snippet : ['results' => $results]}
              {'!YandexMaps2' | snippet : [
                  'objects' => $objects,
                  'mode' => 'mfilter2',
                  'jquery' => '0',
              ]}
              </div>
      
              <div id="title_paginate">
                <div class="container">
                    <svg height="512" width="512" viewBox="0 9.045000076293945 193.14199829101562 175.0540008544922" xmlns="http://www.w3.org/2000/svg" data-type="color" role="img" preserveAspectRatio="xMidYMid meet" style="">
                      <g>
                          <path fill="#fc4e19" d="M185.142 24.931v15.421a18.126 18.126 0 0 0-10.234-3.153h-34.887V24.931h-8v12.268H66.617v-2.371c0-9.374-7.626-17-17-17H28.266c-9.374 0-17 7.626-17 17v3.761A18.14 18.14 0 0 0 8 40.351V9.045H0v175.054h8v-16.643h124.021v16.643h8v-16.643h45.121v16.643h8V24.931h-8zm-10.235 20.267c5.643 0 10.234 4.591 10.234 10.234v2.258H140.02V45.198h34.887zm-42.886 33.05v48.157H66.617v-2.372c0-9.374-7.626-17-17-17H28.266c-9.374 0-17 7.626-17 17v3.762A18.208 18.208 0 0 0 8 129.557v-51.31h124.021zM18.234 134.405H132.02v12.491H8v-2.258c0-5.642 4.591-10.233 10.234-10.233zm121.787 0h34.887c2.581 0 4.936.968 6.738 2.551h-41.625v-2.551zm0 12.491v-1.94h45.121v1.94h-45.121zm34.886-20.491H140.02v-8.129h45.121v11.282a18.126 18.126 0 0 0-10.234-3.153zm10.235-16.129h-45.121V91.598h45.121v18.678zm0-26.678h-45.121v-5.35h45.121v5.35zm-166.908-38.4H132.02V57.69H8v-2.258c0-5.643 4.591-10.234 10.234-10.234z" data-color="1"></path>
                      </g>
                    </svg>
                    <div class="paginate">
                        <h1 class="zg">[[*pagetitle]]</h1>
                        [[!pdoMenu?
                          &parents=`4`
                          &resources=`-[[*id]]`
                          &level=`1`
                          &tpl=`mFilter2.tpl.menu`
                          ]]
                    </div>
                  </div>
              </div>
      
              <div class="container">
                  <div id="sort">
                      <div class="left">
                          <div id="mse2_sort" class="span5 col-md-5">
              				<a href="#" data-sort="price" data-dir="[[+mse2_sort:is=`price:desc`:then=`desc`]]" data-default="desc" class="sort">ПО ЦЕНЕ <span></span></a>
              				<a href="#" data-sort="rating_simple" data-dir="[[+mse2_sort:is=`rating_simple:desc`:then=`desc`]]" data-default="desc" class="sort">ПО РЕЙТИНГУ <span></span></a>
              			</div>
                      </div>
                      <div class="right">
                          <a class="bloch active" href="#">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 19" id="view-grid" width="100%" height="100%"><path d="M0 0h8v8H0zm11 0h8v8h-8zm0 11h8v8h-8zM0 11h8v8H0z"></path></svg>
                          </a>
                          <a class="liners" href="#">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 19" id="view-list" width="100%" height="100%"><path d="M0 0h8v8H0zm11 0h14.004v8H11zm0 11h14.004v8H11zM0 11h8v8H0z"></path></svg>
                          </a>
                      </div>
                  </div>
      
                  <div class="main_aside">
                      <main>
                          <div id="list_of_places">
                              <div id="mse2_results">
                      			[[+results]]
                      		</div>
                      		<div class="mse2_pagination">
                      			[[!+page.nav]]
                      		</div>
                          </div>
                      </main>
                      <aside>
                          <div id="filter">
                              <div class="top">
                                  <span>ФИЛЬТР ПОИСКА</span>
      
                                  <svg xmlns="http://www.w3.org/2000/svg" width="512pt" viewBox="0 0 512 511.9999694824219" height="512pt" data-type="shape" role="img" preserveAspectRatio="xMidYMid meet" style="stroke-width: 0px;">
                                    <g>
                                        <path d="M302 90c-66.184 0-120 53.832-120 120s53.816 120 120 120 120-53.832 120-120S368.184 90 302 90zm70.605 100.605l-75 75c-2.93 2.93-6.77 4.395-10.605 4.395s-7.676-1.465-10.605-4.395l-45-45c-5.86-5.859-5.86-15.351 0-21.21s15.351-5.86 21.21 0L287 233.789l64.395-64.394c5.859-5.86 15.351-5.86 21.21 0s5.86 15.351 0 21.21zm0 0"></path>
                                        <path d="M4.395 443.973c-5.86 5.859-5.86 15.351 0 21.21l42.421 42.422c2.93 2.93 6.77 4.395 10.606 4.395s7.676-1.465 10.605-4.395l75.184-75.183-63.633-63.633zm0 0"></path>
                                        <path d="M302 0C186.219 0 92 94.203 92 210c0 41.637 12.328 80.375 33.313 113.055l-24.524 24.523 63.633 63.633 24.523-24.524C221.625 407.673 260.367 420 302 420c115.781 0 210-94.203 210-210S417.781 0 302 0zm0 360c-82.707 0-150-67.297-150-150S219.293 60 302 60c82.703 0 150 67.297 150 150s-67.297 150-150 150zm0 0"></path>
                                    </g>
                                  </svg>
                              </div>
                              <div class="bottom">
                                  <form action="[[~[[*id]]]]" method="post" id="mse2_filters">
                          			[[+filters]]
                          			<button type="reset" class="reset" class="btn btn-default hidden">Сбросить</button>
                          		</form>
                                  
                              </div>
                          </div>
                      </aside>
                  </div>
              </div>
      </div>
      {var $objects = 'YM2GetObjectsFromMF2Result' | snippet : ['results' => $results]}
              {'!YandexMaps2' | snippet : [
                  'objects' => $objects,
                  'mode' => 'mfilter2',
                  'jquery' => '0',
              ]}
      Вытаскиваем из результатов поиска и выводим на карту.
      Скрипт assets/components/yandexmaps2/js/web/default.js
      (function () {
          function YandexMaps2(options) {
              //
              ['map'].forEach(function (val, i, arr) {
                  if (typeof(options[val]) == 'undefined' || options[val] == '') {
                      console.error('[YandexMaps2] Bad config.', arr);
                      return;
                  }
              });
      
              //
              var self = this;
              self['initialized'] = false;
              self['running'] = false;
      
              /**
               * Инициализирует класс.
               * @returns {boolean}
               */
              self.Initialize = function (options) {
                  if (!self['initialized']) {
                      //
                      self['config'] = {
                          map: 'ym2map',
                          center: [55.72, 37.64],
                          zoom: 10,
                      };
                      self['classes'] = {};
                      self['selectors'] = {};
      
                      //
                      Object.keys(options).forEach(function (key) {
                          if (['selectors'].indexOf(key) !== -1) {
                              return;
                          }
                          self.config[key] = options[key];
                      });
                      ['selectors'].forEach(function (key) {
                          if (options[key]) {
                              Object.keys(options[key]).forEach(function (i) {
                                  self.selectors[i] = options.selectors[i];
                              });
                          }
                      });
                  }
                  self['initialized'] = true;
      
                  return self['initialized'];
              };
      
              /**
               * Запускает основные действия.
               * @returns {boolean}
               */
              self.Run = function () {
                  if (self['initialized'] && !self['running']) {
                      ymaps.ready(self.Map['initialize']);
                      // console.log('self.Run self', self);
      
                      if (self.config['mode'] == 'mfilter2' || self.config['mode'] == 'mfilter') {
                          $(document).on('mse2_load', function (e, response) {
                              if (response['success']) {
                                   //console.log('YandexMaps2 mse2_load response', response);
                                  var s = response.data['results'];
                                  var results1 = s.match(/(?=YandexMaps2Start)(.*?)(?=YandexMaps2End)/g);
      
                                  //console.log(s);
                                  //console.log(results1);
                                  if(results1 === null){
                                      results1 ="";
                                  }else{
                                      results1 = results1.join();
                                  } 
                                  var results = JSON.parse('[' + results1 + ']');
                                  if (typeof(results) == 'object') {
                                      var objects = [];
                                      for (var a in results) {
                                          if (!results[a]['length']) {
                                              continue;
                                          }
                                          for (var b in results[a]) {
                                              objects.push(results[a][b]);
                                          }
                                      }
                                      self.Map.clean(function () {
                                          self.Map.addObjects(objects);
                                      });
                                  }
                              }
                          });
                      }
                  }
                  self['running'] = true;
      
                  return self['running'];
              };
      
              /**
               * Колбеки
               */
              self.Callbacks = {
                  onLoad: function () {
                      var self = this;
                      // console.log('JS onLoad self', self);
      
                      var objects = ('objects' in self['config']) ? self.config['objects'] : [];
                      // console.log('JS onLoad objects', objects);
      
                      objects.forEach(function (o) {
                          self.Map.addObject(o);
                      });
                      self['map'].setBounds(self.map['geoObjects'].getBounds(), {checkZoomRange:true}).then(function(){ if(self['map'].getZoom() > 10) self['map'].setZoom(10);});
                  },
              };
      
              /**
               * Действия с картой
               */
              self.Map = {
                  /**
                   * Инициализация карты
                   */
                  initialize: function () {
                      self['map'] = new ymaps.Map(self.config['map'], {
                          center: self.config['center'],
                          zoom: self.config['zoom'],
                      }, {
                          searchControlProvider: 'yandex#search',
                      });
      
                      self.Callbacks.onLoad.bind(self)();
      
                      // Отключаем зуммирование при скролле
                      if (!self.config['scrollZoom']) {
                          self['map'].behaviors.disable('scrollZoom');
                      }
                  },
      
                  /**
                   * Добавляем на карту объекты
                   */
                  addObjects: function (objects) {
                      objects.forEach(function (o) {
                          self.Map.addObject(o);
                      });
                      self['map'].setBounds(self.map['geoObjects'].getBounds(), {checkZoomRange:true}).then(function(){ if(self['map'].getZoom() > 10) self['map'].setZoom(10);});
                  },
      
                  /**
                   * Добавляем на карту объект (ломаная линия, многоугольник, круг, точка)
                   */
                  addObject: function (arg) {
                      var o;
                      var type = arg['type'];
                      var geometry = arg['geometry'];
                      var options = $.extend({}, arg['options']);
                      var properties = $.extend({}, arg['properties']);
                      // console.log('properties', properties);
      
                      //
                      switch (type) {
                          case 'placemark':
                              if(options.price_from  !== undefined){
                                  if(options.price_from  !== 0){
                                      var iconColor = '#ef790f';
      					            var iconImageHref = '/assets/templates/img/placemark.svg';
      					            var placemarkLayout = ymaps.templateLayoutFactory.createClass(
      					                '<div class="marker-layout"><div class="marker-layout__text">от '+
      					                options.price_from +' &#8381;</div></div>');
      					           options = {
                  						iconLayout: placemarkLayout,
                  						iconShape: {
                  							type: 'Rectangle',
                  							coordinates: [
                  								[-40, -40], [40, -5]
                  							]
                  						}
                  					};
                                  }
                              }
                              //console.log(options);
                              o = new ymaps.Placemark(geometry, properties, options);
                              break;
      
                          case 'polyline':
                              o = new ymaps.Polyline(geometry, properties, options);
                              break;
      
                          case 'polygon':
                              o = new ymaps.Polygon(geometry, properties, options);
                              break;
      
                          case 'circle':
                              o = new ymaps.Circle(geometry, properties, options);
                              break;
                      }
      
                      // Добавляем на карту и в массив
                      self.map['geoObjects'].add(o);
      
                      return o;
                  },
      
                  /**
                   * Очищает карту.
                   */
                  clean: function (callback) {
                      // var tmp = [];
                      // self.map['geoObjects'].each(function (o) {
                      //     tmp.push(o);
                      // });
                      // tmp.forEach(function (o) {
                      //     self.map['geoObjects'].remove(o);
                      // });
                      // delete tmp;
                      self.map['geoObjects'].removeAll();
      
                      callback.bind(self)();
                  }
              };
      
              /**
               * Сообщения.
               * @type {object}
               */
              self.Message = {
                  success: function (message) {
                  },
                  error: function (message) {
                      alert(message);
                  }
              };
      
              /**
               * Инструменты.
               * @type {object}
               */
              self.Tools = {};
      
              /**
               * Initialize && Run!
               */
              if (self.Initialize(options)) {
                  self.Run();
              }
          }
      
          window.YandexMaps2 = YandexMaps2;
      })();
      Изменилась регулярка чтоб она работала в safary
      var results1 = s.match(/(?=YandexMaps2Start)(.*?)(?=YandexMaps2End)/g);

      Маштабировать и центрировать карту чтобы все объекты на нее влезали
      self['map'].setBounds(self.map['geoObjects'].getBounds(), {checkZoomRange:true}).then(function(){ if(self['map'].getZoom() > 10) self['map'].setZoom(10);});
      И свой тип метки на карте
      if(options.price_from  !== undefined){
                                  if(options.price_from  !== 0){
                                      var iconColor = '#ef790f';
      					            var iconImageHref = '/assets/templates/img/placemark.svg';
      					            var placemarkLayout = ymaps.templateLayoutFactory.createClass(
      					                '<div class="marker-layout"><div class="marker-layout__text">от '+
      					                options.price_from +' &#8381;</div></div>');
      					           options = {
                  						iconLayout: placemarkLayout,
                  						iconShape: {
                  							type: 'Rectangle',
                  							coordinates: [
                  								[-40, -40], [40, -5]
                  							]
                  						}
                  					};
                                  }
                              }
      Еще чтобы работал поиск как надо надо поправить ошибки в pdoTools
      Файл core/components/pdotools/model/pdotools/pdofetch.class.php
      Функция addSelects строка примерно 367:
      if ($i == 0 && $this->config['setTotal']) {
                          if (is_string($fields)){
                              $fields = 'SQL_CALC_FOUND_ROWS ' . $fields;
                          }else{
                              $fields = 'SQL_CALC_FOUND_ROWS ' . implode(",",$fields);
                          }
                      }
      $fields = 'SQL_CALC_FOUND_ROWS '. implode(",",$fields); это чтобы в запрос не попадало SQL_CALC_FOUND_ROWS Arroy

      Функция addJoins строка примерно 304
      $tmp = array_merge($this->config['tvsJoin'], $tmp);
      Здесь поменял порядок присоединения таблиц чтоб tv присоединялись раньше чем все остальное. Иначе mySQL ругается на CSDistrict.id = TVdistrict.value. TVdistrict присоединяется позже и типо ему оно не известно. Надо чтоб лефтджоин с TVdistrict был раньше уже сделан.

      Ну вот что помню :). Надеюсь это поможет вам и остальным разработчикам :)
        Павел Гвоздь
        18 ноября 2018, 19:44
        0
        А я не использовал ваш код, поэтому эта дикая простыня мне ни к чему)
          Александр Туниеков
          18 ноября 2018, 20:05
          +2
          Ну вот стараешься вспоминаешь пишешь а в ответ «эта дикая простыня мне ни к чему» :). Я не видел вашего нового кода. Да и, думаю, не скоро увижу, но не факт что не придется снова с напильником пилить :). Я другого способа уменьшить число запросов в базу не знаю кроме как присоединять в основной запрос. Ну нужная заказчику задача сделана, а придется ли мне использовать ваш компонент и нужно ли будет его пилить поживем увидим. Успехов Вам :)
            Павел Гвоздь
            19 ноября 2018, 07:38
            +1
            Если кто-то не догнал, ибо минусуют:
            Я не вижу смысла использовать не свой код, когда я могу написать свой, правильнее и изящнее, на мой взгляд. Без лишних сниппетов, которые совершенно ни к чему, даже в решении Александра, ибо можно обойтись Феномом.

            Я благодарен Александру за этот пост, было интересно почитать, что требуется людям и как они это решают. Отсюда и было принято решение добавить возможность кастомизации в компонент:
            1.1.0-beta (16.11.2018)
            ==============
            - Добавлено событие плагина ymOnLoadObjects с параметрами: array $data, array $objects, array $snippetProperties
            В документации уже есть инфа об этом.

            Принижать вашей работы и вложенных сил я не собирался, если так кто подумал! Однако и «вспоминать и писать ответ» я ведь тоже не просил. Тем более такую простыню в комментарий, в то время, когда есть возможность отредактировать пост. Для меня, перфекциониста, это, как ночной кошмар. :)
              Александр Туниеков
              19 ноября 2018, 09:38
              0
              Посмотрел новую версию вашего компонента. Мне вот одно не понятно. Как в defaultBalloonContent попадают данные ресурса tv pagetitle и т.д. и попадают ли они вообще туда? По моему туда только parent( id ресурса) попадает. Без данных ресурса смысла в defaultBalloonContent никакого.
                Павел Гвоздь
                19 ноября 2018, 10:23
                0
                У нас есть переменная $data, которую мы можем использовать, как душе угодно:
                'defaultBalloonContent' => '{$data | print}',
                Она содержит данные карты данного ресурса, а точнее такого типа информацию:
                Array(
                    parent => (int)
                    class => (string)
                    list => (string)
                )

                При помощи Fenom мы можем вытащить любое поле ресурса, относящегося к данному объекту карты.
                  Александр Туниеков
                  19 ноября 2018, 10:39
                  0
                  Ну вот я и говорю что есть только id ресурса (parent). Понятно что с помощью fenom можно в defaultBalloonContent воткнуть дополнительный сниппет и в нем все поля вытащить. Но все равно лишние запросы в базу. Метод в простыне :) лучше :) можно без доп запросов в базу обойтись :). Только вы компонент изменили и переработать немного нужно метод. Мне сейчас некогда да и особо незачем.
                    Павел Гвоздь
                    19 ноября 2018, 10:45
                    0
                    А зачем мне делать запросы к базе, если человеку не нужны будут default поля? Или ему не надо получать инфу о ресурсе? Или он вообще карту привязывает не к ресурсу? Поймите, мой компонент должен быть универсальным. Вам надо одно, другому — другое. Хотите кастома — пишите плагин.

                    К тому-же, лошадиный запрос с кучей джоинов не даёт гарантии, что сайт будет быстрее работать. У меня был запрос с кучей джоинов, который я разбил на несколько и они отрабатывают быстрее. Удивительно, не правда ли? Так что, джоины не панацея!
                      Александр Туниеков
                      19 ноября 2018, 11:38
                      -1
                      Не знаю не тестировал. По моему все таки 1 запрос быстрее 300 запросов(30 объектов, 10 запросов на каждый).
      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
      11