1. Code
  2. Coding Fundamentals
  3. Rest API

Membangun Startup Anda Dengan PHP: Geolokasi dan Google Places

Ini adalah bagian ketiga dari serial Membangun Startup Anda dengan PHP di Tuts+. Dalam seri ini, saya membimbing Anda melalui peluncuran startup dari konsep ke kenyataan menggunakan aplikasi Meeting Planner saya sebagai contoh nyata. Setiap langkah di sepanjang jalan, kami akan merilis kode Meeting Planner sebagai contoh sumber terbuka yang bisa Anda pelajari.
Scroll to top
This post is part of a series called Building Your Startup With PHP.
Building Your Startup With PHP: Feature Requirements and Database Design
Building Your Startup With PHP: Localization With I18n

Indonesian (Bahasa Indonesia) translation by ⚡ Rova Rindrata (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Ini adalah bagian ketiga dari serial Membangun Startup Anda dengan PHP di Tuts+. Dalam seri ini, saya membimbing Anda melalui peluncuran startup dari konsep ke kenyataan menggunakan aplikasi Meeting Planner saya sebagai contoh nyata. Setiap langkah di sepanjang jalan, kami akan merilis kode Meeting Planner sebagai contoh sumber terbuka yang bisa Anda pelajari.

Pada bagian ini, kita akan membangun beberapa infrastruktur dasar untuk konsep Places dimana orang dapat menjadwalkan pertemuan. Kita akan membahas dasar-dasar bekerja dengan Places, membangun skema database kita, mengintegrasikan Geolokasi HTML5 dan API untuk Google Maps dan Google Places. Idenya adalah menggunakan fitur ini untuk membuat pemilihan lokasi untuk rapat Anda dengan cepat dan mudah. Kami tidak membahas semua fit dan selesai dalam episode ini—tapi kami akan membahasnya lebih banyak lagi dalam tutorial yang akan datang.

Semua kode untuk Meeting Planner ditulis dalam Kerangka Yii2 untuk PHP dan memanfaatkan Bootstrap dan JavaScript. Jika Anda ingin mempelajari lebih lanjut tentang Yii2, lihat seri paralel kami Pemrograman dengan Yii2 di Tuts+.

Sekadar mengingatkan, saya ikut dalam thread komentar di bawah ini. Saya sangat tertarik jika Anda memiliki pendekatan atau ide tambahan yang berbeda, atau ingin menyarankan topik untuk tutorial masa depan. Permintaan fitur untuk Meeting Planner dipersilahkan juga.

Membangun Fungsionalitas Tempat

Sebelum pengguna dapat menjadwalkan pertemuan, kita membutuhkan mereka untuk dapat menemukan dan menyarankan tempat favorit mereka. Awalnya, untuk kesederhanaan, kita akan membangun pencarian Place dan membuat fungsionalitas secara terpisah dari fitur penjadwalan.

Ada tiga cara bagi pengguna untuk menambahkan tempat:

  1. Menggunakan Geolokasi HTML5, mereka bisa mencari lokasi mereka saat ini melalui WiFi dan menambahkannya sebagai tempat.
  2. Dengan menggunakan Google Places API, mereka dapat mencari tempat di database Places menggunakan pelengkapan otomatis. Akhirnya, ketika kita mengetahui lokasi mereka saat ini, kita dapat membatasi hasil pencarian ke tempat-tempat terdekat.
  3. Entri manual. Pengguna bisa memasukkan alamat dan deskripsi untuk tempat mereka sendiri, seperti kantor atau rumah.

Memperluas Skema Tempat

Inilah skema untuk Places yang kita kembangkan di Bagian Dua:

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');

Perhatikan, tidak ada geolokasi yang terkait dengan Places dalam tabel ini. Itu karena engine MySQL InnoDB tidak mendukung indeks spasial. Jadi saya telah membuat tabel sekunder menggunakan tabel MyISAM untuk koordinat geolokasi dari Places. Ini adalah tabel Place_GPS:

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
  }

Karena saya dalam mode prototyping yang cepat, saya akan memperpanjang skema menggunakan migrasi Yii dan akhirnya saya dapat melakukan penyesuaian di masa depan juga.

Untuk memperpanjang skemanya, kita membuat migrasi baru di Yii:

1
./yii migrate/create extend_place_table

Dan berikan kode berikut ini:

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
}

Ini akan menambahkan kolom untuk slug, website, full_address, vicinity, dan notes. Slug adalah alamat URL-friendly untuk menampilkan halaman tampilan Tempat yang dapat dihasilkan Yii untuk kita secara otomatis. Field lainnya terkadang akan diperbarui oleh pengguna dan pada waktu lain diisi dari Google Places API.

Untuk menjalankan migrasinya, kita masukkan yang berikut ini:

1
./yii migrate/up

Anda akan melihat yang berikut ini:

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.

Memperbarui Kode CRUD

Jika Anda mengunjungi halaman Places, mis. http://localhost:8888/mp/index.php/place/create, Anda akan melihat formulir yang dihasilkan otomatis oleh Yii2 default dengan semua field skema kita:

Create Place The Default Yii2 FormCreate Place The Default Yii2 FormCreate Place The Default Yii2 Form

Untuk tutorial ini, saya menjalankan ulang generator kode Yii, Gii, menggunakan langkah-langkah dari Bagian Dua untuk membuat kode untuk skema database baru. Saya menginstruksikan Gii untuk menimpa kode CRUD dari yang sebelumnya.

Catatan: Mungkin paling mudah bagi Anda untuk mengganti sumber sampel Anda dari bagian dua dengan sumber sampel dari bagian ini. Lihat tautan Github ke kanan atas.

Anda juga perlu memperbarui perpustakaan vendor kita dengan composer untuk mengintegrasikan dukungan terhadap perpustakaan Yii2 Google Maps dan Places dari 2amigOS. Berikut adalah bagian dari file composer.json kami:

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": "*"

Kemudian, jalankan composer update untuk mengunduh file-nya:

1
sudo composer update

Tiga Cara Berbeda untuk Menambahkan Tempat

Kita benar-benar akan membangun tiga tindakan controller dan formulir yang berbeda untuk masing-masing jenis tempat ini. Ingat bahwa kita juga harus mengintegrasikan model terkait, PlaceGPS, untuk menyimpan koordinat GPS untuk setiap tempat, tidak peduli bagaimana pengguna menambahkannya.

Menambahkan Tempat ke Bar Navigasi

Untuk menambahkan tautan Tempat ke panel navigasi, edit /views/layouts/main.php. Ini adalah tata letak halaman default dimana Yii membungkus semua file view kita dengannya. Ini termasuk header, bar navigasi Bootstrap, dan footer.

Di bawah $menuItems, saya menambahkan entri array untuk menu Place:

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
        ?>

The Place Index View

Place Index View akan terlihat seperti ini setelah kita menambahkan tombol untuk tiga cara menambahkan Places:

Meeting Planner Place Index PageMeeting Planner Place Index PageMeeting Planner Place Index Page

Di /views/place/index.php, kita bisa menambahkan tiga tombol add place:

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>

Dan, kita dapat menyesuaikan kolom yang muncul dalam tampilan, termasuk membuat kolom kustom ke metode Place yang menampilkan nama yang ramah untuk Place Type:

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
    ]); ?>

Berikut adalah subset dari metode Place Type di /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
     }      

Perhatikan, kami belum membahas status login atau kepemilikan pengguna dari tempat. Kami akan meninjau kembali ini di tutorial berikutnya. Karena kompleksitas dan cakupan tahap ini, kita akan meninggalkan beberapa item akhir untuk tutorial selanjutnya.

Menambahkan Tempat dengan Geolokasi HTML5

Salah satu skenario untuk menambahkan tempat adalah membuat tempat untuk rumah atau kantor Anda. Daripada mengharuskan pengguna mengetik informasi ini secara langsung, kita sering bisa menghasilkannya secara otomatis dengan Geolokasi HTML5.

Geolokasi HTML5 menggunakan alamat WiFi Anda untuk menentukan titik GPS untuk lokasi Anda saat ini. Ini tidak bekerja dengan koneksi seluler / mobile dan ini tidak mudah.

Pengguna mungkin perlu memberikan izin ke browser mereka untuk geolokasi agar fitur ini dapat berfungsi. Cari popup di bawah bar alamat seperti yang ditunjukkan di bawah ini:

Chrome Browser Asking Permission for GeolocationChrome Browser Asking Permission for GeolocationChrome Browser Asking Permission for Geolocation

Saya menggunakan skrip geoposisi dari estebanav untuk mendukung Geolokasi HTML5 dengan dukungan browser seluas mungkin.

Menambahkan Tindakan Place Controller untuk Geolokasi

Di frontend/controllers/PlaceController.php, kita akan membuat metode baru untuk tindakan 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
         }

Karena formulirnya belum diserahkan, Yii akan membuat tampilan create_geo untuk menampilkan formnya.

Di frontend/views/place/create_geo.php, kita akan menyertakan _formGeolocate.php:

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>

Mari kita lihat bagian pertama dari _formGeolocate. Kita harus menyertakan JavaScript untuk Geoposition.js dan juga kode geolokasi kustom kita sendiri untuk mengintegrasikan geoposisi dengan formulir kita. Cara Yii melakukan ini dengan Asset Bundles. Anda menentukan Asset Bundle untuk halaman yang berbeda dan ini memungkinkan Anda untuk mengoptimalkan JS dan CSS mana yang dimuat di berbagai area aplikasi Anda. Kita akan membuat LocateAsset terlebih dahulu:

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);

Di frontend/assets/LocateAsset.php, kita akan mendefinisikan JavaScript yang perlu kita sertakan:

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 memuat awal Google Maps API, perpustakaan geoPosition dan kode Locate.js kustom kita yang ditunjukkan di bawah ini:

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

Pada dasarnya, geolokasi dimulai saat pengguna memicu beginSearch. Kode Geoposisi memanggil fungsi sukses saat dikembalikan ke lokasi pengguna. Kami menyesuaikan fungsi sukses untuk menampilkan peta di lokasi dan untuk mengisi field formulir yang tersembunyi kami dengan latitude dan longitude yang dikembalikan. Saat pengguna mengirimkan formulir, koordinat lokasi akan tersedia untuk aplikasi Web kami.

Inilah kode dalam Success() yang mengisi field formulir dengan koordinat lokasi:

1
$('#place-lat').val(position.coords.latitude);
2
$('#place-lng').val(position.coords.longitude);

Sisa dari _formGeolocate.php dibagi menjadi dua bagian yang sama. Di sisi kiri, kami menyediakan field formulir agar pengguna bisa memasukkan dengan data Geolokasi dan field tersembunyi yang kami perlukan untuk mendukung JavaScript. Di sisi kanan, kami memberi ruang untuk tombol agar memicu Geolokasi dan untuk menampilkan Peta. Fungsi success() mengisi tag <article> dengan peta.

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
  

Inilah tampilan awal formulirnya:

Meeting Planner Create Place by Geolocation FormMeeting Planner Create Place by Geolocation FormMeeting Planner Create Place by Geolocation Form

Klik pada tombol Lookup Location untuk memulai geolokasi. Sekali lagi, cari permintaan izin di bilah navigasi browser.

Setelah lokasi Anda ditemukan, kami akan menampilkan lokasi Anda di peta:

Meeting Planner Create Place by Geolocation After MappingMeeting Planner Create Place by Geolocation After MappingMeeting Planner Create Place by Geolocation After Mapping

Catatan: Saya telah menetapkan penundaan untuk Geolocation sampai lima detik, namun terkadang Anda harus memuat ulang halaman untuk mendapatkan jawaban yang benar setelah memberikan izin. Lokasi WiFi tertentu kurang ditentukan dibanding yang lain.

Mari kita lihat kode pengiriman formulir Meeting Controller:

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]);

Untuk saat ini, kami hanya menempatkan placeholder untuk pengguna create_by dan meninggalkan penanganan error untuk nanti (maaf para puritan, itu bukan fokus dari tutorial ini saat ini).

Saat formulir mengirim dan Place dibuat, kita akan mengambil titik geolokasi dari formulir (field tersembunyi yang terisi oleh script Locate.js) dan menambahkan baris ke tabel Place yang terkait PlaceGPS.

Seperti yang saya sebutkan di bagian dua, kita memisahkan data geolokasi dalam tabel yang berbeda karena mesin MySQL InnoDB tidak mendukung indeks spasial. Ini juga akan meningkatkan kinerja kueri untuk menemukan tempat pertemuan terdekat di antara dua pengguna.

Inilah metode addGeometryByPoint pada model Place.php:

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
     }

Inilah tampilan halaman Place Index setelah record disimpan:

Meeting Planner Place Index Page with New PlacesMeeting Planner Place Index Page with New PlacesMeeting Planner Place Index Page with New Places

Jika Anda ingin melihat implementasi lain dari Geolokasi HTML5 untuk Yii 1.x, lihat Cara Menggunakan Zillow Neighborhood Maps dan Geolokasi HTML5.

Menampilkan Places di Google Maps

Jika Anda mengklik ikon perintah tampilan yang terkait dengan tempat baru kami dalam tampilan indeks di atas, Anda akan melihat ini:

Meeting Planner View A Place With Google MapsMeeting Planner View A Place With Google MapsMeeting Planner View A Place With Google Maps

Kami telah menyesuaikan tampilan halaman yang dihasilkan Gii dan menambahkan kode untuk menggambar Google Map menggunakan ekstensi Yii2 Google Maps.

Inilah tindakan View di 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
    }

Inilah metode getLocation pada model Place.php. Ini mengambil koordinat lokasi dari tabel PlaceGPS:

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
    }

Berikut adalah sebagian dari file tampilan yang menampilkan halaman. Sisi kiri terdiri dari widget Yii2 DetailView standar untuk saat ini. Sisi kanan menghasilkan kode yang menggambar peta:

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 -->

Menambahkan dari Google Places API

Fitur pelengkapan otomatis Google Places adalah cara yang sangat cepat dan sederhana bagi pengguna untuk menambahkan tempat pertemuan. Saya menggunakan ekstensi Yii2 Google Places oleh 2amigOS.

Meeting Planner The Google Place Autocomplete ServiceMeeting Planner The Google Place Autocomplete ServiceMeeting Planner The Google Place Autocomplete Service

Di PlaceController.php, kami akan menambahkan tindakan untuk Create_place_google:

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
   }

File /frontend/views/place/create_place_google.php akan menampilkan formulir dan menginisialisasi JavaScript yang diperlukan untuk mendukung pelengkapan otomatis:

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
?>

Pengembang Petra Barus menyediakan ekstensi Google Places untuk Yii1.x. Untuk tutorial ini, saya mengkode dukungan dasar untuk Yii2. Namun, Barus cukup baik untuk melepaskan ekstensi Yii2 beberapa hari kemudian. Saya belum mengintegrasikan kodenya. Inilah ekstensi Pelengkapan otomatis Yii2 Google Places terbaru miliknya.

Inilah bundel MapAsset yang saya buat untuk JavaScript terkait yang akan dibutuhkan:

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
}

Berikut adalah kode formulir _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 -->

Ada field searchbox yang akan menerima masukan pelengkapan otomatis pengguna. Ada juga berbagai field tersembunyi yang akan dimuat JavaScript kami dengan hasil dari layanan Google Places.

Inilah create_place.js yang menyelesaikan semua "keajaibannya":

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
}

Metode setupListeners() menghubungkan field searchbox kami ke layanan pelengkapan otomatis Google Places. Saat terjadi peristiwa place_changed, populateResult() dipanggil untuk mengisi field tersembunyi pada formulir dengan data dari Google dan memuat peta yang ditampilkan di sebelah kanan setengah formulir.

Meeting Planner Add From Google Place Autocomplete After LoadMapMeeting Planner Add From Google Place Autocomplete After LoadMapMeeting Planner Add From Google Place Autocomplete After LoadMap

Anda dapat menggunakan debugger browser untuk memeriksa field tersembunyi setelah diisi dengan data formulir melalui JavaScript. Data ini akan diposkan dengan formulir pada saat pengajuan sehingga kami bisa menambahkannya ke database Place.

Meeting Planner Hidden Fields from Google Places AutocompleteMeeting Planner Hidden Fields from Google Places AutocompleteMeeting Planner Hidden Fields from Google Places Autocomplete

Inilah elemen sisa dari tindakan penyimpanan placeController 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]);

Ini sangat mirip dengan tindakan Create_geo. Kami memiliki metode model Place.php terpisah untuk mempermudah pengumpulan data lokasi. Inilah 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
    }

Mengatur Filter Batas Geografis untuk Penelusuran Pelengkapan Otomatis

Layanan Places Autocomplete juga memungkinkan Anda menyiapkan batas persegi panjang geografis untuk memfilter penelusuran Anda. Saat pengguna mulai mengetik, pelengkapan otomatis hanya akan menggunakan tempat dalam jarak sepuluh mil dari mereka. Karena kita belum mengatur lokasi pengguna saat ini sebagai variabel sesi, saya tidak menerapkan batas persegi panjang saat ini. Tapi kita bisa melakukannya nanti. Berikut adalah contoh 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
}

Menambahkan Tempat Secara Manual

Cara ketiga pengguna bisa menambahkan tempat adalah dengan secara manual memberikan informasi detail dan alamat. Saat mereka mengirimkan formulir, kita akan mencoba mencari alamat dan mendapatkan data geolokasi, tapi tidak masalah jika kita tidak dapat menemukannya. Pendekatan manual akan memungkinkan pengguna menambahkan tempat seperti rumah atau kantor mereka yang mungkin tidak ingin dikaitkan dengan data pemetaan Google.

Inilah formulirnya:

Meeting Planner Manually Add PlaceMeeting Planner Manually Add PlaceMeeting Planner Manually Add Place

Inilah kode tindakan pengiriman PlaceController.php. Kita menggunakan klien 2Amigos Maps Geocoding untuk mencari lokasi dari full_address. Jelas ada banyak perbaikan yang dapat kita lakukan untuk mendorong pengguna memasukkan alamat lengkap atau mungkin mengizinkan mereka menghubungkan lokasi Google Places di kemudian hari.

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
        }

Apa berikutnya?

Ruang lingkup tutorial ini terbukti cukup besar. Saya ingin menunjukkan kepada Anda berbagai komponen yang terlibat dalam penggunaan geolokasi dan pemetaan tanpa melewatkan terlalu banyak elemen proses pengkodean. Jadi, tentu saja, ada banyak cara pintas saat ini. Pada tutorial berikutnya, kami akan terus menyempurnakan Places dalam keseluruhan sistem, berfokus pada izin pengguna, kontrol akses, menambahkan dukungan untuk tempat favorit pengguna, dan penyempurnaan lainnya.

Silahkan kirimkan pertanyaan dan komentar Anda di bawah ini. Saya sangat tertarik jika Anda memiliki pendekatan atau ide tambahan yang berbeda, atau ingin menyarankan topik untuk tutorial masa depan. Anda juga bisa menghubungi saya di Twitter @reifman atau email saya langsung. Ikuti halaman instruktur Tuts+ saya untuk melihat artikel masa depan dalam serial ini.

Tautan Terkait