laravel学んで2か月で自サービスを開発した話 Part7

皆さんこんにちは　こうも開発記録頑張るぞい

今までの開発記録はこちらへ
<a href="https://qiita.com/k-hayack884/items/e571138e8bf7f35f7502">胡蝶蘭を捨てるくらいならワイが欲しいので、サービス開発する編</a>
<a href="https://qiita.com/k-hayack884/items/720c7a6300139317deac">公式ドキュメントの言う通り、パッケージをインストールされたら、Inertia.jsが導入されて？？？になった編</a>
<a href="https://qiita.com/k-hayack884/items/dccb9d479bd162144559">マルチログインを作ってみた編</a>
<a href="https://qiita.com/k-hayack884/items/0354c0ddecee58272e46">デザインをtailwindcssに丸投げする編</a>
<a href="https://qiita.com/k-hayack884/items/e4c691041373a97afd97">デザイナーに怒られないために、画像をリサイズする編</a>
<a href="https://qiita.com/k-hayack884/items/ab8ddbb97b035c55fd1b">AWS×リボ払いで破産へGO編</a>

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323238313934362f30396630376266302d323930352d643666652d366366632d6138373864653736386438342e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/75ebac46-14b2-9abc-7326-a0f4bf61208f.png)

# 今回やること
- リレーションの設定
- カテゴリーをseederを作って作成
- googlemap　apiを使う

## 商品登録機能の開発
とはいっても、ユーザー登録機能とほとんどやることは一緒
ただし、追加で実装しないといけないのが
### ユーザーと商品を紐づけるリレーションを実装すること

私がデータベースを使ってとても苦戦したのがリレーションである。
データベース同士の関係によって、**一対一、一体多、多対多**という関係性がある

というわけで自分なりに理解したことを大学の例え話を使って解説する。

## 一対一
一対一の場合、学生証のように一人につき一枚しかもらえないものが該当する。一人の学生が別々の学生証を２枚持っていることはあってはいけないからである。
![一対一.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/f3fb0190-1724-ad9b-e826-da6a696aef62.png)

## 一対多
一体多は片方は一つしかもっていないのに対し、もう片方は多数を持つことができることを意味する。学部のように、学生は一つの学部にしか入れないのに対し、学部は多数の学生を持っているということだ。
![一対多.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/024c3bf9-be6f-2c12-88f8-55289604ea56.png)
## 多対多
こちらを理解するのに相当苦労したが、大学のサークルをイメージするようにしたらしっくりくるようになった。
サークルは複数の学生を保持している一方、学生もサークルを掛け持ちができるので、どちら側も複数の要素を持つことになる
![多対多.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/6a1655d7-78b9-059e-5b90-9ef3296c202f.png)
多対多の場合２つのデータベースで表現することはできないので、その間に中間テーブルというテーブルを作って表現する

これで一通りはイメージをつかめたはずなので、実際にプログラムしていこうと思う

## 外部キーに要注意
前回のトランザクションでもあった話だが、ユーザーが削除されたとき、先にそのユーザーが出品した商品を削除しなければならない。
そこを注意して外部キーを設定していく。
```create_products_table.php
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')
            ->constrained()
            ->onUpdate('cascade')
            ->onDelete('cascade');
            $table->foreignId('secondary_category_id')
            ->constrained();
            $table->string('img')->nullable();
            $table->string('name');
            $table->string('comment');
            $table->string('address')->nullable();
            $table->integer('trade_type');
            $table->timestamps();
        });
    }

```
今回はユーザーテーブルとつなぐuse_idとカテゴリーテーブルとつなぐsecondary_category_id'に外部キーで紐づけている
違いは、```('cascade')```があるかないか。
('cascade')をつけることによって、ユーザーテーブルで何かしらの変更があった場合、先に商品テーブルを参照して処理してくれる。
これで、ユーザーを削除しても、そのユーザーが出品している商品が残り続けるとことはない。
一方で、カテゴリーはユーザーが削除しても、カテゴリーの内容を変更するわけではないので、こちらはそのままである。

このようにマイグレーションファイルに書いていったが、これだけではデータベース同士がつながっていない。
というわけで今度はモデルファイルでリレーションをする
```User.php
    public function product()
    {
        return $this->hasMany(Product::class);
    }
```
ユーザーテーブルと商品テーブルの関係性は、ユーザーが多数の商品を持っているということから**一体多**で設定する
商品テーブル側も設定、ついでに、データベース登録を許可する設定も書いておく。
```Product.php
    protected $fillable = [
        'user_id',
        'secondary_category_id',
        'name',
        'comment',
        'address',
        'trade_type',
        'img',
    ];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
```
これでリレーションの設定は完了。

## カテゴリーの作成
ECサイトには膨大な商品が出品されていて、一つ一つ探すには膨大な手間がかかる。その時間を少しでも減らすために使われているのがタグやカテゴリーである。
このサイトもいろんな種類の花が出品されることが予想されるため~~未経験個人開発のサイトなんか誰も来ねえよ~~、利便性の向上のためにカテゴリーで検索できるようにする。

ということで、まずはリレーション
![155475282-e1024968-bf43-4233-b9cb-f9208f691350.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/24856e90-3f21-4f91-3606-35c2c921edcb.png)
かなりシンプルです
商品を抽象化して、小カテゴリー、さらに抽象化して、大カテゴリーとなっている。
**大カテゴリー　一体多　小カテゴリー　一体多　　商品**
にすれはOK
```Product.php
    public function category()
    {
        return $this->belongsTo(SecondaryCategory::class, 'secondary_category_id');
    }
```
``` SecondaryCategory.php
class SecondaryCategory extends Model
{
    use HasFactory;
    public function secondary()
    {
        return $this->belongsTo(PrimaryCategory::class);
    }
}

```
```PrimaryCategory.php
class PrimaryCategory extends Model
{
    use HasFactory;
    public function secondary()
    {
        return $this->hasMany(SecondaryCategory::class);
    }
}
```
### カテゴリーデータの作成
カテゴリー名はこちらで決めてあるのででSeederを使う。
Seederはダミーデータの作成にも使えるため、何度も登録するのが面倒な人は活用してもいいと思う。
まずはdatabase\seedersにCategorySeeder.phpを作成
```CategorySeeder.php
namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class CategorySeeder extends Seeder
{
    public function run()
    {
        DB::table('primary_categories')->insert([
            ['id'=>1,
                'name' => '洋ラン',
                'sort_order' => 1,
            ],
            ['id'=>2,
                'name' => 'アジサイ',
                'sort_order' => 2,
            ],
            ['id'=>3,
                'name' => 'シクラメン',
                'sort_order' => 3,
            ],
            ['id'=>4,
                'name' => '観葉植物',
                'sort_order' => 4,
            ],
            ['id'=>5,
                'name' => '多肉植物',
                'sort_order' => 5,
            ],
            ['id'=>6,
                'name' => '球根植物',
                'sort_order' => 6,
            ],
            ['id'=>7,
                'name' => 'フラワースタンド',
                'sort_order' => 7,
            ],
            ['id'=>8,
                'name' => 'その他',
                'sort_order' => 8,
            ],

        ]);
        DB::table('secondary_categories')->insert([
            [
                'name' => '胡蝶蘭',
                'sort_order' => 1,
                'primary_category_id' => 1,
            ],
            [
                'name' => 'シンビジューム',
                'sort_order' => 2,
                'primary_category_id' => 1
            ],
            [
                'name' => 'カトレア',
                'sort_order' => 3,
                'primary_category_id' => 1
            ],
            [
                'name' => 'その他洋ラン',
                'sort_order' => 4,
                'primary_category_id' => 1
            ],
            [
                'name' => 'アジサイ',
                'sort_order' => 5,
                'primary_category_id' => 2
            ],
            [
                'name' => 'シクラメン',
                'sort_order' => 6,
                'primary_category_id' => 3
            ],
            [
                'name' => 'ガーデンシクラメン',
                'sort_order' => 7,
                'primary_category_id' => 3
            ],
            [
                'name' => 'アンスリウム',
                'sort_order' => 8,
                'primary_category_id' => 4
            ],
            [
                'name' => 'モンステラ',
                'sort_order' => 9,
                'primary_category_id' => 4
            ],
            [
                'name' => 'ガジュマル',
                'sort_order' => 10,
                'primary_category_id' => 4
            ],
            [
                'name' => 'パキラ',
                'sort_order' => 11,
                'primary_category_id' => 4
            ],
            [
                'name' => 'その他観葉植物',
                'sort_order' => 12,
                'primary_category_id' => 4
            ],
            [
                'name' => 'サボテン',
                'sort_order' => 13,
                'primary_category_id' => 5
            ],
            [
                'name' => 'カランコエ',
                'sort_order' => 14,
                'primary_category_id' => 5
            ],
            [
                'name' => 'その他多肉植物',
                'sort_order' => 15,
                'primary_category_id' => 5
            ],
            [
                'name' => 'チューリップ',
                'sort_order' => 16,
                'primary_category_id' => 6
            ],
            [
                'name' => 'カラー',
                'sort_order' => 17,
                'primary_category_id' => 6
            ],
            [
                'name' => 'ヒヤシンス',
                'sort_order' => 18,
                'primary_category_id' => 6
            ],
            [
                'name' => 'その他球根植物',
                'sort_order' => 19,
                'primary_category_id' => 6
            ],
            [
                'name' => '寄せ植え',
                'sort_order' => 20,
                'primary_category_id' => 7
            ],
            [
                'name' => 'その他',
                'sort_order' => 21,
                'primary_category_id' => 8
            ],
        ]);
    }
}
```
その後、最初からあるDatabaseSeeder.phpに
```DatabaseSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call([ 
            CategorySeeder::class,　//追加
        ]);     
    }
}
```
あとはコマンドでphp artisan db:seedを入れると
![FireShot Capture 003 - localhost _ 127.0.0.1 _ flower-gift _ secondary_categories - phpMyAdm_ - localhost.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/e93ffb91-7e59-5aed-da9b-4fc61ed3405c.png)
### やったぜ

## ルートとコントローラーとビュー
ほぼプロフィール機能の使いまわしのため割愛。
追加機能の部分はのちにまとめてやる予定です。

## GoogleMap 
皆さん突然ですが、ジモティーを使ったことはありますか？
私はあります！
ジモティーといえばユーザー同士が待ち合わせ場所で直接会って取引できますよね。例えば、こんな感じです。
![ジモティー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/3635e557-39fe-f222-ccb5-7d4bc0f149ff.png)

![business_man3_1_question.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/1875ae26-279b-efa3-03d0-5ae26edd05c6.png)
#### でも、国分寺ってどこがわかりませんよね？~~エンジニアの大半は東京在住だからわかるだろ~~
![necchusyou_face_boy1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/bc6f1c57-08ea-5214-dbf5-3885d75cd8c8.png)
#### ということで、地図をつければ親切じゃないか！

### google map APIの仕様手順
<a href="https://qiita.com/Haruka-Ogawa/items/997401a2edcd20e61037">Google Maps API を使ってみた</a>
APIの取得までの手順はこちらを参照してください

### コードを書きます。
ビュー側で設定していきます
```show.blade.php
<script src="https://maps.googleapis.com/maps/api/js?key=（APIキー）&callback=initMapWithAddress" async defer>
//MAPを挿入したい所に挿入

//略
<script>
    var _my_address = '{{ $productInfo->address }}';

    function initMapWithAddress() {
        var opts = {
            zoom: 15,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
        };
        var my_google_map = new google.maps.Map(document.getElementById('my_map'), opts);
        var geocoder = new google.maps.Geocoder();
        geocoder.geocode({
                'address': _my_address,
                'region': 'jp'
            },
            function(result, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    var latlng = result[0].geometry.location;
                    my_google_map.setCenter(latlng);
                    var marker = new google.maps.Marker({
                        position: latlng,
                        map: my_google_map,
                        title: latlng.toString(),
                        draggable: true
                    });
                    google.maps.event.addListener(marker, 'dragend', function(event) {
                        marker.setTitle(event.latLng.toString());
                    });
                }
            }
        );
    }
</script>
```
**これで、マップを挿入する準備ができました！
早速見てみましょう**
![necchusyou_face_boy1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/8503176d-f31c-803c-de73-55485c434fe8.png)
#### どんな場所が表示されるかな　わくわく
![google.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/084ec107-5d24-29f3-9c26-2ff67afb3393.png)
![face_angry_man5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/e18639ee-c2ba-ef3b-294a-469cf8268736.png)
### 誰だよ！取引先を無人島に設定したやつ！！

おわり