
Если вы спрашиваете: «Что такое Yii?», Ознакомьтесь с разделом Введение в Yii Framework , в котором рассматриваются преимущества Yii и содержится обзор Yii 2.0, выпущенного в октябре 2014 года.
В этой серии «Программирование с Yii2» я расскажу читателям, как использовать Yii2 Framework для PHP. В этом уроке мы рассмотрим реализацию интерактивных страниц с использованием Ajax. В частности, я собираюсь осветить использование Ajax в двух областях приложения Meeting Planner, о котором параллельно пишу серию Building Your Startup .
Сначала мы рассмотрим, как мы загружаем карту Google на страницу в ответ на вход пользователя в определенное место. Как показано ниже, после того, как я вхожу в Plum Bistro и нажимаю return, карта справа загружается динамически без обновления страницы.

Во-вторых, я покажу вам, как мы записываем изменения, которые пользователь вносит в собрание на этапе планирования. Планировщик собраний позволяет участникам легко определить свои предпочтительные места и время, а затем выбрать окончательный вариант.

Ajax значительно упрощает и ускоряет процесс, позволяя пользователям перемещать несколько элементов управления переключателями, чтобы указать свои предпочтения без обновления страницы.
Просто напоминание, я участвую в комментариях ниже. Мне особенно интересно, если у вас есть другие подходы, дополнительные идеи или вы хотите предложить темы для будущих уроков. Если у вас есть вопрос или предложение по теме, пожалуйста, напишите ниже. Вы также можете связаться со мной через Twitter @reifman напрямую.
Использование Ajax с Yii

Если вы только начинаете с Ajax и хотите начать медленно, на Yii Playground есть два простых примера Ajax, которые могут быть полезны для обзора. Один изменяет текст на странице через Ajax, а другой загружает ответ на форму на той же странице, причем оба без обновления, и каждый включает подробные примеры кода.

Давайте углубимся в два наших основных примера. Все источники этих примеров можно найти в репозитории кода Планировщика собраний на GitHub .
Интерактивное отображение карт Google
Создание формы ввода
Когда форма « Создать место» (/frontend/views/place/create_place_google.php) изначально загружается, она включает виджет поиска Google Places в реальном времени:

Интеграция API Google Places JavaScript
Форма загружает библиотеку JavaScript Карт Google и связывает ее с полем ввода place-searchbox:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
$gpJsLink= ‘https://maps.googleapis.com/maps/api/js?’
‘key’ => Yii::$app->params[‘google_maps_key’],
‘libraries’ => ‘places’,
));
echo $this->registerJsFile($gpJsLink);
$options = ‘{«types»:[«establishment»],»componentRestrictions»:{«country»:»us»}}’;
echo $this->registerJs(«(function(){
var input = document.getElementById(‘place-searchbox’);
var options = $options;
searchbox = new google.maps.places.Autocomplete(input, options);
setupListeners(‘place’);
})();» , \yii\web\View::POS_END );
|
Частичная форма _formPlaceGoogle.php включает в себя некоторые скрытые поля, в которых результаты карты могут быть сохранены до отправки всей страницы, а также скрытый div для отображения карты через Ajax.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
use frontend\assets\MapAsset;
MapAsset::register($this);
…
<?= BaseHtml::activeHiddenInput($model, ‘name’);
<?= BaseHtml::activeHiddenInput($model, ‘google_place_id’);
<?= BaseHtml::activeHiddenInput($model, ‘location’);
<?= BaseHtml::activeHiddenInput($model, ‘website’);
<?= BaseHtml::activeHiddenInput($model, ‘vicinity’);
<?= BaseHtml::activeHiddenInput($model, ‘full_address’);
…
<div class=»col-md-6″>
<article></article>
</div> <!— end col2 —>
|
В таблице Place of Planner Meeting хранятся имя Google, place_id, местоположение, веб-сайт, окрестности и полный адрес для использования в приложении.
MapAsset, включенный выше, загружает наш файл create_place.js, который работает между Google и нашей формой; он в основном управляет передачей и ответом данных через Ajax.
Наш Ajax, управляющий JavaScript
Я проведу вас через create_place.js по частям. Во-первых, есть setupListeners() , вызываемый родительской формой:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
function setupListeners(model) {
// searchbox is the var for the google places object created on the page
google.maps.event.addListener(searchbox, ‘place_changed’, function() {
var place = searchbox.getPlace();
if (!place.geometry) {
// Inform the user that a place was not found and return.
return;
} else {
// migrates JSON data from Google to hidden form fields
populateResult(place,model);
}
});
var place_input = document.getElementById(model+’-searchbox’);
google.maps.event.addDomListener(place_input, ‘keydown’, function(e) {
if (e.keyCode == 13) {
e.preventDefault();
}
});
}
|
Когда пользователь начинает печатать, виджет опускает опции ввода текста для реальных мест, и событие place_changed обрабатывается при каждом нажатии клавиши. Слушатель keydown выше не дает ключу возврата (ASCII 13 или 0xD для вас, гиков), отправлять форму.
Вот как это выглядит при вводе. Я вхожу в Plum для Plum Bistro:

Сбор итоговой карты и ее данных
Если человек выбрал ввод или щелкнул место в раскрывающемся списке, вызывается populateResult() ; если нет, мы ничего не делаем.
|
01
02
03
04
05
06
07
08
09
10
11
|
function populateResult(place,model) {
// moves JSON data retrieve from Google to hidden form fields
// so Yii2 can post the data
$(‘#’+model+’-location’).val(JSON.stringify(place[‘geometry’][‘location’]));
$(‘#’+model+’-google_place_id’).val(place[‘place_id’]);
$(‘#’+model+’-full_address’).val(place[‘formatted_address’]);
$(‘#’+model+’-website’).val(place[‘website’]);
$(‘#’+model+’-vicinity’).val(place[‘vicinity’]);
$(‘#’+model+’-name’).val(place[‘name’]);
loadMap(place[‘geometry’][‘location’],place[‘name’]);
}
|
Это заполняет все скрытые поля данными из Google и вызывает loadMap() для отображения карты:

Функция loadMap() очень специфична для API Google Place и отображает карту, loadMap() выше справа:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
function loadMap(gps,name) {
var gps_parse = gps.toString().replace(«(«, «»).replace(«)», «»).split(«, «);
var gps_lat = parseFloat(gps_parse[0]);
var gps_lng = parseFloat(gps_parse[1]);
if (document.querySelector(‘article’).children.length==0) {
var mapcanvas = document.createElement(‘div’);
mapcanvas.id = ‘mapcanvas’;
mapcanvas.style.height = ‘300px’;
mapcanvas.style.width = ‘300px’;
mapcanvas.style.border = ‘1px solid black’;
document.querySelector(‘article’).appendChild(mapcanvas);
}
var latlng = new google.maps.LatLng(gps_lat,gps_lng);
var myOptions = {
zoom: 16,
center: latlng,
mapTypeControl: false,
navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL},
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById(«mapcanvas»), myOptions);
var marker = new google.maps.Marker({
position: latlng,
map: map,
title:name
});
}
|
Пользовательский опыт быстрый и впечатляющий. Попробуй это !
Динамическая запись изменений собрания
Далее, давайте посмотрим, как мы записываем изменения в планы встреч в режиме реального времени. Здесь нет Google API; это больше ванильный AJAX в Yii Framework.
Когда люди добавляют даты, время и места в свои планы встреч, вы увидите страницу, подобную этой:

Столбцы « Вы» и « Они» показывают предпочтения каждого участника в отношении мест и времени. Увеличенный слайдер выбора позволяет человеку принять окончательное решение о месте и времени встречи.
Есть много данных, которые нужно собрать от людей, и мы не хотим требовать обновления страницы при каждом изменении. Ajax является идеальным решением для этой проблемы.
Я пройдусь по коду для панели «Место встречи» выше. Панель Meeting-Time выше работает аналогично.
Следуя Кодексу
Из-за инфраструктуры MVC и моего желания повторно использовать партиалы кода, поток здесь может показаться сложным для отслеживания. Вспомогательные функции PHP и JavaScript иногда приходилось размещать в родительских файлах, а не в частностях, с которыми они были наиболее тесно связаны. Я постараюсь дать вам обзор в первую очередь. Я призываю вас сделать несколько проходов, читая это, чтобы полностью понять это. И снова, вы можете просматривать код через GitHub .
Подсказка: имейте в виду, что имена файлов для частичных имен обычно начинаются с подчеркивания.
- Страница планировщика собраний загружается по адресу /frontend/views/meeting/view.php. Этот файл также содержит вспомогательные функции JavaScript для управления состоянием кнопок, таких как « Отправить» и « Завершить» (т. Е. Может ли пользователь отправить это приглашение после этого изменения? С помощью Планировщика собраний, как правило, необходимо выбрать одно место и один раз, прежде чем его можно будет отправить). ) и отображение визуальных уведомлений о том, что изменения будут отправлены по электронной почте другим участникам, когда пользователь закончит.
- При отображении панели « Где» для мест загружается файл /frontend/views/meeting-place/_panel.php. Этот файл включает вспомогательные PHP-функции
showOwnerStatus()иshowParticipantStatus(), которые будут повторно использоваться его дочерним элементом _list.php. Но, самое главное, _panel.php включает методы JavaScript для событияswitchChangeползункаswitchChange. - Файл _panel.php использует _list.php для отображения каждой отдельной строки для каждого места. Этот файл будет отображать ползунки Bootstrap путем вызова функций
showOwnerStatus()иshowParticipantStatus(). - Функции
switchChangeбудутswitchChangeAjax вызовы MeetingPlaceChoiceController.php. - И, наконец, MeetingPlaceChoiceController.php вызывает модель MeetingPlaceChoice.php для записи изменений в базе данных.
Извините, размещение соответствующего кода сложное и разложенное.
Теперь я пошагово проведу вас по ключевым компонентам.
Код Ajax шаг за шагом
Вот Meeting / view.php рендеринг Meeting-Place / _panel.php. Здесь отображается частичное для строк возможных мест и выбора участников:
|
01
02
03
04
05
06
07
08
09
10
11
|
<?php
// where
if (!($model->meeting_type == \frontend\models\Meeting::TYPE_PHONE || $model->meeting_type == \frontend\models\Meeting::TYPE_VIDEO)) {
echo $this->render(‘../meeting-place/_panel’, [
‘model’=>$model,
‘placeProvider’ => $placeProvider,
‘isOwner’ => $isOwner,
‘viewer’ => $viewer,
]);
}
?>
|
Ниже приведен JavaScript, относящийся к действиям, которые отвечают на результаты Ajax, но не нужны непосредственно для Ajax. Вам не нужно понимать, что делают эти функции, чтобы понять этот пример Ajax, но я включил их, поскольку они вызываются в ответ на события Ajax.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
<?php
$script = <<< JS
var notifierOkay;
if ($(‘#notifierOkay’).val() == ‘on’) {
notifierOkay = true;
} else {
notifierOkay = false;
}
function displayNotifier(mode) {
if (notifierOkay) {
if (mode == ‘time’) {
$(‘#notifierTime’).show();
} else if (mode == ‘place’) {
$(‘#notifierPlace’).show();
} else {
alert(«We\’ll automatically notify the organizer when you’re done making changes.»);
}
notifierOkay=false;
}
}
function refreshSend() {
$.ajax({
url: ‘$urlPrefix/meeting/cansend’,
data: {id: $model->id, ‘viewer_id’: $viewer},
success: function(data) {
if (data)
$(‘#actionSend’).removeClass(«disabled»);
else
$(‘#actionSend’).addClass(«disabled»);
return true;
}
});
}
function refreshFinalize() {
$.ajax({
url: ‘$urlPrefix/meeting/canfinalize’,
data: {id: $model->id, ‘viewer_id’: $viewer},
success: function(data) {
if (data)
$(‘#actionFinalize’).removeClass(«disabled»);
else
$(‘#actionFinalize’).addClass(«disabled»);
return true;
}
});
}
JS;
$position = \yii\web\View::POS_READY;
$this->registerJs($script, $position);
?>
|
Здесь, в Meeting-Place / _panel.php, создается таблица с указанием мест и выборов, вызывающая _list.php:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<table class=»table»>
<thead>
<tr class=»small-header»>
<td></td>
<td ><?=Yii::t(‘frontend’,’You’) ?></td>
<td ><?=Yii::t(‘frontend’,’Them’) ?></td>
<td >
<?php
if ($placeProvider->count>1 && ($isOwner || $model->meetingSettings[‘participant_choose_place’])) echo Yii::t(‘frontend’,’Choose’);
?></td>
</tr>
</thead>
<?= ListView::widget([
‘dataProvider’ => $placeProvider,
‘itemOptions’ => [‘class’ => ‘item’],
‘layout’ => ‘{items}’,
‘itemView’ => ‘_list’,
‘viewParams’ => [‘placeCount’=>$placeProvider->count,’isOwner’=>$isOwner,’participant_choose_place’=>$model->meetingSettings[‘participant_choose_place’]],
]) ?>
</table>
<?php else: ?>
<?php endif;
|
Что еще более важно, он также включает в себя приведенный ниже JavaScript, который мы используем для выполнения вызовов Ajax, когда пользователь перемещает переключатель, изменяя его состояние. Функции выбора соответствуют большему синему ползунку выбора, в то время как функции выбора соответствуют ползункам предпочтения.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
$script = <<< JS
placeCount = $placeProvider->count;
// allows user to set the final place
$(‘input[name=»place-chooser»]’).on(‘switchChange.bootstrapSwitch’, function(e, s) {
// console.log(e.target.value);
// turn on mpc for user
$.ajax({
url: ‘$urlPrefix/meeting-place/choose’,
data: {id: $model->id, ‘val’: e.target.value},
// e.target.value is selected MeetingPlaceChoice model
success: function(data) {
displayNotifier(‘place’);
refreshSend();
refreshFinalize();
return true;
}
});
});
// users can say if a place is an option for them
$(‘input[name=»meeting-place-choice»]’).on(‘switchChange.bootstrapSwitch’, function(e, s) {
//console.log(e.target.id,s);
// set intval to pass via AJAX from boolean state
if (s)
state = 1;
else
state =0;
$.ajax({
url: ‘$urlPrefix/meeting-place-choice/set’,
data: {id: e.target.id, ‘state’: state},
success: function(data) {
displayNotifier(‘place’);
refreshSend();
refreshFinalize();
return true;
}
});
});
JS;
$position = \yii\web\View::POS_READY;
$this->registerJs($script, $position);
?>
|
Функции выше делают вызов actionSet() в MeetingPlaceChoiceController для ответа на изменение коммутатора с помощью Ajax-запросов:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
public function actionSet($id,$state)
{
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
// caution — incoming AJAX type issues with val
$id=str_replace(‘mpc-‘,»,$id);
//if (Yii::$app->user->getId()!=$mpc->user_id) return false;
if (intval($state) == 0 or $state==’false’)
$status = MeetingPlaceChoice::STATUS_NO;
else
$status = MeetingPlaceChoice::STATUS_YES;
//$mpc->save();
MeetingPlaceChoice::set($id,$status,Yii::$app->user->getId());
return $id;
}
|
Действия контроллера, которые отвечают через Ajax, должны иметь формат ответа JSON (таким образом, Yii знает, что они не предназначены для доставки HTML):
|
1
|
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
|
Вот метод MeetingPlaceChoice::set() , который записывает действия пользователя в базе данных и создает запись MeetingLog, которая отслеживает все изменения во время планирования.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
public static function set($id,$status,$user_id = 0,$bulkMode=false)
{
$mpc = MeetingPlaceChoice::findOne($id);
if ($mpc->user_id==$user_id) {
$mpc->status = $status;
$mpc->save();
if (!$bulkMode) {
// log only when not in bulk mode ie accept all
// see setAll for more details
if ($status==MeetingPlaceChoice::STATUS_YES) {
$command = MeetingLog::ACTION_ACCEPT_PLACE;
} else {
$command = MeetingLog::ACTION_REJECT_PLACE;
}
MeetingLog::add($mpc->meetingPlace->meeting_id,$command,$mpc->user_id,$mpc->meeting_place_id);
}
return $mpc->id;
} else {
return false;
}
}
|
Особенности, связанные с изменениями собрания
В Meeting Planner я веду журнал каждого отдельного изменения. Это позволяет мне узнать, когда прошло несколько минут с момента последнего изменения человека, и уведомить других участников собрания. Я пытаюсь провести эксперимент с этим сервисом, вместо того, чтобы требовать, чтобы участники нажимали кнопку «Отправить» каждый раз, когда они хотят внести изменения.
Тем не менее, это требует обучения их, чтобы понять, что все в порядке, чтобы изменить его и оставить его, то есть закрыть окно браузера. Таким образом, функции displayNotifier() отображают некоторые флеш-оповещения, чтобы помочь с этим — в конечном счете, я их отполирую и удалю для опытных пользователей.
MeetingLog также позволяет мне генерировать текстовую сводку истории планирования собрания. Если вы хотите узнать больше об этом, я написал об этом в статье «Создание стартапа: Уведомление людей об изменениях в собраниях и доставка уведомлений» .
Что дальше?
Я надеюсь, что эти примеры помогут вам понять основы Ajax в Yii. Если вы заинтересованы в более продвинутом Ajax, я планирую включить загруженные Ajax формы в серию Meeting Planner. И, по общему признанию, Ajax — это область, где сообщество Yii не поделилось множеством примеров. Как правило, Ajax работает в Yii так же, как и в PHP и других фреймворках, поэтому вы можете учиться на примерах из других сообществ фреймворков.
Следите за будущими уроками в нашей серии «Программирование с Yii2», поскольку мы продолжаем изучать различные аспекты фреймворка. Вы также можете ознакомиться с нашей серией «Построение стартапа с помощью PHP» , в которой используется расширенный шаблон Yii2 при создании реального приложения.
Если вы хотите узнать, когда появится следующий учебник по Yii2, следуйте за мной @reifman в Твиттере или зайдите на страницу моего инструктора . Моя страница инструктора будет включать все статьи из этой серии, как только они будут опубликованы.
Ссылки по теме
- Аякс (Википедия)
- Начало работы — Ajax (Сеть разработчиков Mozilla)
- Yii2 Developer Exchange , мой ресурсный сайт Yii2