初めてLaravelを使って開発したものをherokuへデプロイしたら大変だった

##はじめに
表題の通りです。
こちらの本を使ってlaravel学習し、Webアプリを作成しましたが、デプロイないまま終わってしまったのでデプロイの手順を調べながら進めます。

<a target="_blank" href="https://www.amazon.co.jp/gp/product/B08TBF3FQS/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=B08TBF3FQS&linkCode=as2&tag=cocolonifty00-22&linkId=360040de458acafb5b542a217d0da934">動かして学ぶ！Laravel開発入門</a>

####構成
- IDE:cloud9
- フレームワーク：laravel
- 言語：PHP
- DB：MySQL

####前提
- herokuへのアカウント登録は済んでいる。
- GitHubへのソースアップは行っている。

##デプロイの設定
herokuはGitHubと繋げてそのまま公開ができるのでまずはその設定をします。
[アプリ一覧](https://dashboard.heroku.com/apps)より、Newで適当なアプリ名で作成。
![スクリーンショット 2021-08-14 17.43.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/58c77e59-cf1a-e259-f285-7dc7fbe9fdfc.png)

作成したアプリを選択し、[Deploy]タブでGitHubの設定を行います。
![スクリーンショット 2021-08-14 17.46.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/e4087d79-3d2a-3cbd-bb6a-161a4bf60d3f.png)
さらにスクロールし、自動デプロイの設定もしておきます。
今回はもうコミットが済んでしまっているGitHubを使うので手動デプロイしましょう。[Deploy Branch]ボタンを押下します。
![スクリーンショット 2021-08-14 17.52.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/91e5db8c-3ee0-af88-57e1-922c65119351.png)

###エラー1つ目（ビルドパッケージの選択）
```bush:herokuのログ
-----> Building on the Heroku-20 stack
-----> Determining which buildpack to use for this app
 !     No default language could be detected for this app.
			HINT: This occurs when Heroku cannot detect the buildpack to use for this application automatically.
			See https://devcenter.heroku.com/articles/buildpacks
 !     Push failed
```
言語が検出できなかったので、ビルドパッケージを指定してくれと言われる。通常laravelのプロジェクトを普通に作っていればおそらく検出可能です。理由は後述しますが、とりあえず言われた通りに[Setting]タブよりビルドパッケージをphpで指定します。
![スクリーンショット 2021-08-14 17.59.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/2b8f786c-3a29-1150-dd45-411800a320a0.png)

###エラー2つ目（herokuに必要なものの再配置や新規作成）
```bash:herokuのログ
-----> Building on the Heroku-20 stack
-----> Using buildpack: heroku/php
-----> App not compatible with buildpack: https://buildpack-registry.s3.amazonaws.com/buildpacks/heroku/php.tgz
       
 !     ERROR: Application not supported by this buildpack!
 !     
 !     The 'heroku/php' buildpack is set on this application, but was
 !     unable to detect a PHP codebase.
 !     
 !     A PHP app on Heroku requires a 'composer.json' at the root of
 !     the directory structure, or an 'index.php' for legacy behavior.
 !     
 !     If you are trying to deploy a PHP application, ensure that one
 !     of these files is present at the top level directory.
 !     
 !     If you are trying to deploy an application written in another
 !     language, you need to change the list of buildpacks set on your
 !     Heroku app using the 'heroku buildpacks' command.
 !     
 !     For more information, refer to the following documentation:
 !     https://devcenter.heroku.com/articles/buildpacks
 !     https://devcenter.heroku.com/articles/php-support#activation
       More info: https://devcenter.heroku.com/articles/buildpacks#detection-failure
 !     Push failed
```
phpのビルドパッケージにしては、以下がないと言われています。

- HerokuのPHPアプリには、のルートに「composer.json」が必要です。
- ディレクトリ構造、またはレガシー動作の場合は「index.php」。

私のGitHubリポジトリの構成は明らかに前者を満たしていません。

```bash:cloud9のルートディレクトリからコミットしている
/
 ├──.c9
 ├──cms
 │  └──public
 │  │  └──index.php
 │  └──composer.json
 ├──.git
 ├──.gitignore
 └──README.md
```
なぜcloud9部分からgit管理してしまったのか。
素直に構成を変えます。（ローカルで.gitを削除し、cmsの下に作り直せばいいのですが、リモートリポジトリとの差が大きくなり、マージで何か起きそうだったのが怖くて新しいリモートリポジトリを作り、そちらに.gitの階層を改めたソースをコミットしました。）

```bash:修正後
/
 ├──public
 │  └──index.php
 ├──composer.json
 ├──.git
 ├──.gitignore
 └──README.md
```

次に`index.php`エラーからはよくわからなかったですが、laravelのディレクトリ構成により、public配下にあることはherokuに伝える必要があると思われるのでエラーを検索。
以下がヒットしました。
https://devcenter.heroku.com/articles/custom-php-settings#setting-the-document-root

上記のリンク先の通り、コマンドで必要なファイルが作られますが、手作りで配置しても問題ないと思います。

```bash:/Procfile
web: vendor/bin/heroku-php-apache2 public/
```
ルート直下に格納しコミットしpush。
先ほど自動デプロイ設定をしているので自動でデプロイが走ります。
通りました。
![スクリーンショット 2021-08-14 18.19.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/e315c497-1a1b-18b9-5bdd-96f989d83b51.png)

なお、１つめのエラーは、この２つ目の対応をした状態でしたら発生しなかったと思います。

###エラー3つ目（laravelの復号化：APP_KEYの設定）
まだ色々設定していませんが、まずはWebサーバが動いていることの確認をします。
herokuアプリへアクセス：https://my-laravel-project-heroku.herokuapp.com/
![スクリーンショット 2021-08-14 18.23.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/f1852b7b-88f1-6d32-b4d2-cb52b9c61aca.png)
500はサーバエラーですが、レスポンスコードを返せているということはWEBサーバは動いています。

想定は、心当たりはDB設定が未だったのでDB接続時にエラーが出ることでしたが、TOPページでDB接続は行っていないので別段の問題があるようです。
ここで初めて知りましたが、laravelは暗号化されており複合には`.env`ファイルの`APP_KEY`が必要らしいです。
開発時は、comporserを実行しましたが、このAPP_KEYは自動で生成されていたようです。

```bash:写経したためcomporserが何をしてくれるかも曖昧なまま現在に至る
$ composer global require "laravel/installer"
```
このキーがないと暗号化されたままでheroku側で復号化できないので、herokuにも設定する必要がありますので、まずは.envファイルからAPP_KEYをコピーします。
![スクリーンショット 2021-08-14 19.13.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/1c64f6f2-d242-2191-a628-00c00f22f116.png)

[Settng]タブにてherokuのConfigを開きます。
![スクリーンショット 2021-08-14 18.52.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/5e253a86-a310-2fe6-076e-15c76c673952.png)
KEY:VALUE形式なので、左側に「APP_KEY」、右側にコピーした値を入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/8d2280fb-297a-292a-dd4a-d30d40d6da05.png)

※なお、envファイルをcommit対象にしてherokuに持ち込んで使う方法もあるようですがセキュア情報がpublicファイルになるのは大丈夫なんでしょうか。publicじゃないgitで管理しているのであれば問題ないと思います。

TOP画面が出ました。
![スクリーンショット 2021-08-14 19.20.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/b625a2c3-cf2e-91ff-e0e9-07f0409661e2.png)

###エラー4つ目（データベース追加と設定）
先ほど出たTOP画面で試しに、ログイン情報を入れてDB接続を発生されたところ500エラーになりました。
herokuでは明らかにDB設定をしていませんので想定通りのエラーです。

では、これを解消していきます。まずはデータベースは[Resource]タブから登録します。
![スクリーンショット 2021-08-14 18.33.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/a1768890-8422-799a-c33f-94afb3376c1b.png)

[ClearDB MySQL]の[Ignite]プランがFreeなのでこちらを選択します。（ただし利用するにはクレジットカードの登録は必要です）
![スクリーンショット 2021-08-14 18.29.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/b014b675-4396-5b41-7d8b-32421bda3460.png)

接続するアプリを聞かれるので、入力します。（私の場合、my-laravel-project-heroku）
問題なければ、[Submit Order Form]ボタンを押下
![スクリーンショット 2021-08-14 18.29.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/ecc859f3-4890-96f7-fb49-6c3676ea2738.png)
追加されました。
![スクリーンショット 2021-08-14 18.40.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/dd1df5e8-4b21-f69d-df1e-b5157ecf9039.png)
なお、ここで[Settings]タブのConfigには`CLEARDB_DATABASE_URL`が自動的に追加されます。
![スクリーンショット 2021-08-14 19.01.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/3ada3d8b-0885-c554-0946-73fe21a3cce9.png)
laravelだと`CLEARDB_DATABASE_URL`ではなく、`DATABASE_URL`で設定を読んでいるんですよね。。
![スクリーンショット 2021-08-14 19.28.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/03f56a9b-9bb8-fb7d-c7ee-3a944c30a594.png)
このままだと参照できないので、[Settings]の方を変えます。
変更の方法は色々あると思いますが、今回は、キーに`DATABASE_URL`にバリューに`CLEARDB_DATABASE_URL`と同じ値を入れる形としました。
`DATABASE_URL`以外でも.envに定義している環境設定があるので`CLEARDB_DATABASE_URL`の情報を元に追加していきます。

```bash:CLEARDB_DATABASE_URLの構成
mysql://(ユーザー名):(パスワード)@(ホスト名)/(データベース名)?reconnect=true
```
![スクリーンショット 2021-08-14 20.34.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/a25024e2-3880-2484-245e-f34f5561fb61.png)

エラーが変わってきました。DBには接続できたようです。
![スクリーンショット 2021-08-14 19.33.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/c549aa1f-08ec-f617-3a02-c319840f7e09.png)

なお、herokuではPHP側を変える設定が説明されていますが、laravelのローカル開発時（Cloud9では.envを読むので）動作と合わなくなってしまうのでこの手段は取っていません。
https://devcenter.heroku.com/articles/cleardb#using-cleardb-with-php

###エラー5つ目（マイグレーション）
まだ中身がないので、残るはマイグレーションをやっていきます。
ここまでCLIを使わず、画面上で設定してきましたが、流石にlaravelの機能であるマイグレーションをheroku画面で実施することができないのでCLIを使います。

#####Cloud9にherokuのインストール
ローカルにherokuを入れているのでそちらを使っても良かったのですがせっかくなのでcloud9からやってみます。

```bash:インストール
$ source <(curl -sL https://cdn.learnenough.com/heroku_install)
$ heroku -v
heroku/7.56.1 linux-x64 node-v12.21.0
```
#####Cloud9でマイグレーション
まずはログイン（MFA有効だとcloud9上でうまくいかないので無効にしました）

```bash:ログイン
$ heroku login -i
heroku: Enter your login credentials
Email: {メールアドレス}
Password: ***********
Logged in as {メールアドレス}
```
ログインできたらアプリを指定してrunを実行します。

```bash:マイグレーション（Canceled!!!!!）
$ heroku run "php artisan migrate" -a my-laravel-project-heroku
 Do you really wish to run this command? (yes/no) [no]:
 > yes
Command Canceled!
```
なんでやねん。
タイプミスで`yse`通ったのでよくわからないです。`y`でもいけました。

```bash:気を取り直してマイグレーション
$ heroku run "php artisan migrate" -a my-laravel-project-heroku 
Running php artisan migrate on ⬢ my-laravel-project-heroku... up, run.5001 (Free)
**************************************
*     Application In Production!     *
**************************************

 Do you really wish to run this command? (yes/no) [no]:
 > yse

Migration table created successfully.
```
ついにDBアクセス含めた画面が出ました。
これは、アカウント登録した後の画面です。右上にDB登録されたアカウント名が取得され表示されています。
![スクリーンショット 2021-08-14 21.02.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/d7c05254-4f5e-6ad1-7401-6706b72a5667.png)

###テストデータを投入する
せっかくなのでテストデータを入れます。

```bash:BooksTableSeederクラスはcloud9側で作成済みのためいきなり実行しています
$ heroku run "php artisan db:seed --class='BooksTab
leSeeder'" -a my-laravel-project-heroku
In BooksTableSeeder.php line 15:
  Class "Faker\Factory" not found  
```
もう、すんなりうまく行くことがない。

######同じ事象がたくさん出ているようで、こちらを参考にさせていただきました
https://dhate.hatenablog.com/entry/2019/12/08/005746

解決方法を読んで納得しましたが、テストデータ入れるためのものを`require`に入れるのはよくないですね。
このエラーは正しいと思いますが、今回はデモ的にデータが欲しいのでこの通りにします。

/cms/composer.jsonを以下のようにしましました。（fakerは`fzaninotto`のフォーク）
![スクリーンショット 2021-08-14 21.21.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/98d12812-2de8-7333-4082-648ddb2dd9fa.png)
この後は、`composer update`をし、commitとpush。
デプロイが終わったところを見計らい、再実行したところうまくいきました。

```bash
$ heroku run "php artisan db:seed --class='BooksTableSeeder'" -a my-laravel-project-heroku    
Database seeding completed successfully.
```

【余談】
テストデータは挿入されたのですが、fakerで画像ファイルが作成されず、データベースにはファイル名だけ登録されているため画像がリンク切れとなりました。
![スクリーンショット 2021-08-14 21.59.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/9c1f46ce-927c-3c7d-288e-643345688678.png)

herokuのサーバ内に入って確認しましたが、upload/配下はCloud9環境でテストデータを作成しGitHubにあげたファイルのままです。
Webアプリからも画像アップできるUIがあったので試してみましたが、419エラーとなりました。
fakerではなくherokuでは画像アップロード自体できないように見えました。
試しにherokuサーバにbash出入り、画像がアップされる先に権限をつけてみました。
UI操作から419エラーは出なくなりましたが、画像はアップされていませんでした。

```bash:権限付与で画像アップエラーは消えるがアップはされない
// heroku内へ
$ heroku run {アプリ名}

// 画像アップに関係するフォルダの権限を付与
$ chmod a+wrx public/upload
$ chmod a+wrx public
```
herokuに画像をアップし続けて負荷をかける可能性を考えると正しい挙動な気がします。
GitHubからアップした画像ファイルは許容されているので力技ですが、テーブル内の値をupload/配下のファイル名に合わせましたが、herokuにおいてはUIからのアップロードは不可となりました。

```bash:herokuでアドオンしたdbへ入って一つ一つ丁寧に作業しました。1つだけサンプルでupdate文書いておきます。。
$ mysql -u {DB_USERNAME} -h {DB_HOST} -p
>>> use {DB_DATABASE}
>>> update books set item_img = "f7a773e1290ff18932af806d9a0134c5.png" where item_img = "3c141ed179e0b41b44c864da732d0a49.png";
```

##デプロイ完了
成果物は[こちら](#成果物)にまとめました。

![スクリーンショット 2021-08-14 23.16.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/380300/a13a6ebd-1ce6-5928-ce5a-512f1ea54985.png)

##おわりに
さっと終わらせるつもりでしたが、時間かかってしまい疲れました。

この本、環境構築周りでの気遣いはものすごくありましたが、
途中から画像の更新処理や削除処理がなかったり、デプロイがないので注意ですね。。
<a target="_blank"  href="https://www.amazon.co.jp/gp/product/B08TBF3FQS/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=B08TBF3FQS&linkCode=as2&tag=cocolonifty00-22&linkId=a111c799abcb13fe25d0d84c2cb55cdf"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&MarketPlace=JP&ASIN=B08TBF3FQS&ServiceVersion=20070822&ID=AsinImage&WS=1&Format=_SL160_&tag=cocolonifty00-22" ></a>


##成果物
あとで変えると思うのでタグも置いておきます。
画像周りは前述の通り何もかもおかしいです。
GitHub：https://github.com/c3drive/my_laravel_project_heroku
GitHub執筆時点ソース：https://github.com/c3drive/my_laravel_project_heroku/releases/tag/1.0
heroku：https://my-laravel-project-heroku.herokuapp.com/
ーメールアドレス：test@example.com
ーパスワード：test00000


##備忘
```bash:heroku内のdbやwebサーバに入るコマンド
$ heroku run {アプリ名}
$ mysql -u {DB_USERNAME} -h {DB_HOST} -p
```