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

К счастью, нужный функционал оказалось не сложно допилить напильником. Под катом описание.
1. Нужно в поиске чтоб высвечивались не только точки на карте, но и были ниже карты сами результаты поиска.
В Работа с mFilter2 на шаге 3 меняют чанк tpl.mSearch2.row
И в ява-скрипте assets/components/yandexmaps2/js/web/default.js в строке
И в строке
и ниже эти метки добавляются на карту.
Решение
Меняем чанк tpl.mSearch2.row так
Меняем в ява-скрипте assets/components/yandexmaps2/js/web/default.js с 61 строки до if (typeof(results) == 'object') { на следующий код
Нужно на карте выводить подпись и балун общие для всех точек
Решение
Меняем сниппет 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 меняем функцию getObjectspublic 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>Страница поиска пока выглядит так
Комментарии: 11
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
и
Важно! Работает иначе. Как настроить, читайте в доке (отправил PR Василию, думаю скоро примет изменения в документацию).
1. Вызов
не очень хорошая идея. Как выяснилось на каждый объект в результатах поиска приходиться более десятка вызовов в базу данных. Что, конечно, тормозит работу сайта. Более разумное решение присоединить все что можно к основному запросу и потом вытащить из него нужные данные.
Присоединяем
и выбираем колонку с данными объектов карты «ym2Map»:«objects».
Чанк my.tpl.mSearch2.row
дебаем балун и выводим объекты карты в результаты поиска
Чанк tpl.mFilter2.outer3
Вытаскиваем из результатов поиска и выводим на карту.
Скрипт assets/components/yandexmaps2/js/web/default.js
Изменилась регулярка чтоб она работала в safary
Маштабировать и центрировать карту чтобы все объекты на нее влезали
И свой тип метки на карте
Еще чтобы работал поиск как надо надо поправить ошибки в pdoTools
Файл core/components/pdotools/model/pdotools/pdofetch.class.php
Функция addSelects строка примерно 367:
$fields = 'SQL_CALC_FOUND_ROWS '. implode(",",$fields); это чтобы в запрос не попадало SQL_CALC_FOUND_ROWS Arroy
Функция addJoins строка примерно 304
Здесь поменял порядок присоединения таблиц чтоб tv присоединялись раньше чем все остальное. Иначе mySQL ругается на CSDistrict.id = TVdistrict.value. TVdistrict присоединяется позже и типо ему оно не известно. Надо чтоб лефтджоин с TVdistrict был раньше уже сделан.
Ну вот что помню :). Надеюсь это поможет вам и остальным разработчикам :)
Я не вижу смысла использовать не свой код, когда я могу написать свой, правильнее и изящнее, на мой взгляд. Без лишних сниппетов, которые совершенно ни к чему, даже в решении Александра, ибо можно обойтись Феномом.
Я благодарен Александру за этот пост, было интересно почитать, что требуется людям и как они это решают. Отсюда и было принято решение добавить возможность кастомизации в компонент:
В документации уже есть инфа об этом.
Принижать вашей работы и вложенных сил я не собирался, если так кто подумал! Однако и «вспоминать и писать ответ» я ведь тоже не просил. Тем более такую простыню в комментарий, в то время, когда есть возможность отредактировать пост. Для меня, перфекциониста, это, как ночной кошмар. :)
Она содержит данные карты данного ресурса, а точнее такого типа информацию:
При помощи Fenom мы можем вытащить любое поле ресурса, относящегося к данному объекту карты.
К тому-же, лошадиный запрос с кучей джоинов не даёт гарантии, что сайт будет быстрее работать. У меня был запрос с кучей джоинов, который я разбил на несколько и они отрабатывают быстрее. Удивительно, не правда ли? Так что, джоины не панацея!