German (Deutsch) translation by Katharina Grigorovich-Nevolina (you can also view the original English article)



Das ist Teil drei der Erstellung Ihrer Startup With PHP-Serie auf Tuts +. In dieser Serie führe ich Sie durch die Einführung eines Startups von der Idee zur Realität, indem ich meine Meeting Planner-App als ein Beispiel aus dem echten Leben benutze. Bei jedem Schritt veröffentlichen wir den Meeting-Planer-Code als Open-Source-Beispiele, von denen Sie lernen können.
In diesem Teil werden wir einige der zugrunde liegenden Infrastrukturen für das Konzept von Orte erstellen, an denen Personen Besprechungen planen können. Wir werden die Grundlagen der Arbeit mit Places behandeln, aufbauend auf unserem Datenbankschema, die Integration von HTML5 Geolocation und APIs für Google Maps und Google Places. Die Idee ist, diese Funktionen zu verwenden, um die Wahl des Ortes für Ihre Meetings schnell und einfach zu machen. Wir werden nicht alle Pass- und Finishs in dieser Episode abdecken - aber wir werden mehr davon in einem kommenden Tutorial behandeln.
Der gesamte Code für Meeting Planner ist in das Yii2 Framework für PHP geschrieben und nutzt Bootstrap und JavaScript. Wenn Sie mehr über Yii2 erfahren möchten, schauen Sie Ihr unsere Parallel-Serie Programmierung mit Yii2 bei Tuts + an.
Nur zur Erinnerung, ich nehme an den Kommentarthreads unten teil. Ich bin besonders interessiert, wenn Sie verschiedene Ansätze oder zusätzliche Ideen haben oder Themen für zukünftige Tutorials vorschlagen möchten. Feature-Anfragen für den Meeting Planner sind ebenfalls willkommen.
Erstellung Funktionalität der Orte
Bevor Benutzer Besprechungen planen können, müssen sie ihre bevorzugten Orte finden und vorschlagen können. Aus Gründen der Einfachheit werden wir die Funktionen zum Suchen und Erstellen von Orten getrennt von der Planungsfunktion erstellen.
Benutzern stehen drei Möglichkeiten zum Hinzufügen von Orten zur Verfügung:
- Mit HTML5 Geolocation können sie ihren aktuellen Standort über WLAN nachschlagen und diesen als Ort hinzufügen.
- Mithilfe der Google Places-API können sie mithilfe der automatischen Vervollständigung nach einem Ort in der Places-Datenbank suchen. Wenn wir schließlich ihren aktuellen Standort kennen, können wir die Suchergebnisse auf Orte in der Nähe beschränken.
- Manueller Eintrag. Benutzer können eine Adresse und eine Beschreibung für ihren eigenen Ort, wie ein Büro oder ein Haus, eingeben.
Erweitern des Platzierungsschemas
Hier ist das Schema für Orte, die wir im zweiten Teil entwickelt haben:
1 |
$tableOptions = null; |
2 |
if ($this->db->driverName === 'mysql') { |
3 |
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; |
4 |
}
|
5 |
|
6 |
$this->createTable('{{%place}}', [ |
7 |
'id' => Schema::TYPE_PK, |
8 |
'name' => Schema::TYPE_STRING.' NOT NULL', |
9 |
'place_type' => Schema::TYPE_SMALLINT.' NOT NULL DEFAULT 0', |
10 |
'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', |
11 |
'google_place_id' => Schema::TYPE_STRING.' NOT NULL', // e.g. google places id |
12 |
'created_by' => Schema::TYPE_BIGINT.' NOT NULL', |
13 |
'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', |
14 |
'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', |
15 |
], $tableOptions); |
16 |
$this->addForeignKey('fk_place_created_by', '{{%place}}', 'created_by', '{{%user}}', 'id', 'CASCADE', 'CASCADE'); |
Beachten Sie, dass in dieser Tabelle keine Geolocation mit einem Place verknüpft ist. Das liegt daran, dass die MySQL InnoDB-Engine räumliche Indizes nicht unterstützt. Also habe ich eine sekundäre Tabelle mit der MyISAM-Tabelle für die Geolocation-Koordinaten von Places erstellt. Es ist die Place_GPS
-Tabelle:
1 |
class m141025_213611_create_place_gps_table extends Migration |
2 |
{
|
3 |
public function up() |
4 |
{
|
5 |
$tableOptions = null; |
6 |
if ($this->db->driverName === 'mysql') { |
7 |
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=MyISAM'; |
8 |
}
|
9 |
|
10 |
$this->createTable('{{%place_gps}}', [ |
11 |
'id' => Schema::TYPE_PK, |
12 |
'place_id' => Schema::TYPE_INTEGER.' NOT NULL', |
13 |
'gps'=>'POINT NOT NULL', |
14 |
], $tableOptions); |
15 |
$this->execute('create spatial index place_gps_gps on '.'{{%place_gps}}(gps);'); |
16 |
$this->addForeignKey('fk_place_gps','{{%place_gps}}' , 'place_id', '{{%place}}', 'id', 'CASCADE', 'CASCADE'); |
17 |
}
|
Da ich im Rapid-Prototyping-Modus bin, werde ich das Schema mithilfe von Yi's Migrationen erweitern und eventuell auch zukünftige Anpassungen vornehmen.
Um das Schema zu erweitern, erstellen wir eine neue Migration in Yii:
1 |
./yii migrate/create extend_place_table |
Und stellen Sie den folgenden Code bereit:
1 |
<?php
|
2 |
|
3 |
use yii\db\Schema; |
4 |
use yii\db\Migration; |
5 |
|
6 |
class m150114_202542_extend_place_table extends Migration |
7 |
{
|
8 |
public function up() |
9 |
{
|
10 |
$tableOptions = null; |
11 |
if ($this->db->driverName === 'mysql') { |
12 |
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; |
13 |
}
|
14 |
$this->addColumn('{{%place}}','slug','string NOT NULL'); |
15 |
$this->addColumn('{{%place}}','website','string NOT NULL'); |
16 |
$this->addColumn('{{%place}}','full_address','string NOT NULL'); |
17 |
$this->addColumn('{{%place}}','vicinity','string NOT NULL'); |
18 |
$this->addColumn('{{%place}}','notes','text'); |
19 |
}
|
20 |
|
21 |
public function down() |
22 |
{
|
23 |
$this->dropColumn('{{%place}}','slug'); |
24 |
$this->dropColumn('{{%place}}','website'); |
25 |
$this->dropColumn('{{%place}}','full_address'); |
26 |
$this->dropColumn('{{%place}}','vicinity'); |
27 |
$this->dropColumn('{{%place}}','notes'); |
28 |
}
|
29 |
}
|
Dadurch werden Spalten für Slug, Website, vollständige_Adresse, Umgebung und Notizen hinzugefügt. Der Slug ist eine URL-freundliche Adresse für die Anzeige der Ortsansichtsseite, die Yii automatisch für uns generieren kann. Die anderen Felder werden manchmal von Nutzern aktualisiert, andere von der Google Places-API.
Um die Migration auszuführen, geben Sie Folgendes ein:
1 |
./yii migrate/up |
Sie sollten Folgendes sehen:
1 |
Yii Migration Tool (based on Yii v2.0.0) |
2 |
|
3 |
Total 1 new migration to be applied: |
4 |
m150114_202542_extend_place_table |
5 |
|
6 |
Apply the above migration? (yes|no) [no]:yes |
7 |
*** applying m150114_202542_extend_place_table
|
8 |
> add column slug string NOT NULL to table {{%place}} ... done (time: 0.011s) |
9 |
> add column website string NOT NULL to table {{%place}} ... done (time: 0.010s) |
10 |
> add column full_address string NOT NULL to table {{%place}} ... done (time: 0.010s) |
11 |
> add column vicinity string NOT NULL to table {{%place}} ... done (time: 0.011s) |
12 |
> add column notes text to table {{%place}} ... done (time: 0.011s) |
13 |
*** applied m150114_202542_extend_place_table (time: 0.055s) |
14 |
|
15 |
|
16 |
Migrated up successfully. |
Aktualisieren des CRUD-Codes
Wenn Sie die Places-Seite besuchen, z.B. http://localhost: 8888/mp/index.php/place/create, Sie sehen das automatisch generierte Yii2-Formular mit allen unseren Schemafeldern:



Erstellen Sie das Standard-Yii2-FormularFür dieses Tutorial habe ich den Code-Generator von Yii, Gii, erneut ausgeführt, wobei ich die Schritte aus Teil 2 nutze, um Code für das neue Datenbankschema zu erstellen. Ich wies Gii an, den CRUD-Code von früher zu überschreiben.
Hinweis: Es kann für Sie am einfachsten sein, Ihre Probenquelle aus Teil 2 mit der Probenquelle aus diesem Teil zu ersetzen. Sehen Sie den Github-Link oben rechts.
Sie müssen auch unsere Lieferantenbibliotheken mit Composer aktualisieren, um die Unterstützung für die 2amigOS Yii2 Google Maps und Places Bibliotheken zu integrieren. Hier ist ein Teil unserer composer.json-Datei:
1 |
"minimum-stability": "stable", |
2 |
"require": { |
3 |
"php": ">=5.4.0", |
4 |
"yiisoft/yii2": "*", |
5 |
"yiisoft/yii2-bootstrap": "*", |
6 |
"yiisoft/yii2-swiftmailer": "*", |
7 |
"2amigos/yii2-google-maps-library": "*", |
8 |
"2amigos/yii2-google-places-library": "*" |
Führen Sie dann das Composer-Update aus, um die Dateien herunterzuladen:
1 |
sudo composer update
|
Drei verschiedene Möglichkeiten, Orte hinzuzufügen
Wir werden tatsächlich drei verschiedene Controller-Aktionen und -Formen für jeden dieser Arten von Orten erstellen. Denken Sie daran, dass wir auch das zugehörige Modell, PlaceGPS
, integrieren müssen, um die GPS-Koordinaten für jeden Ort zu speichern, unabhängig davon, wie der Benutzer sie hinzufügt.
Hinzufügen von Orten zur Navigationsleiste
Um einen Places-Link zur Navigationsleiste hinzuzufügen, bearbeiten Sie /views/layouts/main.php
. Das ist das Standard-Seitenlayout, mit dem Yii alle unsere Ansichtsdateien umschließt. Es enthält die Kopfzeile, Bootstrap-Navigationsleiste und Fußzeile.
Unten in $menuItems
füge ich einen Array-Eintrag für das Menü Ort hinzu:
1 |
NavBar::begin([ |
2 |
'brandLabel' => 'MeetingPlanner.io', // |
3 |
'brandUrl' => Yii::$app->homeUrl, |
4 |
'options' => [ |
5 |
'class' => 'navbar-inverse navbar-fixed-top', |
6 |
],
|
7 |
]);
|
8 |
$menuItems = [ |
9 |
['label' => 'Home', 'url' => ['/site/index']], |
10 |
['label' => 'Places', 'url' => ['/place']], |
11 |
['label' => 'About', 'url' => ['/site/about']], |
12 |
['label' => 'Contact', 'url' => ['/site/contact']], |
13 |
];
|
14 |
if (Yii::$app->user->isGuest) { |
15 |
$menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']]; |
16 |
$menuItems[] = ['label' => 'Login', 'url' => ['/site/login']]; |
17 |
} else { |
18 |
$menuItems[] = [ |
19 |
'label' => 'Logout (' . Yii::$app->user->identity->username . ')', |
20 |
'url' => ['/site/logout'], |
21 |
'linkOptions' => ['data-method' => 'post'] |
22 |
];
|
23 |
}
|
24 |
echo Nav::widget([ |
25 |
'options' => ['class' => 'navbar-nav navbar-right'], |
26 |
'items' => $menuItems, |
27 |
]);
|
28 |
NavBar::end(); |
29 |
?>
|
Die Ortsindexansicht
Die Ortsindexansicht sieht folgendermaßen aus, nachdem wir Schaltflächen für die drei Möglichkeiten zum Hinzufügen von Orten hinzugefügt haben:



In /views/place/index.php
können wir drei Schaltflächen add place
hinzufügen:
1 |
<p>
|
2 |
<?= Html::a('Add Place', ['create'], ['class' => 'btn btn-success']) ?> |
3 |
<?= Html::a('Add Current Location', ['create_geo'], ['class' => 'btn btn-success']) ?> |
4 |
<?= Html::a('Add a Google Place', ['create_place_google'], ['class' => 'btn btn-success']) ?> |
5 |
</p>
|
Außerdem können wir die Spalten anpassen, die in der Ansicht angezeigt werden, einschließlich der Erstellung einer benutzerdefinierten Spalte für eine Place-Methode, die den Anzeigenamen für den Bereichstyp anzeigt:
1 |
<?= GridView::widget([ |
2 |
'dataProvider' => $dataProvider, |
3 |
'filterModel' => $searchModel, |
4 |
'columns' => [ |
5 |
['class' => 'yii\grid\SerialColumn'], |
6 |
'name', |
7 |
[
|
8 |
'attribute' => 'place_type', |
9 |
'format' => 'raw', |
10 |
'value' => function ($model) { |
11 |
return '<div>'.$model->getPlaceType($model->place_type).'</div>'; |
12 |
},
|
13 |
],
|
14 |
['class' => 'yii\grid\ActionColumn'], |
15 |
],
|
16 |
]); ?> |
Hier ist eine Teilmenge der Place Type Methoden in /models/Place.php
:
1 |
const TYPE_OTHER = 0; |
2 |
const TYPE_RESTAURANT = 10; |
3 |
const TYPE_COFFEESHOP = 20; |
4 |
const TYPE_RESIDENCE = 30; |
5 |
const TYPE_OFFICE = 40; |
6 |
const TYPE_BAR = 50; |
7 |
|
8 |
...
|
9 |
|
10 |
public function getPlaceType($data) { |
11 |
$options = $this->getPlaceTypeOptions(); |
12 |
return $options[$data]; |
13 |
}
|
14 |
|
15 |
public function getPlaceTypeOptions() |
16 |
{
|
17 |
return array( |
18 |
self::TYPE_RESTAURANT => 'Restaurant', |
19 |
self::TYPE_COFFEESHOP => 'Coffeeshop', |
20 |
self::TYPE_RESIDENCE => 'Residence', |
21 |
self::TYPE_OFFICE => 'Office', |
22 |
self::TYPE_BAR => 'Bar', |
23 |
self::TYPE_OTHER => 'Other' |
24 |
);
|
25 |
}
|
Beachten Sie, dass der Anmeldestatus oder die Benutzereigentümerschaft für Orte noch nicht angesprochen wurden. Wir werden das im nächsten Tutorial noch einmal besprechen. Aufgrund der Komplexität und des Umfangs dieser Phase werden wir eine Handvoll Finish-Elemente für ein späteres Tutorial hinterlassen.
Hinzufügen von Orten mit HTML5 Geolocation
Ein Szenario zum Hinzufügen von Orten besteht darin, einen Platz für Ihr Zuhause oder Ihr Büro zu schaffen. Anstatt zu verlangen, dass Benutzer diese Informationen manuell eingeben, können wir das oft automatisch mit HTML5 Geolocation generieren.
HTML5 Geolocation verwendet Ihre WiFi-Adresse, um GPS-Punkte für Ihren aktuellen Standort zu ermitteln. Es funktioniert nicht mit zellulären/mobilen Verbindungen und es ist nicht idiotensicher.
Der Benutzer muss wahrscheinlich seinem Browser die Berechtigung für Geolocation erteilen, damit diese Funktion funktioniert. Suchen Sie nach einem Popup unter der Adressleiste, wie unten gezeigt:



Ich verwende das Geopositionsskript von estebanav, um HTML5 Geolocation mit der größtmöglichen Browserunterstützung zu unterstützen.
Hinzufügen der Place Controller-Aktion für Geolocation
In frontend/controller/PlaceController.php
erstellen wir eine neue Methode für die Aktion Create_geo
:
1 |
/** |
2 |
* Creates a new Place model via Geolocation |
3 |
*/ |
4 |
public function actionCreate_geo() |
5 |
{ |
6 |
$model = new Place(); |
7 |
if ($model->load(Yii::$app->request->post())) { |
8 |
... to be explained below... |
9 |
} else { |
10 |
return $this->render('create_geo', [ |
11 |
'model' => $model, |
12 |
]); |
13 |
} |
Da das Formular noch nicht übergeben wurde, rendert Yii die create_geo
-Ansicht, um das Formular anzuzeigen.
In frontend/views/place/create_geo.php
fügen wir _formGeolocate.php
ein:
1 |
<?php
|
2 |
|
3 |
use yii\helpers\Html; |
4 |
|
5 |
|
6 |
/* @var $this yii\web\View */
|
7 |
/* @var $model frontend\models\Place */
|
8 |
|
9 |
$this->title = 'Create Place By Geolocation'; |
10 |
$this->params['breadcrumbs'][] = ['label' => 'Places', 'url' => ['index']]; |
11 |
$this->params['breadcrumbs'][] = $this->title; |
12 |
?>
|
13 |
<div class="place-create"> |
14 |
|
15 |
<h1><?= Html::encode($this->title) ?></h1> |
16 |
|
17 |
<?= $this->render('_formGeolocate', [ |
18 |
'model' => $model, |
19 |
]) ?> |
20 |
|
21 |
</div>
|
Sehen wir uns den ersten Teil von _formGeolocate
an. Wir müssen das JavaScript für Geoposition.js und unseren eigenen Geolocation-Code integrieren, um die Geoposition mit unserem Formular zu integrieren. Die Art, wie Yii dies tut, ist mit Asset-Bundles. Sie definieren ein Asset-Paket für verschiedene Seiten und können so optimieren, welche JS und CSS in verschiedenen Bereichen Ihrer Anwendung geladen werden. Anwendung geladen werden. Wir erstellen zuerst LocateAsset
:
1 |
?php |
2 |
|
3 |
use yii\helpers\Html; |
4 |
use yii\helpers\BaseHtml; |
5 |
use yii\widgets\ActiveForm; |
6 |
|
7 |
use frontend\assets\LocateAsset; |
8 |
LocateAsset::register($this); |
In frontend/assets/LocateAsset.php
definieren wir das JavaScript, das wir einbeziehen müssen:
1 |
<?php
|
2 |
|
3 |
namespace frontend\assets; |
4 |
|
5 |
use yii\web\AssetBundle; |
6 |
|
7 |
class LocateAsset extends AssetBundle |
8 |
{
|
9 |
public $basePath = '@webroot'; |
10 |
public $baseUrl = '@web'; |
11 |
public $css = [ |
12 |
];
|
13 |
public $js = [ |
14 |
'js/locate.js', |
15 |
'js/geoPosition.js', |
16 |
'http://maps.google.com/maps/api/js?sensor=false', |
17 |
];
|
18 |
public $depends = [ |
19 |
];
|
20 |
}
|
LocateAsset
lädt das Google Maps-API, die geoPosition
-Bibliothek und unseren benutzerdefinierten Locate.js-Code vor, der unten angezeigt wird:
1 |
function beginSearch() { |
2 |
$('#preSearch').hide(); |
3 |
$('#searchArea').removeClass('hidden'); |
4 |
//if (navigator.geolocation) { //navigator.
|
5 |
if (geoPosition.init()) { |
6 |
geoPosition.getCurrentPosition(success, errorHandler, {timeout:5000}); |
7 |
} else { |
8 |
error('Sorry, we are not able to use browser geolocation to find you.'); |
9 |
}
|
10 |
}
|
11 |
|
12 |
function success(position) { |
13 |
$('#actionBar').removeClass('hidden'); |
14 |
$('#autolocateAlert').addClass('hidden'); |
15 |
var s = document.querySelector('#status'); |
16 |
//var buttons = document.querySelector('#locate_actions');
|
17 |
if (s.className == 'success') { |
18 |
// not sure why we're hitting this twice in FF, I think it's to do with a cached result coming back
|
19 |
return; |
20 |
}
|
21 |
|
22 |
s.innerHTML = "You are here:"; |
23 |
s.className = 'success'; |
24 |
|
25 |
var mapcanvas = document.createElement('div'); |
26 |
mapcanvas.id = 'mapcanvas'; |
27 |
mapcanvas.style.height = '300px'; |
28 |
mapcanvas.style.width = '300px'; |
29 |
mapcanvas.style.border = '1px solid black'; |
30 |
|
31 |
document.querySelector('article').appendChild(mapcanvas); |
32 |
|
33 |
var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude); |
34 |
var myOptions = { |
35 |
zoom: 16, |
36 |
center: latlng, |
37 |
mapTypeControl: false, |
38 |
navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL}, |
39 |
mapTypeId: google.maps.MapTypeId.ROADMAP |
40 |
};
|
41 |
var map = new google.maps.Map(document.getElementById("mapcanvas"), myOptions); |
42 |
|
43 |
var marker = new google.maps.Marker({ |
44 |
position: latlng, |
45 |
map: map, |
46 |
title:"You are here! (at least within a "+position.coords.accuracy+" meter radius)" |
47 |
});
|
48 |
$('#locate_actionbar').removeClass('hidden'); |
49 |
$('#place-lat').val(position.coords.latitude); |
50 |
$('#place-lng').val(position.coords.longitude); |
51 |
}
|
52 |
|
53 |
function errorHandler(err) { |
54 |
var s = document.querySelector('#status'); |
55 |
s.innerHTML = typeof msg == 'string' ? msg : "failed"; |
56 |
s.className = 'fail'; |
57 |
//if (err.code == 1) {} // user said no!
|
58 |
document.location.href='/place/index?errorLocate'; |
59 |
}
|
60 |
Grundsätzlich wird Geolocation initiiert, wenn der Benutzer beginSearch
auslöst. Der Geopositionscode ruft die Erfolgsfunktion auf, wenn er mit dem Standort des Benutzers zurückgegeben wird. Wir passen die Erfolgsfunktion an, um eine Karte am Standort anzuzeigen und unsere versteckten Formularfelder mit dem zurückgegebenen Breiten- und Längengrad zu füllen. Wenn der Benutzer das Formular sendet, stehen die Standortkoordinaten unserer Web-App zur Verfügung.
Hier ist der Code innerhalb von Success()
, der die Formularfelder mit den Standortkoordinaten füllt:
1 |
$('#place-lat').val(position.coords.latitude); |
2 |
$('#place-lng').val(position.coords.longitude); |
Der Rest von _formGeolocate.php
ist in zwei gleiche Hälften aufgeteilt. Auf der linken Seite stellen wir die Formularfelder für den Benutzer zur Verfügung, um mit den Geolokationsdaten und den versteckten Feldern, die wir benötigen, um das JavaScript zu unterstützen. Auf der rechten Seite lassen wir Platz für eine Schaltfläche, um Geolocation auszulösen und die Karte anzuzeigen. Die Funktion success()
füllt <article>
mit der Map.
1 |
<div class="place-form"> |
2 |
<?php $form = ActiveForm::begin(); ?> |
3 |
<div class="col-md-6"> |
4 |
|
5 |
<?= $form->field($model, 'name')->textInput(['maxlength' => 255]) ?> |
6 |
|
7 |
<?= $form->field($model, 'website')->textInput(['maxlength' => 255]) ?> |
8 |
|
9 |
<?= $form->field($model, 'place_type') |
10 |
->dropDownList( |
11 |
$model->getPlaceTypeOptions(), |
12 |
['prompt'=>'What type of place is this?'] |
13 |
)->label('Type of Place') ?> |
14 |
|
15 |
<?= $form->field($model, 'notes')->textArea() ?> |
16 |
|
17 |
<?= BaseHtml::activeHiddenInput($model, 'lat'); ?> |
18 |
<?= BaseHtml::activeHiddenInput($model, 'lng'); ?> |
19 |
|
20 |
<div class="form-group"> |
21 |
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> |
22 |
</div>
|
23 |
|
24 |
</div> <!-- end col 1 --><div class="col-md-6"> |
25 |
<div id="preSearch" class="center"> |
26 |
<p><br /></p> <?= Html::a('Lookup Location', ['lookup'], ['class' => 'btn btn-success', 'onclick' => "javascript:beginSearch();return false;"]) ?> |
27 |
</div>
|
28 |
|
29 |
<div id="searchArea" class="hidden"> |
30 |
<div id="autolocateAlert"> |
31 |
</div> <!-- end autolocateAlert --> |
32 |
<p>Searching for your current location...<span id="status"></span></p> |
33 |
<article>
|
34 |
</article>
|
35 |
<div class="form-actions hidden" id="actionBar"> |
36 |
</div> <!-- end action Bar--> |
37 |
</div> <!-- end searchArea --> |
38 |
</div> <!-- end col 2 --> |
39 |
<?php ActiveForm::end(); ?> |
40 |
|
41 |
</div>
|
42 |
So sieht das Formular zunächst aus:



Klicken Sie auf die Schaltfläche Lookup Location, um die Geolokalisierung zu starten. Suchen Sie erneut nach einer Berechtigungsanforderung in der Browsernavigationsleiste.
Sobald Ihr Standort gefunden ist, zeigen wir Ihren Standort auf einer Karte an:



Hinweis: Ich habe die Verzögerung für Geolocation auf fünf Sekunden festgelegt, aber manchmal müssen Sie die Seite erneut laden, um die richtige Antwort zu erhalten, nachdem Sie die Berechtigung erteilt haben. Bestimmte WiFi -Standorte sind weniger bestimmt als andere.
Werfen wir einen Blick auf den Übermittlungscode des Meeting-Controller-Formulars:
1 |
public function actionCreate_geo() |
2 |
{
|
3 |
$model = new Place(); |
4 |
if ($model->load(Yii::$app->request->post())) { |
5 |
if (Yii::$app->user->getIsGuest()) { |
6 |
$model->created_by = 1; |
7 |
} else { |
8 |
$model->created_by= Yii::$app->user->getId(); |
9 |
}
|
10 |
$form = Yii::$app->request->post(); |
11 |
$model->save(); |
12 |
// add GPS entry in PlaceGeometry
|
13 |
$model->addGeometryByPoint($model,$form['Place']['lat'],$form['Place']['lng']); |
14 |
return $this->redirect(['view', 'id' => $model->id]); |
Im Moment legen wir nur einen Platzhalter für den created_by-Benutzer ein und lassen die Fehlerbehandlung für später zurück (Tut mir leid, Puristen, das ist momentan nicht der Fokus dieses Tutorials).
Wenn das Formular erstellt und ein Ort erstellt wird, greifen wir den Geolocation-Punkt aus dem Formular (diese ausgeblendeten Felder werden vom Locate.js-Skript ausgefüllt) und fügen der zugehörigen Ortstabelle PlaceGPS
eine Zeile hinzu.
Wie im zweiten Teil erwähnt, trennen wir die Geolocation-Daten in einer anderen Tabelle, da die MySQL InnoDB-Engine keine räumlichen Indizes unterstützt. Das verbessert auch die Leistung von Abfragen, um die nächsten Besprechungsplätze zwischen zwei Benutzern zu finden.
Hier ist die addGeometryByPoint
-Methode im Place.php-Modell:
1 |
public function addGeometryByPoint($model,$lat,$lon) { |
2 |
$pg = new PlaceGPS; |
3 |
$pg->place_id=$model->id; |
4 |
$pg->gps = new \yii\db\Expression("GeomFromText('Point(".$lat." ".$lon.")')"); |
5 |
$pg->save(); |
6 |
}
|
So sollte die Seite Place-Index aussehen, nachdem der Datensatz gespeichert wurde:



Wenn Sie eine andere Implementierung von HTML5 Geolocation für Yii 1.x sehen möchten, lesen Sie die Informationen zur Verwendung von Zillow Neighborhood Maps und HTML5 Geolocation.
Orte in Google Maps anzeigen
Wenn Sie auf das Befehlssymbol für die Ansicht klicken, das unserem neuen Ort in der obigen Indexansicht zugeordnet ist, sehen Sie Folgendes:



Wir haben die von Google generierte Ansichtsseite angepasst und Code hinzugefügt, um die Google Map mit der Google Maps-Erweiterung von Yii2 zu zeichnen.
Hier ist die View
-Aktion in PlaceController.php:
1 |
/**
|
2 |
* Displays a single Place model.
|
3 |
* @param integer $id
|
4 |
* @return mixed
|
5 |
*/
|
6 |
public function actionView($id) |
7 |
{
|
8 |
$model = $this->findModel($id); |
9 |
$gps = $model->getLocation($id); |
10 |
return $this->render('view', [ |
11 |
'model' => $model, |
12 |
'gps'=> $gps, |
13 |
]);
|
14 |
}
|
Hier ist die Methode getLocation
im Modell Place.php. Er ruft die Standortkoordinaten aus der Tabelle PlaceGPS
ab:
1 |
public function getLocation($place_id) { |
2 |
$sql = 'Select AsText(gps) as gps from {{%place_gps}} where place_id = '.$place_id; |
3 |
$model = PlaceGPS::findBySql($sql)->one(); |
4 |
$gps = new \stdClass; |
5 |
if (is_null($model)) { |
6 |
return false; |
7 |
} else { |
8 |
list($gps->lat, $gps->lng) = $this->string_to_lat_lon($model->gps); |
9 |
}
|
10 |
return $gps; |
11 |
}
|
Hier ist ein Teil der View-Datei, die die Seite rendert. Die linke Seite besteht vorerst aus einem Standard Yii2 DetailView
Widget. Die rechte Seite erzeugt den Code, der die Karte zeichnet:
1 |
<div class="col-md-6"> |
2 |
<div class="place-view"> |
3 |
|
4 |
<?= DetailView::widget([ |
5 |
'model' => $model, |
6 |
'attributes' => [ |
7 |
'name', |
8 |
'place_type', |
9 |
'website', |
10 |
'full_address', |
11 |
],
|
12 |
]) ?> |
13 |
|
14 |
</div>
|
15 |
</div> <!-- end first col --> |
16 |
<div class="col-md-6"> |
17 |
<?
|
18 |
if ($gps!==false) { |
19 |
$coord = new LatLng(['lat' => $gps->lat, 'lng' => $gps->lng]); |
20 |
$map = new Map([ |
21 |
'center' => $coord, |
22 |
'zoom' => 14, |
23 |
'width'=>300, |
24 |
'height'=>300, |
25 |
]);
|
26 |
$marker = new Marker([ |
27 |
'position' => $coord, |
28 |
'title' => $model->name, |
29 |
]);
|
30 |
// Add marker to the map
|
31 |
$map->addOverlay($marker); |
32 |
echo $map->display(); |
33 |
} else { |
34 |
echo 'No location coordinates for this place could be found.'; |
35 |
}
|
36 |
?>
|
37 |
|
38 |
</div> <!-- end second col --> |
Hinzufügen aus der Google Places-API
Die Autocomplete-Funktion von Google Places bietet Benutzern eine unglaublich schnelle und einfache Möglichkeit, Besprechungsplätze hinzuzufügen. Ich verwende die Yii2 Google Places-Erweiterung von 2amigOS.



In PlaceController.php fügen wir eine Aktion für Create_place_google
hinzu:
1 |
/**
|
2 |
* Creates a new Place model from Google Place
|
3 |
* If creation is successful, the browser will be redirected to the 'view' page.
|
4 |
* @return mixed
|
5 |
*/
|
6 |
public function actionCreate_place_google() |
7 |
{
|
8 |
$model = new Place(); |
9 |
if ($model->load(Yii::$app->request->post())) { |
10 |
... to be explained further below... |
11 |
} else { |
12 |
return $this->render('create_place_google', [ |
13 |
'model' => $model, |
14 |
]);
|
15 |
}
|
16 |
}
|
Die Datei /frontend/views/place/create_place_google.php
zeigt das Formular an und initialisiert das JavaScript, das zur Unterstützung der automatischen Vervollständigung benötigt wird:
1 |
<div class="place-create"> |
2 |
|
3 |
<h1><?= Html::encode($this->title) ?></h1> |
4 |
<?= $this->render('_formPlaceGoogle', [ |
5 |
'model' => $model, |
6 |
]) ?> |
7 |
|
8 |
</div>
|
9 |
|
10 |
<?
|
11 |
|
12 |
$gpJsLink= 'http://maps.googleapis.com/maps/api/js?' . http_build_query(array( |
13 |
'libraries' => 'places', |
14 |
'sensor' => 'false', |
15 |
));
|
16 |
echo $this->registerJsFile($gpJsLink); |
17 |
|
18 |
$options = '{"types":["establishment"],"componentRestrictions":{"country":"us"}}'; |
19 |
echo $this->registerJs("(function(){ |
20 |
var input = document.getElementById('place-searchbox');
|
21 |
var options = $options; |
22 |
searchbox = new google.maps.places.Autocomplete(input, options);
|
23 |
setupListeners();
|
24 |
})();" , \yii\web\View::POS_END ); |
25 |
// 'setupBounds('.$bound_bl.','.$bound_tr.');
|
26 |
?>
|
Entwickler Petra Barus hat eine Google Places-Erweiterung für Yii1.x bereitgestellt. Für dieses Tutorial habe ich die grundlegende Unterstützung für Yii2 handkodiert. Allerdings war Barus so freundlich, nur wenige Tage später eine Yii2-Erweiterung zu veröffentlichen. Ich habe seinen Code noch nicht integriert. Ich habe seinen Code noch nicht integriert. Hier ist seine neueste Yii2 Google Places Autocomplete-Erweiterung.
Hier ist das MapAsset
-Paket, das ich für das zugehörige JavaScript erstellt habe, das benötigt wird:
1 |
<?php
|
2 |
|
3 |
namespace frontend\assets; |
4 |
|
5 |
use yii\web\AssetBundle; |
6 |
|
7 |
class MapAsset extends AssetBundle |
8 |
{
|
9 |
public $basePath = '@webroot'; |
10 |
public $baseUrl = '@web'; |
11 |
public $css = [ |
12 |
];
|
13 |
public $js = [ |
14 |
'js/create_place.js', |
15 |
];
|
16 |
public $depends = [ |
17 |
];
|
18 |
}
|
Hier ist der Formularcode _formPlaceGoogle.php
:
1 |
<?php
|
2 |
|
3 |
use yii\helpers\Html; |
4 |
use yii\helpers\BaseHtml; |
5 |
use yii\widgets\ActiveForm; |
6 |
|
7 |
use frontend\assets\MapAsset; |
8 |
MapAsset::register($this); |
9 |
|
10 |
/* @var $this yii\web\View */
|
11 |
/* @var $model frontend\models\Place */
|
12 |
/* @var $form yii\widgets\ActiveForm */
|
13 |
?>
|
14 |
<div class="col-md-6"> |
15 |
|
16 |
<div class="placegoogle-form"> |
17 |
<p>Type in a place or business known to Google Places:</p> |
18 |
|
19 |
<?php $form = ActiveForm::begin(); ?> |
20 |
<?= $form->field($model, 'searchbox')->textInput(['maxlength' => 255])->label('Place') ?> |
21 |
|
22 |
<?= BaseHtml::activeHiddenInput($model, 'name'); ?> |
23 |
<?= BaseHtml::activeHiddenInput($model, 'google_place_id'); ?> |
24 |
<?= BaseHtml::activeHiddenInput($model, 'location'); ?> |
25 |
<?= BaseHtml::activeHiddenInput($model, 'website'); ?> |
26 |
<?= BaseHtml::activeHiddenInput($model, 'vicinity'); ?> |
27 |
<?= BaseHtml::activeHiddenInput($model, 'full_address'); ?> |
28 |
|
29 |
<?= $form->field($model, 'place_type') |
30 |
->dropDownList( |
31 |
$model->getPlaceTypeOptions(), |
32 |
['prompt'=>'What type of place is this?'] |
33 |
)->label('Type of Place') ?> |
34 |
|
35 |
<div class="form-group"> |
36 |
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> |
37 |
</div>
|
38 |
|
39 |
<?php ActiveForm::end(); ?> |
40 |
|
41 |
</div>
|
42 |
</div> <!-- end col1 --> |
43 |
<div class="col-md-6"> |
44 |
<div id="map-canvas"> |
45 |
<article></article>
|
46 |
</div>
|
47 |
</div> <!-- end col2 --> |
Es gibt searchbox
, das die automatische Vervollständigung des Benutzers akzeptiert. Es gibt auch eine Vielzahl von versteckten Feldern, die unser JavaScript mit den Ergebnissen aus dem Google Places-Dienst lädt.
Hier ist die create_place.js, die alle "Magie" erfüllt:
1 |
function setupListeners() { |
2 |
// google.maps.event.addDomListener(window, 'load', initialize);
|
3 |
// searchbox is the var for the google places object created on the page
|
4 |
google.maps.event.addListener(searchbox, 'place_changed', function() { |
5 |
var place = searchbox.getPlace(); |
6 |
if (!place.geometry) { |
7 |
// Inform the user that a place was not found and return.
|
8 |
return; |
9 |
} else { |
10 |
// migrates JSON data from Google to hidden form fields
|
11 |
populateResult(place); |
12 |
}
|
13 |
});
|
14 |
}
|
15 |
|
16 |
function populateResult(place) { |
17 |
// moves JSON data retrieve from Google to hidden form fields
|
18 |
// so Yii2 can post the data
|
19 |
$('#place-location').val(JSON.stringify(place['geometry']['location'])); |
20 |
$('#place-google_place_id').val(place['place_id']); |
21 |
$('#place-full_address').val(place['formatted_address']); |
22 |
$('#place-website').val(place['website']); |
23 |
$('#place-vicinity').val(place['vicinity']); |
24 |
$('#place-name').val(place['name']); |
25 |
loadMap(place['geometry']['location'],place['name']); |
26 |
}
|
27 |
|
28 |
function loadMap(gps,name) { |
29 |
var mapcanvas = document.createElement('div'); |
30 |
mapcanvas.id = 'mapcanvas'; |
31 |
mapcanvas.style.height = '300px'; |
32 |
mapcanvas.style.width = '300px'; |
33 |
mapcanvas.style.border = '1px solid black'; |
34 |
|
35 |
document.querySelector('article').appendChild(mapcanvas); |
36 |
|
37 |
var latlng = new google.maps.LatLng(gps['k'], gps['D']); |
38 |
var myOptions = { |
39 |
zoom: 16, |
40 |
center: latlng, |
41 |
mapTypeControl: false, |
42 |
navigationControlOptions: {style: google.maps.NavigationControlStyle.SMALL}, |
43 |
mapTypeId: google.maps.MapTypeId.ROADMAP |
44 |
};
|
45 |
var map = new google.maps.Map(document.getElementById("mapcanvas"), myOptions); |
46 |
|
47 |
var marker = new google.maps.Marker({ |
48 |
position: latlng, |
49 |
map: map, |
50 |
title:name |
51 |
});
|
52 |
}
|
Die Methode setupListeners()
verknüpft unser searchbox
mit dem Google Places-Autocomplete-Service. Wenn ein Ereignis place_changed
auftritt, wird populateResult()
aufgerufen, um die ausgeblendeten Felder im Formular mit Daten von Google zu füllen und die Karte zu laden, die in der rechten Hälfte des Formulars angezeigt wird.



Mit dem Browser-Debugger können Sie die ausgeblendeten Felder nach dem Ausfüllen mit Formulardaten über JavaScript überprüfen. Diese Daten werden mit dem Formular bei der Einreichung gepostet, damit wir sie zur Place-Datenbank hinzufügen können.



Hier ist das verbleibende Element der PlaceController-Erstellungsaktion Create_place_google
:
1 |
public function actionCreate_place_google() |
2 |
{
|
3 |
$model = new Place(); |
4 |
if ($model->load(Yii::$app->request->post())) { |
5 |
if (Yii::$app->user->getIsGuest()) { |
6 |
$model->created_by = 1; |
7 |
} else { |
8 |
$model->created_by= Yii::$app->user->getId(); |
9 |
}
|
10 |
$form = Yii::$app->request->post(); |
11 |
$model->save(); |
12 |
// add GPS entry in PlaceGeometry
|
13 |
$model->addGeometry($model,$form['Place']['location']); |
14 |
return $this->redirect(['view', 'id' => $model->id]); |
Es ähnelt der Create_geo
-Aktion. Wir haben eine separate Place.php-Modellmethode, um die Standortdatenerfassung zu vereinfachen. Hier ist addGeometry()
:
1 |
public function addGeometry($model,$location) { |
2 |
$x = json_decode($location,true); |
3 |
reset($x); |
4 |
$lat = current($x); |
5 |
$lon = next($x); |
6 |
$pg = new PlaceGPS; |
7 |
$pg->place_id=$model->id; |
8 |
$pg->gps = new \yii\db\Expression("GeomFromText('Point(".$lat." ".$lon.")')"); |
9 |
$pg->save(); |
10 |
}
|
Festlegen von geografischen Grenzfiltern für die automatische Vervollständigungssuche
Der Places Autocomplete-Dienst ermöglicht es Ihnen außerdem, ein geographisches Begrenzungsrechteck einzurichten, um Ihre Suche darin zu filtern. Wenn der Benutzer beginnt zu tippen, verwendet die Autocomplete nur Orte innerhalb von zehn Meilen von ihnen. Da wir den aktuellen Standort des Benutzers nicht als Sitzungsvariable eingerichtet haben, implementiere ich das Begrenzungsrechteck derzeit nicht. Aber wir können das später machen. Hier ist ein Beispiel für setupBounds()
:
1 |
function setupBounds(pt1, pt2, pt3, pt4) { |
2 |
defaultBounds = new google.maps.LatLngBounds( |
3 |
new google.maps.LatLng(pt1, pt2), |
4 |
new google.maps.LatLng(pt3, pt4)); |
5 |
searchbox.setBounds(defaultBounds); |
6 |
}
|
Orte manuell hinzufügen
Der dritte Weg, auf dem Benutzer Orte hinzufügen können, ist das manuelle Bereitstellen von Details und Adressinformationen. Wenn sie das Formular abschicken, werden wir versuchen, die Adresse nachzuschlagen und die Geolocation-Daten zu erhalten, aber es ist in Ordnung, wenn wir das nicht finden können. Der manuelle Ansatz ermöglicht Benutzern das Hinzufügen von Orten, z. B. ihres Hauses oder eines Büros, die sie möglicherweise nicht mit Google-Kartendaten verknüpfen möchten.
So sieht das Formular aus:



So sieht der Übermittlungsaktionscode PlaceController.php aus. Wir verwenden den 2Amigos Maps Geocoding-Client, um den Standort von full_address
abzurufen. Es gibt offensichtlich viele Verbesserungen, die wir vornehmen können, um den Benutzer dazu zu ermutigen, die vollständige Adresse einzugeben oder ihn möglicherweise zu einem späteren Zeitpunkt mit einem Standort von Google Places verbinden zu lassen.
1 |
$model = new Place(); |
2 |
if ($model->load(Yii::$app->request->post())) { |
3 |
$form = Yii::$app->request->post(); |
4 |
if (Yii::$app->user->getIsGuest()) { |
5 |
$model->created_by = 1; |
6 |
} else { |
7 |
$model->created_by= Yii::$app->user->getId(); |
8 |
}
|
9 |
$model->save(); |
10 |
$gc = new GeocodingClient(); |
11 |
$result = $gc->lookup(array('address'=>$form['Place']['full_address'],'components'=>1)); |
12 |
$location = $result['results'][0]['geometry']['location']; |
13 |
if (!is_null($location)) { |
14 |
$lat = $location['lat']; |
15 |
$lng = $location['lng']; |
16 |
var_dump($lat); |
17 |
var_dump($lng); |
18 |
// add GPS entry in PlaceGeometry
|
19 |
$model->addGeometryByPoint($model,$lat,$lng); |
20 |
}
|
21 |
return $this->redirect(['view', 'id' => $model->id]); |
22 |
}
|
Was kommt als nächstes?
Der Umfang dieses Tutorials erwies sich als ziemlich groß. Ich wollte Ihnen verschiedene Komponenten zeigen, die an der Geolokalisierung und Kartennutzung beteiligt sind, ohne zu viele Elemente des Kodierungsprozesses zu überspringen. Offensichtlich gibt es im Moment viele Abkürzungen. Im nächsten Tutorial werden wir weiterhin Orte im Gesamtsystem verfeinern, wobei wir uns auf Benutzerberechtigungen, Zugriffskontrollen, Unterstützung für die bevorzugten Orte des Benutzers und andere Verfeinerungen konzentrieren.
Bitte zögern Sie nicht, Ihre Fragen und Kommentare unten zu posten. Ich bin besonders interessiert, wenn Sie verschiedene Ansätze oder zusätzliche Ideen haben oder Themen für zukünftige Tutorials vorschlagen möchten. Sie können mich auch auf Twitter @reifman erreichen oder mir direkt eine E-Mail schicken. Folgen Sie meiner Tuts + Instructor Seite, um zukünftige Artikel in dieser Serie zu sehen.