iPhoneのショートカットに関するポートフォリオを作成しました【Laravel /Vue.js/ Docker / AWS / CircleCI】

## はじめに
### 記事の目的
ポートフォリオとして作成したWEBアプリケーションの紹介記事です。
iPhoneのショートカット機能に関するWEBアプリケーションを作成しました。
https://shortcutrecipe.herokuapp.com/recipeList
(↓途中からiPhoneのショートカットアプリに飛んでいます）
<a href="https://gyazo.com/399c4ab0c6445b3c9260ddfa1fc592e7"><img src="https://i.gyazo.com/399c4ab0c6445b3c9260ddfa1fc592e7.gif" alt="Image from Gyazo" width="350"/></a>
### 自己紹介
- エンジニア歴1年
- 好きなものはiPhoneのショートカットとNotion
- 課題解決をしたり便利なものを作ったり伝えたりする仕事がしたいため、エンジニアになる

### 目次
[1. 作成したアプリについて【 概要と機能 】](#1-作成したアプリについて概要と機能)
[2. 作成したアプリについて 【技術】](#2-作成したアプリについて-技術)


# 1. 作成したアプリについて【概要と機能】
目次は以下の通りです。
[1. アプリの概要](#1-アプリの概要)
[2. 特徴](#2-特徴)
[3. 機能一覧](#3-機能一覧)


## (1) アプリの概要
### アプリ名　Shortcut Recipe
iPhoneやiPadには複数の機能や操作を自動化する "ショートカット"という機能があります。
ショートカットを使うと、例えば壁紙を1タップで変更したり、 決済アプリを一覧から表示したりすることができます。
"Shortcut Recipe"では様々な"ショートカット"を 簡単にダウンロードしたり、作ったショートカットを投稿したりすることができます。

### ショートカットの例
**決済アプリ一覧表示**
決済アプリを一覧から簡単に表示させることができます。
レジの前でアプリを探す手間が省けます。
<a href="https://gyazo.com/e056dd8644f198a484181ed4fa7c18d3"><img src="https://i.gyazo.com/e056dd8644f198a484181ed4fa7c18d3.gif" alt="Image from Gyazo" width="350"/></a>

**壁紙変更**
タップ1回のみで、特定のフォルダの写真からランダムに壁紙を変更できます。
1時間ごとに壁紙を変更する設定も可能です。
<a href="https://gyazo.com/9013972e77578864be3c93f6fe6c52cd"><img src="https://i.gyazo.com/9013972e77578864be3c93f6fe6c52cd.gif" alt="Image from Gyazo" width="350"/></a>
### 作成理由
私は日常のちょっとした手間を効率化することが好きなので、iPhoneを効率化できるショートカット機能にハマりました。
自分でショートカットを作ったり、ネット上のショートカットを入手したりしてiPhoneの操作を便利にしていくことが好きでした。

他の人が作ったショートカットを探すときはガジェット系ブログを参考にするのですが、ショートカットを自由に投稿・検索できるサイトがないことに気がつきました。自慢のショートカットを共有したり、ショートカットを効率的に探したりできるWEBアプリがあれば非常に便利だと思ったことが、作成したきっかけです。

## (2) 特徴
主な特徴は以下の通りです。
### ショートカット一覧
トップページからショートカットの一覧を確認することができます。
<img width="300" alt="ショートカット一覧" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545959/8123f98e-50d8-a498-603f-585d8b0cd492.png">

### ショートカット詳細
詳細ボタンを押すとモーダルで詳細画面が表示されます。
ショートカットの紹介文や参考サイト、作成者情報、使用するApple製品、使用するアプリが表示されます。
GETボタンを押すとショートカットをダウンロードするためのリンクが表示されます。
<img width="300" alt="ショートカット一覧" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545959/0045ac75-3a5b-c044-2925-1615cb5f53d3.png">
### 検索機能
検索機能を実装しています。
例えば「バッテリー」と検索するとバッテリー節約に関するショートカットが表示されます。
<img width="300" alt="ショートカット一覧" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545959/be57c72e-40ca-79cd-46aa-d0ea9f28506f.png">
### フィルター機能
フィルター機能を実装しています。
「使用するApple製品」や「使用するアプリ」からショートカットを絞り込むことができます。
<img width="300" alt="ショートカット一覧" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545959/8dbdf8e5-12d0-4d42-b89e-2fa854bc0409.png">
### ショートカット追加
ショートカットの追加画面です。
便利なショートカットを他の人にも紹介したい場合は投稿することができます。
必須項目は「ショートカットのレシピ名」と「ショートカットのiCloudリンク」のみです。
※ショートカットのiCloudリンクはアプリから簡単に調べることができます。
<img width="300" alt="ショートカット一覧" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545959/5a6d7893-c409-0125-48a3-d781d39e7dac.png">

## (3) 機能一覧
機能は以下の通りです。
- 新規ユーザー登録
- ログイン
- ログアウト
- パスワードリマインダー（Gmail使用）
- プロフィール変更
- 退会
- ショートカット登録
- ショートカット編集・削除
- ショートカット一覧表示・検索・フィルター
- ページネーション

# 2. 作成したアプリについて 【技術】
目次は以下の通りです。
[1. 使用技術](#1使用技術)
[2. インフラ構成図](#2インフラ構成図)
[3. DB設計](#3db設計)
[4. 工夫したところ（実装面）](#4-工夫したところ実装面)
[5. 工夫したところ（機能デザイン面）](#5-工夫したところ機能デザイン面)
[6. 苦労したところ](#6-苦労したところ)

## (1)使用技術
### フロントエンド
- Vue.js 2.6.12
- Vuetify 2.2.26

以下の理由によりjQueryではなくVue.jsを採用しました。

- DOMの操作がしやすいため
- scriptの記述が少なく、可読性が上がるため

デザインには自信がないため機能面の実装に集中できるように、比較的簡単にUI/UXの高いデザインが実装できるVuetifyを採用しました。

### バックエンド
- PHP 7.3.24
- Laravel 5.8.38
- MySQL　8.0.23
- composer
- PHPUnit

使用・学習理由は以下の通りです。

**PHP**
- WEBアプリ開発やWordpressなど様々な用途で使われるため汎用性が高い
- フルスクラッチ開発が可能である
 
**Laravel**
- 現在PHPで最も人気なフレームワーク
- 求人数がかなり多い
- 参考サイトが多数あるため学習しやすい

### インフラ
- MAMP 6.3 (初期開発環境）
- Docker 20.10.7 /Docker Compose 1.29.2（開発環境）
- AWS(EC2,ACM,RDS,Route53,VPC,EIP)
- Apache
- CircleCI 2.1
（現在はherokuにデプロイしています）

初めは開発環境としてMAMPを使用していましたが、Dockerを導入しました。
使用・学習理由は以下の通りです。

**Docker**
- 開発環境をチームで揃えやすいという点に惹かれた
- 現場でも使用する機会が多いと耳にしたため、少しだけでも勉強したいと思ったため

**AWS**
- 職場でもサーバーをオンプレミスからクラウドに移行を検討しており、興味があったため
- 様々な企業の求人情報を確認したところインフラとして活用している企業が多かったため

**CI/CD**
- 本番環境へのデプロイ作業の手間をなくしたかったため

### その他
- Git/GitHub
- VSCode
- Prettier、ESLint
- Notion（メモ用）
- AdobeXD（デザイン用）
- Draw.io（インフラ図作成）

## (2)インフラ構成図
インフラ構成図に、自動デプロイの流れを書き足したものになります。
1~4までを手動で行うことで、5~11が自動デプロイされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545959/ab2c3e60-88d7-d0ca-c197-06fa727378c9.png)

## (3)DB設計
ER図は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545959/75169eff-d2d5-51b0-d8cf-1fbfefd9d312.png)

ER図のテーブルについて、簡単な説明です。

| テーブル名 | 説明 |
| --- | --- |
| Users | ユーザー情報の管理 |
| Recipes | ショートカット（レシピ）の管理 |
| Products | ショートカットに必要なApple製品の管理 |
| Applications | ショートカットに必要なアプリの管理 |
| Recipes_relation_products(*) | 「どのショートカット（レシピ）には「どのApple製品」が必要か |
| Recipes_relation_Applications(*) | 「どのショートカット（レシピ）」には「どのアプリ」が必要か |
*中間テーブル

## (4) 工夫したところ(実装面）

### FatControllerにならないよう実装
FatControllerにならないよう、一部のメソッド内の処理をUseCaseに切り分けました。
Controllerの可読性を上げるのが狙いです。

```AddRecipeUseCase.php
final class AddRecipeUseCase
{
    public function handle($request)
    {
        $recipe = new Recipe;
        DB::transaction(function () use ($recipe, $request) {
            $recipe->fill($request->all());
            $recipe->user_id = Auth::id();
            $recipe->save();
            $recipe->applications()->sync($request->select_application);
            $recipe->products()->sync($request->select_product);
        });
    }
}

```


```RecipeController.php
    public function addRecipe(AddRecipeRequest $request)
    {
        $addRecipeUseCase = new AddRecipeUseCase();
        $addRecipeUseCase->handle($request);
        session()->flash('flash_message', '登録しました');
    }


```

### N+1問題対策

SQLをデータの件数分発行してしまうN+1問題に対応してしまうため、EagerLoadを使用して対応しています。
多対多の関係であるRecipe（ショートカット）テーブルとApplicationテーブル、Productsテーブルへの対応のため、以下のように実装しています。

```Recipe.php

class Recipe extends Model
{
    ~~省略~~

    public function applications()
    {
        return $this->belongsToMany('App\Application', 'recipes_relation_applications')->withTimestamps();
    }

    public function Products()
    {
        return $this->belongsToMany('App\Product', 'recipes_relation_products')->withTimestamps();
    }
}
```


```RecipeController.php

$recipes = Recipe::with(['products', 'applications'])
           ->where('recipes.delete_flg', false)
           ->get();
```

### 非同期処理の実装
非同期処理としてaxiosを使用しています。
入力フォームからログイン情報等を送信した場合、バリデーションエラーが返ってきたとしても画面遷移することなくエラーメッセージを表示できるようにしています。


```AddRecipeComponent.js
axios.post("addRecipe", this.recipe)
.then(function() {
    self.errors = [];
    location.href = "/";
})
.catch(function(error) {
    for (let key in error.response.data.errors) {
        self.errors[key] =
            error.response.data.errors[key][0];
    }
});
```

### CircleCIを使用した自動デプロイ
インフラ図に記載したフローで自動デプロイしています。
おかげでポートフォリオを修正した際に、EC2にログインすることなくデプロイできるので非常に楽です。

## (5) 工夫したところ(機能・デザイン面）
### フィルタリングや検索で高速に絞り込めることが可能
Vue.jsを使用したことで高速な表示切り替えを実装しました。
<a href="https://gyazo.com/69848ad8dc76fdb7ed556f26ce77b3a0"><img src="https://i.gyazo.com/69848ad8dc76fdb7ed556f26ce77b3a0.gif" alt="Image from Gyazo" width="350"/></a>

### Vuetifyを使用したデザイン
入力フォームの少しリッチなアニメーションや、滑らかなサイドバーの表示をVuetifyによって実装しています。
<a href="https://gyazo.com/3672fdf8d0a2a6cd99e191ab86f62324"><img src="https://i.gyazo.com/3672fdf8d0a2a6cd99e191ab86f62324.gif" alt="Image from Gyazo" width="350"/></a>


### レスポンシブ対応
iPhoneからの操作を想定しますが、PC表示にも対応しています。
<img width="800" alt="ショートカット一覧" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545959/7148448f-4875-358c-44e4-aeabdcaade90.png">


## (6) 苦労したところ

### CircleCI
中々テスト用DBでテストが実行されず、時間を溶かしました。
CircleCIの`config.yml`にテスト用DBの接続情報が記載されいなかったことが原因でした。

### 本番環境
本番環境でデータベースに接続できず苦労しました。
原因はDBの通信として`unix_socket`を使用していたことでした。
EC2からRDSへの接続は`socket`通信で行う必要があるため、`unix_socket`に関する記述を削除することで、データベース接続ができました。


### Vuetify
Vuetifyを使えばリッチなUI/UXデザインが実装できるものの、自分で少し修正を加えたい場合に思うようにいかず苦労しました。
結局CSSに!importantを使って無理やり実装したこともありました。
また、`v-navigation-drawer`というコンポーネントを使ってサイドバーを実装した際に、予想外の動作をして苦労しました。
[【Vuetify】画面幅を変更した際にサイドバーが表示されてしまう](https://qiita.com/kuro221/items/94e0cf9dd01cb3eaf0dc "タイトル")

## (7)課題
### テストコード
自動デプロイのためにCircleCIを使用しているものの、テストコードが不十分です。
せっかくのCircleCIを活かしきれていないので、テストコードの書き方を勉強してテスト数を増やしていきたいと思います。

### リファクタリング
メソッドの責任を単一にするためのリファクタリングを実施していきたいです。
UseCaseに切り分けられる箇所はまだありそうなので、対応していきます。


## 最後に 
ポートフォリオを作成したことで様々な分野の理解が深まりました。
「便利なものを伝えたい」という自分の思考性にあったWEBアプリを作成できたと思います。
ただし、課題は残っているので、今後さらに改善を図っていきたいと思います。
最後にポートフォリオ作成に参考にしたTechPitの教材を掲載して終わりにします。

[Laravel × CircleCI × AWSで学ぶCI/CD | Techpit](https://www.techpit.jp/courses/78)
[Laravel で Fat Controller をリファクタリングしよう | Techpit](https://www.techpit.jp/courses/176)