[rails]Googlemap APIを使ってプチ食べログを作りたいのでテスト環境で途中まで実装してみた！(Googlemap、店舗検索編)

https://qiita.com/_soratanaka_/items/e751ec3a78db61f604e6

新しい記事を投稿しました。
コードやGooglemapのページを大きく変更しておりますので、もし参考にしようとしている方が万が一いらっしゃいましたら、お気をつけください。

## [やりたいこと]
ポートフォリオ用に、食べログのラーメン屋さん専門みたいなのを作ろうかなと
やろうとしている機能は
○Device機能
☆GooglemapAPI(今回の主軸)
○多対多（アソシエーション、フォロー機能）
○AWS
などです。

 ## [注意]
かなり行き当たりばったりな記事です。
先にサンプルファイルで機能実装テストが完了してから本番ファイルで実装する予定です。
こちらのテストではユーザー関連は実装しておりませんので
後々、他のgemなどの影響で書き方など変わる可能性があります。ご承知ください。


 ## [環境]
※使っていない可能性のある環境も記載してます。
Mac M1チップ
rails 6.1.6
ruby 3.0.1
node v16.17.1


## [手順]
rails s辺りは省略します。
また、DB関連もですが、今回はテスト環境ですので、仮になります。
おそらく最低限[latitude][longitude]があるといいはずです。

今回は
```schema.rb
  create_table "shops", force: :cascade do |t|
    t.string "name"
    t.string "address"
    t.float "latitude"
    t.float "longitude"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
```
になっております。

というわけで、早速テスト
googlemapAPIは

https://qiita.com/Haruka-Ogawa/items/997401a2edcd20e61037

↑こちらを参考に取得させていただきました(この場を借りて感謝いたします！)

テストの場所は
shop/index
をテスト場所にしております。

というわけで、とりあえずいったんコードを記載
参考Qiita記事↓(こちらもこの場を借りて感謝いたします！)

https://qiita.com/japwork/items/1d01663b462264ab92a9

```:Gemfile
[省略]
gem 'dotenv-rails'
gem 'google_places'
gem 'gmaps4rails'
gem 'geocoder'
gem 'gon'
gem 'jquery-rails'
```

```app/views/shops/index.html.erb
<%= javascript_pack_tag 'shop/index' %>

<h2>gmap</h2>

  <%= form_with url: shops_path, local: true, method: :get do |f| %>
    <%= f.text_field :word , name: "search"%>
    <%= f.submit "検索", class: "btn btn-primary" %>
  <% end %>

<div id='map'></div>

<style>
#map {
  height: 600px;
  width: 600px;
}
</style>

<script>
let map
let geocoder
var marker = [];
var infoWindow = [];
var allShops = gon.places;
var markerData = [] 
const display = document.getElementById('display')
  for (var i = 0; i < allShops.length; i++) { 
    markerData.push({
    title: allShops[i]['name'],
    lat: allShops[i]['lat'],
    lng: allShops[i]['lng'],
    content: allShops[i]['name'] + '<br><img src="./ippatsu.png" width="200"><br>'
    });
  };

function initMap(){
  geocoder = new google.maps.Geocoder()
  var mapLatLng = new google.maps.LatLng({lat: 35.6809591, lng: 139.7673068});
  map = new google.maps.Map(document.getElementById('map'), {
  center: mapLatLng, // 地図の中心座標を指定
  zoom: 13, // 地図のデフォルトのズーム指定
  });

  for (var i = 0; i < markerData.length; i++) {
  // 緯度経度のデータを作成
    markerLatLng = new google.maps.LatLng({lat: markerData[i]['lat'], lng: markerData[i]['lng']});
    marker[i] = new google.maps.Marker({
      position: markerLatLng, // マーカーを立てる位置を指定
      map: map, // マーカーを立てる地図を指定
      title: markerData[i]['title'] // ツールヒント
      });
      
    // 吹き出しの設定
    infoWindow[i] = new google.maps.InfoWindow({
      // 吹き出しに表示する内容を設定する
      content: markerData[i]['content']
      });
      
    // マーカーにクリックイベントを追加
    markerEvent(i);
    }
  }

  function markerEvent(i) {
    marker[i].addListener('click', function() {
      // 吹き出しの表示
      infoWindow[i].open(map, marker[i]);
  });
}

</script>
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script>
```

```app/controllers/shops_controller.rb
class ShopsController < ApplicationController
  def index
    if params[:search].nil?
      @client = ::GooglePlaces::Client.new(ENV['GOOGLE_MAP_API'])
      @places = @client.spots_by_query("ラーメン", language: 'ja', types: 'food')
      gon.places = @places
    else
    @client = ::GooglePlaces::Client.new(ENV['GOOGLE_MAP_API'])
    @places = @client.spots_by_query("#{params[:search]},ラーメン", language: 'ja', types: 'food')
    gon.places = @places
    end
  end
```

また、こちらには記載しておりませんが、envファイルにgooglemapAPIから取得したキーを記載しております。
なんのこと?という方は後に説明します。



 ## [解説]※気にしない方は飛ばしても構いません。
では、初心者なりにそれぞれ解説していきます！

```:Gemfile
[省略]
gem 'dotenv-rails'　 #railsでenvファイルを使えるようになるgem。
gem 'google_places' #google_placesを使用する際に必要なgem。
gem 'gmaps4rails'   #RailsでGoogleMapを表示させるのに必要らしい…が、使ってない記事もあったのでもしかしたらいらない可能性あり
gem 'geocoder'      #住所から緯度経度を出してくれるGooglemap機能を使うために必要。今回は使っていないけど後々使う可能性もあり
gem 'gon'           #railsの変数をJSで使うために必要なgem。
gem 'jquery-rails'  #JQueryを使うなら必要。正直どこで使っているか曖昧なので勉強中。
```
ここは内容はそのままなので割愛。

```app/views/shops/index.html.erb
<%= javascript_pack_tag 'shop/index' %>

<h2>gmap</h2>

    <%= form_with url: shops_path, local: true, method: :get do |f| %>
      <%= f.text_field :word , name: "search"%>
      <%= f.submit "検索", class: "btn btn-primary" %>
    <% end %>

<div id='map'></div>

```
ここら辺はrailsの基本なのであまり説明はしませんが
```
<%= javascript_pack_tag 'shop/index' %>
```
は後々JSファイルを別に分ける際に使用予定。現在は見やすくするために一緒のファイルにしてます。
```
    <%= form_with url: shops_path, local: true, method: :get do |f| %>
      <%= f.text_field :word , name: "search"%>
      <%= f.submit "検索", class: "btn btn-primary" %>
    <% end %>
```
は入力フォーム。

```
<div id='map'></div>
```
でJSを使用してマップを表示させております。



```app/views/shops/index.html.erb
<style>
#map {
  height: 600px;
  width: 600px;
}
</style>

```

CSSです。大嫌いです。


JSに入る前に先にコントローラーの説明をします。
```app/controllers/shops_controller.rb
class ShopsController < ApplicationController
  def index
    if params[:search].nil?
      @client = ::GooglePlaces::Client.new(ENV['GOOGLE_MAP_API'])
      @places = @client.spots_by_query("ラーメン", language: 'ja', types: 'food')
      gon.places = @places
    else
    @client = ::GooglePlaces::Client.new(ENV['GOOGLE_MAP_API'])
    @places = @client.spots_by_query("#{params[:search]},ラーメン", language: 'ja', types: 'food')
    gon.places = @places
    end
  end
```

今回はindexへの表示テストなのでindexだけにしております。

まずは最初の条件から
```app/controllers/shops_controller.rb
    if params[:search].nil?
      @client = ::GooglePlaces::Client.new(ENV['GOOGLE_MAP_API'])
      @places = @client.spots_by_query("ラーメン", language: 'ja', types: 'food')
      gon.places = @places
```
こちらは検索されていない場合(初期値)の条件
```app/controllers/shops_controller.rb
    if params[:search].nil?
```
で条件分けしてます。

次からが割と本題
```app/controllers/shops_controller.rb
@client = ::GooglePlaces::Client.new(ENV['GOOGLE_MAP_API'])
```
そもそもの話ですが、google placesというのはgoogle様から”施設などの情報を使用”させていただくサービスです。
railsではそちらの情報が入った変数、インスタンス化して使用することができます。
よくわからないという方がいらっしゃいましたら、まぁ施設情報が記載したデータを原本からコピーしたものをいただくコードだと思ってください。

そして、先ほど飛ばした
```app/controllers/shops_controller.rb
(ENV['GOOGLE_MAP_API'])
```
に関してですが、ぶっちゃけここで説明するには長くなりすぎるくらい長くなるので、参考記事を記載します。(感謝！)

https://qiita.com/harutakaoka522/items/e0abddfd0311eb4fb32b

読むのめんどくさいという方用に一言で書くと、漏れると困る情報をまとめる場所です。
ですので、もし作成した場合は絶対にgitにあげないようにしましょう。
というか、しっかり調べてから使いましょう。これはマジで

```app/controllers/shops_controller.rb
      @places = @client.spots_by_query("ラーメン", language: 'ja', types: 'food')
```

ここが検索のキモです。
```app/controllers/shops_controller.rb
      .spots_by_query()
```
これで検索の定義をします。
これに関して詳細を知りたい！という方がいらっしゃいましたら、このコードを検索することをお勧めいたします。
一応、このgemのGit先を記載しておきますので、参考に(感謝！)

https://github.com/qpowell/google_places

一応、このページでの使い方を記載すると
```
("ラーメン", language: 'ja', types: 'food')
```
手前から
”検索ワード”
”言語設定”
”タグ”
という認識でいいかと思います。

また、注意事項として
デフォルトで20件しか取得してきません！！！！！←注意
また、設定を変えるの結構だるそうだし、最大60件までしか取得できません！！！！！←これで諦めました。
それでも！という方は調べてください。


そして最後に
```app/controllers/shops_controller.rb
      gon.places = @places
```
こちらは、先ほどgemファイルに記載した
```:Gemfile
gem 'gon'           #railsの変数をJSで使うために必要なgem。
```
こちらを使用する宣言みたいなものです。

条件分岐の後半はこちらの繰り返しなので割愛。


では最後にJSの部分
```app/views/shops/index.html.erb
<script>
let map
let geocoder
var marker = [];
var infoWindow = [];
var allShops = gon.places;
var markerData = [] 
const display = document.getElementById('display')
  for (var i = 0; i < allShops.length; i++) { 
    markerData.push({
    title: allShops[i]['name'],
    lat: allShops[i]['lat'],
    lng: allShops[i]['lng'],
    content: allShops[i]['name'] + '<br><img src="./ippatsu.png" width="200"><br>'
    });
  };

function initMap(){
  geocoder = new google.maps.Geocoder()
  var mapLatLng = new google.maps.LatLng({lat: 35.6809591, lng: 139.7673068});
  map = new google.maps.Map(document.getElementById('map'), {
  center: mapLatLng, // 地図の中心座標を指定
  zoom: 13, // 地図のデフォルトのズーム指定
  });

  for (var i = 0; i < markerData.length; i++) {
  // 緯度経度のデータを作成
    markerLatLng = new google.maps.LatLng({lat: markerData[i]['lat'], lng: markerData[i]['lng']});
    marker[i] = new google.maps.Marker({
      position: markerLatLng, // マーカーを立てる位置を指定
      map: map, // マーカーを立てる地図を指定
      title: markerData[i]['title'] // ツールヒント
      });
      
    // 吹き出しの設定
    infoWindow[i] = new google.maps.InfoWindow({
      // 吹き出しに表示する内容を設定する
      content: markerData[i]['content']
      });
      
    // マーカーにクリックイベントを追加
    markerEvent(i);
    }
  }

  function markerEvent(i) {
    marker[i].addListener('click', function() {
      // 吹き出しの表示
      infoWindow[i].open(map, marker[i]);
  });
}

</script>
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GOOGLE_MAP_API'] %>&callback=initMap" async defer></script>
```
正直、私はRailsをメインに勉強していたので、JSはまだ一週間も勉強しておりません。
ですので、ちゃんと解説できません！
(実際、このコードはほとんど全ていろんなサイトからコードをパクリにパクったキメラです)

つまり、実際に書いたコード以外は正直「へー」とか思いながらコピったものばかりなので、申し訳ないですがかなり簡潔に説明します。
```
let map
let geocoder
var marker = [];
var infoWindow = [];
var allShops = gon.places;
var markerData = [] 
const display = document.getElementById('display')
```
変数宣言です。
JSはRubyと違って変数の宣言が必要です。これはJSの特徴というよりも
”Rubyが宣言を必要としない”ことが特徴ですので、知らなかった人は頭の片隅に入れておいてください。

```
  for (var i = 0; i < allShops.length; i++) { 
    markerData.push({
    title: allShops[i]['name'],
    lat: allShops[i]['lat'],
    lng: allShops[i]['lng'],
    content: allShops[i]['name'] + '<br><img src="./ippatsu.png" width="200"><br>'
    });
  };
```
コントローラーで検索したお店の名前、緯度経度をハッシュの配列にしました。
参考サイトが全く見つからなかったので、ここは作りました。ドヤりたいけど見直したら普通のコードな気がする。
なお、吹き出しの内容を変えたい場合は
```
content: allShops[i]['name'] + '<br><img src="./ippatsu.png" width="200"><br>'
```
を変えたら変えれるはずです。

```
function initMap(){
  geocoder = new google.maps.Geocoder()
  var mapLatLng = new google.maps.LatLng({lat: 35.6809591, lng: 139.7673068});
  map = new google.maps.Map(document.getElementById('map'), {
  center: mapLatLng, // 地図の中心座標を指定
  zoom: 13, // 地図のデフォルトのズーム指定
  });
```
初期値です。
東京駅に設定しています。
変更したい方は

```
  var mapLatLng = new google.maps.LatLng({lat: 35.6809591, lng: 139.7673068});
```
の数値を変えたい場所の緯度経度に変更すれば変えれるはずです。
違っても責任は取りません。

```
for (var i = 0; i < markerData.length; i++) {
  // 緯度経度のデータを作成
    markerLatLng = new google.maps.LatLng({lat: markerData[i]['lat'], lng: markerData[i]['lng']});
    marker[i] = new google.maps.Marker({
      position: markerLatLng, // マーカーを立てる位置を指定
      map: map, // マーカーを立てる地図を指定
      title: markerData[i]['title'] // ツールヒント
      });
      
    // 吹き出しの設定
    infoWindow[i] = new google.maps.InfoWindow({
      // 吹き出しに表示する内容を設定する
      content: markerData[i]['content']
      });
      
    // マーカーにクリックイベントを追加
    markerEvent(i);
    }
  }
```
マーカーと吹き出しの定義をしております。

```
  function markerEvent(i) {
    marker[i].addListener('click', function() {
      // 吹き出しの表示
      infoWindow[i].open(map, marker[i]);
  });
```
マーカーをクリックすると吹き出しが表示される設定をしております。

以上！JSの内容が薄くて申し訳ないですが、まぁ間違ったことを書く方が悪いと思い書きませんでした。


## [実際の表示]
![スクリーンショット 2022-11-28 1.24.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2881851/52ab928f-e1e7-7eff-3992-cd1a86e16631.png)



スクショで拡大しておりますので、実際にはもっと小さいです。

以上です。

次回は、吹き出しに他ページへのリンクを出すようにする設定でも入れようかな。とか考えてます。
(書くかは未定ですが)