- 投稿日:2019-07-09T21:16:51+09:00
Carbonを使って投稿経過時間を表示する(Laravel)
About Carbon
Carbonについての説明は下記の記事に詳しく書かれています。
PHPで日付時刻処理を書くならCarbonを使うべき
phpの便利な日付、時刻オブジェクト Carbon を使用する。平成、令和などの和暦を表示させる
全217件!Carbonで時間操作する実例概要
過去に作ったモジュールについて記事を書いてます。
instgramなどの投稿に、投稿から「〇〇分前」などと表示される時間経過の部分を作ります。
画像の「9分前」、「1秒前」の部分のことです。
コード
bladeテンプレートの投稿コンテンツにこれを書くだけでできます。
index.blade.php<?php //時間の差分を求める $postedAt = new \Carbon\Carbon($message->created_at); $now = \Carbon\Carbon::now(); $secondsSincePosted = $postedAt->diffInSeconds($now); if($secondsSincePosted > 59){ $minutesSincePosted = $postedAt->diffInMinutes($now); if($minutesSincePosted > 59){ $hoursSincePosted = $postedAt->diffInHours($now); if($hoursSincePosted > 23){ $daysSincePosted = $postedAt->diffInDays($now); if($daysSincePosted > 6){ $yearsSincePosted = $postedAt->diffInYears($now); if($yearsSincePosted > 0){ echo $postedAt->format("Y年n月j日"); }else{ echo $postedAt->format("n月j日"); } }else{ echo $daysSincePosted.'日前'; } }else{ echo $hoursSincePosted.'時間前'; } }else{ echo $minutesSincePosted.'分前'; } }else{ echo $secondsSincePosted.'秒前'; } ?>まとめ
Carbonはいいぞ。
Githubにも上げてます。
jdkfx/posted-date
- 投稿日:2019-07-09T20:07:10+09:00
LaradockでLaravel環境構築【手順書】
はじめに
laradockでlaravel環境を作る手順書です。
単純に動かすだけの手順でなく、下記点を含んでいます。
- laradockで2つ目の環境を構築する時に困らないための設定事項
- 設定しておくと開発時に多少楽になるための設定事項
- 嵌った点の備忘録
前提環境
- docker-composeが使用できること
- windowsの場合Windows10 Proであること
- docker-composeがインストールされていること
- ターミナルを使用できること
- windowsの場合gitbashやpower shell推奨
- 海外のCDNサーバにブロックされていたりしないこと
- 短期間に環境を作りまくったせいか一部サーバに一時的にブロックされたことがあります
- この場合ネットワークを変えることで解決したりします(ポケットwifi等)
- 2018年以前に取得したlaradockを使用しないこと
- laradockの使用しているdocker-compose.ymlがv2である可能性があります
完成形
サーバ構成
- nginx
- + php-fpm
- mysql
- redis
- APサーバ(workspace)
ディレクトリ構成
laradockとプロジェクトディレクトリ(laravel)を同階層に置く想定です
my_project ├── laravel │ ├── app │ ├── bootstrap │ ├── config │ └ ... └── laradock ├── adminer ├── aerospike └ ...手順
- laradock取得
- laradockの設定ファイル修正
- laradockビルド・アクセス
- laravelインストール・yarnインストール
- laravel設定ファイル修正
- ブラウザアクセス
1.laradock取得
適当な場所にプロジェクト用ディレクトリを作成し、laradockをダウンロード
mkdir ~/my_project cd ~/my_project git clone https://github.com/Laradock/laradock.git2.laradockの設定ファイル修正
- 設定ファイルの作成
- .env作成
- createdb.sql作成
cd ./laradock cp -a ./env-example ./.env cp -a ./mysql/docker-entrypoint-initdb.d/createdb.sql.example ./mysql/docker-entrypoint-initdb.d/createdb.sql
- 下記ファイルを修正
.env+ APP_CODE_PATH_HOST=../laravel/ - APP_CODE_PATH_HOST=../ + APP_CODE_PATH_CONTAINER=/var/www/laravel/ - APP_CODE_PATH_CONTAINER=/var/www/ //mysql等のデータ本体の置き場所。これを修正しておかないと未来で困る。 + DATA_PATH_HOST=~/.laradock/my_project/data - DATA_PATH_HOST=~/.laradock/data //コンテナ区別用prefix。これを修正しておかないと未来で困る。 + COMPOSE_PROJECT_NAME=my_project-laradock - COMPOSE_PROJECT_NAME=laradock + PHP_VERSION=7.3 - PHP_VERSION=7.2 //laravelがmysql8.0を使うにはユーザ設定に追加で修正が必要なため5系を指定 + MYSQL_VERSION=5.7 - MYSQL_VERSION=latest/nginx/sites/default.conf+ server_name dev.my_project.com; - server_name localhost; + root /var/www/laravel/public; - root /var/www/public;/mysql/docker-entrypoint-initdb.d/createdb.sql+ CREATE DATABASE IF NOT EXISTS `project_db` COLLATE 'utf8_general_ci' ; + GRANT ALL ON `project_db`.* TO 'default'@'%' ; - #CREATE DATABASE IF NOT EXISTS `dev_db_1` COLLATE 'utf8_general_ci' ; - #GRANT ALL ON `dev_db_1`.* TO 'default'@'%' ; // テスト用DB作成(ユーザは使いまわす想定) + CREATE DATABASE IF NOT EXISTS `test` COLLATE 'utf8_general_ci' ; + GRANT ALL ON `test`.* TO 'default'@'%' ; - #CREATE DATABASE IF NOT EXISTS `dev_db_2` COLLATE 'utf8_general_ci' ; - #GRANT ALL ON `dev_db_2`.* TO 'default'@'%' ;3. laradockビルド・アクセス
下記コマンドを実行し、サーバを構築・ビルド
cd ~/my_project/laradock docker-compose build workspace nginx mysql redis docker-compose up -d nginx mysql redis下記コマンドでworkspaceサーバへアクセス
docker-compose exec --user=laradock workspace bash4. laravelインストール・yarnインストール
workspaceサーバ内で下記コマンド実行
laradock@~~~~:/var/www$ composer create-project laravel/laravel --prefer-dist laradock@~~~~:/var/www$ cd laravel laradock@~~~~:/var/www/laravel$ yarn install5. laravel設定ファイル修正
.envを修正
.env+ DB_HOST=mysql - DB_HOST=127.0.0.1 + DB_DATABASE=project_db - DB_DATABASE=default + DB_USERNAME=default - DB_USERNAME=homestead6. ブラウザアクセス
hostsの修正
windowsの場合
メモ帳を管理者で実行し、下記ファイルを修正
C\Windows\System32\drivers\etc\hosts+ 127.0.0.1 dev.my_project.commacの場合
/private/etc/hosts+ 127.0.0.1 dev.my_project.com
ブラウザアクセス
ブラウザを開き、http://dev.my_project.com へアクセス
laravelが表示されていれば完了です!![]()
おまけ
ありがちな失敗
docker-compose buildに失敗する
npm ERR! code EAI_AGAIN npm ERR! errno EAI_AGAIN上記のようなエラーが発生した場合、時間をおいて再ビルドすれば直ることがあります。
docker-compose up時にmysqlが起動しない
既にlaradockでプロジェクトを立ち上げたことがある場合、
そのプロジェクトとDATA_PATH_HOSTの場所被った場合立ち上げに失敗します。DATA_PATH_HOSTを変えビルドしなおし(docker-compose build --no-cache mysql)をしたり、
既存のDATA_PATH_HOSTの中身を削除したりしてみてください。
(削除して大丈夫かはご自身でご判断ください)docker-compose upが全体的に失敗する
過去にdocker等で仮想イメージを作ったことがある場合、
既存のものとポートが被ったりすることが多々あります。workspace, nginx, mysql, redisのポートをずらしたりdockerを再起動したりビルドしなおしたり色々試してください。
*特に2018年までのlaradockにはCOMPOSE_PROJECT_NAMEの設定が無いため、コンテナ名が被りますmysqlを誤って8.0で作成してしまった
laravelは最新版でもmysql8.0標準の暗号化方法(caching_sha2_password)をサポートしていません。(2019/6/28現在)
mysql8.0を作成してしまい、そこで作ってしまったユーザでログインしたい場合、
下記のようなsqlによりパスワード設定を更新してください。ユーザ:'default'を、パスワード:'secret'で更新
ALTER USER 'default' IDENTIFIED WITH mysql_native_password BY 'secret';Q.A
php5.4は使えないのか?
使えません。5.6以上となります。
windows10 homeでは使えないのか?
現在のところ使えません。
どうしても使いたい人はWSL2でググると道があります。DBにつながるか確認したい
workspace内で下記コマンド打てばテーブル作成処理が走るので、試してみるといいと思います。
$ cd /var/www/laravel/ $ php artisan migrate成功後は下記コマンドでだいたい元通りにできます
$ php artisan migrate:rollback
- 投稿日:2019-07-09T16:59:09+09:00
Laravel キーワード検索 複数
はじめに
Laravelアプリにて、複数条件の検索機能を実装しました!
文字とカテゴリーの2つで検索出来る仕様です!始め(修正前)はコントローラー/モデルで下記のように、条件毎でメソッドを分け記述していました。(メソッド名は気にせず)
QuestionsController.php(修正前)class QuestionsController extends Controller { private $question; private $category; public function __construct(Question $question, Category $category) { $this->question = $question; $this->category = $category; } public function index(Request $request) { //カテゴリー一覧を表示するため、カテゴリーモデルでデータを連想配列で取得するメソッド $categoryNames = $this->category->カテゴリー名取得メソッド(); $keyword = $request->input('キーワード'); $categoryId = $request->input('カテゴリー'); if (isset($keyword) && isset($categoryId)) { $questions = $this->question->なんちゃらメソッド1($keyword, $categoryId); } elseif (isset($keyword)) { $questions = $this->question->なんちゃらメソッド2($keyword); } elseif (isset($categoryId)) { $questions = $this->question->なんちゃらメソッド3($categoryId); } else { $questions = $this->question->all(); } return view('question.index', compact(['questions', 'categoryNames'])); } }モデル
questiion.php(修正前)class Question extends Model { public function なんちゃらメソッド1($keyword, $categoryId) { return $this->with(['紐づくデータ'])->where('title', 'LIKE', '%' . $keyword . '%') ->where('category_id', '=', $categoryId) ->get(); } public function なんちゃらメソッド2($keyword) { return $this->with(['紐づくデータ'])->where('title', 'LIKE', '%' . $keyword . '%')->get(); } public function なんちゃらメソッド3($categoryId) { return $this->with(['紐づくデータ'])->where('category_id', '=', $categoryId)->get(); } }今回は条件が2つなので上記の記述量で収まっていますが、検索条件が増えた場合、メソッドと条件分岐をその都度書き足す必要が出てきます。
めんどくさいですし、コントローラがファットになってしまいます。複数条件での検索を探す中で、クロージャやorWhereなど色々出てきましたが、わからず。。
最終的に下記の記述に書き直しました!QuestionsController.php(修正後)class QuestionsController extends Controller { <--- 略 ---> public function index(Request $request) { $categoryNames = $this->category->カテゴリー名取得メソッド(); $questions = $this->question->なんちゃらメソッド($request); return view('question.index', compact(['questions', 'categoryNames'])); } }モデル
Question.php(修正後)class Question extends Model { public function なんちゃらメソッド($request) { $keyword = $request->input('キーワード'); $categoryId = $request->input('カテゴリー'); $query = $this->with(['紐づくデータ', '紐づくデータ']); if (isset($keyword)) { $query->where('title', 'LIKE', '%' . $keyword . '%'); } if (isset($categoryId)) { $query->where('category_id', '=', $categoryId); } return $query->get(); } }inputでの値の抽出もモデルに記述し、なるべくコントローラーのコードを減らしました!
eagerロードで紐づく値を取得したものを$query変数に代入し、条件が入力されていればその条件にあったSQLの処理が走ります!このように書けば、メソッド毎に処理を追うこともなくなり、条件が増えた場合の追加が楽、可読性向上などのメリットがあると思います!
改善点あればご教示お願い致します!
- 投稿日:2019-07-09T16:59:09+09:00
Laravel 検索機能 複数条件
はじめに
Laravelアプリにて、複数条件の検索機能を実装しました!
文字とカテゴリーの2つで検索出来る仕様です!始め(修正前)はコントローラー/モデルで下記のように、条件毎でメソッドを分け記述していました。(メソッド名は気にせず)
QuestionsController.php(修正前)class QuestionsController extends Controller { private $question; private $category; public function __construct(Question $question, Category $category) { $this->question = $question; $this->category = $category; } public function index(Request $request) { //カテゴリー一覧を表示するため、カテゴリーモデルでデータを連想配列で取得するメソッド $categoryNames = $this->category->カテゴリー名取得メソッド(); $keyword = $request->input('キーワード'); $categoryId = $request->input('カテゴリー'); if (isset($keyword) && isset($categoryId)) { $questions = $this->question->なんちゃらメソッド1($keyword, $categoryId); } elseif (isset($keyword)) { $questions = $this->question->なんちゃらメソッド2($keyword); } elseif (isset($categoryId)) { $questions = $this->question->なんちゃらメソッド3($categoryId); } else { $questions = $this->question->all(); } return view('question.index', compact(['questions', 'categoryNames'])); } }モデル
questiion.php(修正前)class Question extends Model { public function なんちゃらメソッド1($keyword, $categoryId) { return $this->with(['紐づくデータ'])->where('title', 'LIKE', '%' . $keyword . '%') ->where('category_id', '=', $categoryId) ->get(); } public function なんちゃらメソッド2($keyword) { return $this->with(['紐づくデータ'])->where('title', 'LIKE', '%' . $keyword . '%')->get(); } public function なんちゃらメソッド3($categoryId) { return $this->with(['紐づくデータ'])->where('category_id', '=', $categoryId)->get(); } }今回は条件が2つなので上記の記述量で収まっていますが、検索条件が増えた場合、メソッドと条件分岐をその都度書き足す必要が出てきます。
めんどくさいですし、コントローラがファットになってしまいます。複数条件での検索を探す中で、クロージャやorWhereなど色々出てきましたが、わからず。。
最終的に下記の記述に書き直しました!QuestionsController.php(修正後)class QuestionsController extends Controller { <--- 略 ---> public function index(Request $request) { $categoryNames = $this->category->カテゴリー名取得メソッド(); $questions = $this->question->なんちゃらメソッド($request); return view('question.index', compact(['questions', 'categoryNames'])); } }モデル
Question.php(修正後)class Question extends Model { public function なんちゃらメソッド($request) { $keyword = $request->input('キーワード'); $categoryId = $request->input('カテゴリー'); $query = $this->with(['紐づくデータ', '紐づくデータ']); if (isset($keyword)) { $query->where('title', 'LIKE', '%' . $keyword . '%'); } if (isset($categoryId)) { $query->where('category_id', '=', $categoryId); } return $query->get(); } }inputでの値の抽出もモデルに記述し、なるべくコントローラーのコードを減らしました!
eagerロードで紐づく値を取得したものを$query変数に代入し、条件が入力されていればその条件にあったSQLの処理が走ります!このように書けば、メソッド毎に処理を追うこともなくなり、条件が増えた場合の追加が楽、可読性向上などのメリットがあると思います!
改善点あればご教示お願い致します!
- 投稿日:2019-07-09T15:10:09+09:00
AmazonLinux2でLaravelの開発環境構築
AWSでLaravelの開発環境を構築して行きたいと思います。
前提条件として、
すでにAWSでアカウントは作成済みとしています。
また、簡単なWEBアプリケーションは作成したことがある方を対象としていますEC2の起動
まずは、AWSのコンソールからEC2を起動します。
「インスタンスの作成」をクリックすると、Amazon マシンイメージ(AMI)の選択画面になるので、Amazon Linux 2 AMI (HVM),SSD Volume Type を「選択」します。次にインスタンスタイプの選択画面になるので、好きなインスタンスタイプを選択します。t2.microが無料利用枠の対象なので、利用できる方はこちらのタイプが良いかと思います。
私は開発環境での利用なので、今回 t3.nano を選択しました。インスタンスの詳細の設定については、基本的には自由に設定できますが、よくわからなければ一旦全てデフォルトのままでも問題ないかと思います。
私は、そこまで頻繁に利用しない開発環境ということもあるので、スポットインスタンスのリクエストにチェックを入れて、費用を安く抑えています。スポットインスタンスは、費用を安く抑えられる反面、連続稼働を保証していないので、本番環境ではお勧めできませんので注意ください。ストレージの追加も任意ですが、ルートの8GiBのみで進みます。
セキュリティグループの設定は、一旦最小限のセキュリティグループを作成します。
タイプ プロトコル ポート範囲 ソース SSH TCP 22 マイIP (ご自身の接続IP) HTTP TCP 80 カスタム 0.0.0.0/0,::/0 ターミナルへのログインのために、SSHのポートをご自身のIPで設定。
HTTPは、どこからでも閲覧可能なように設定。自分しか確認できないようにしたい場合は、HTTPのソースについてもマイIPを設定してください。最後に、ログイン用のキーを生成して終了です。
EC2の初期設定
EC2が起動したら、ec2-userとしてログインしてみましょう。
作成したキーを .ssh/ 以下に配置しておきます。ssh ec2-user@ec2-**-**-**-***.ap-northeast-1.compute.amazonaws.com -i ./.ssh/(秘密キー)Laravelをセットアップする前に、ざっとEC2の設定を行います。
とりあえず、パッケージを最新に更新
$ sudo yum update -yec2-userのままでも良いですが、実際に利用するユーザーを作成
ユーザー名をいつも利用しているユーザー名を利用してください。説明では munakata として進めます。$ sudo su - # useradd munakata # passwd munakata # usermod -G wheel munakatamunakata に sudo権限を付与します。
# visudoroot以下に追加
visudo## Allow root to run any commands anywhere root ALL=(ALL) ALL munakata ALL=(ALL) ALLmunakataのホームディレクトリ(/home/munakata) 以下の
.ssh/authorized_keys に公開鍵をセット。すでにご自身の公開鍵はお持ちかと思いますが、まだない方は作成してください。
SSHキー等で検索すれば、いくつか情報が出てくるかと思います。一応、権限を記載しておきます。
権限 パス 700 ~/.ssh 600 ~/.ssh/authorized_keys これで、munakataユーザーでログインする準備は完了です。
一度ログアウトして、実際に新しいユーザーでログインしてみましょう。ssh munakata@ec2-**-**-**-***.ap-northeast-1.compute.amazonaws.com -i ./.ssh/(munakataの秘密キー)ローカライズ設定を行います。
タイムゾーンを日本時間にセット
/etc/sysconfig/clock$sudo vim /etc/sysconfig/clock ZONE="Asia/Tokyo" UTC=false反映には再起動が必要なので、とりあえず日本時間にセット
$ sudo cp /usr/share/zoneinfo/Japan /etc/localtime日本語設定
/etc/sysconfig/i18n$ sudo vim /etc/sysconfig/i18n LANG=ja_JP.UTF-8ここまでで、ざっとEC2の初期設定が完了です。
必要なパッケージのインストール
ここからは、AmazonLinux2でLaravelを構築するために必要なパッケージをインストールしていきます。
なるべく特殊なことはせず、ある程度AWSで用意された標準的なもので構築したいと思います。WEBサーバーとして、apache2.4 、PHPのバージョンは、PHP7.3 を使用します。
いきなりですが、AmazonLinux2で、yumを利用してPHPをインストールするとPHP5になってしまいます。
AmazonLinuxでは、yum install php72 でPHP7.2をインストールできたのですが、AmazonLinux2には用意されていません。
その代わりに、Extra Library が用意されているようです。
Extra Library は、amazon-linux-extras で利用可能です。php7.3をインストールしてみます。
$ sudo amazon-linux-extras php7.3一緒に必要なパッケージもインストールされます
php-cli.x86_64 7.3.6-1.amzn2.0.1 @amzn2extra-php7.3 php-common.x86_64 7.3.6-1.amzn2.0.1 @amzn2extra-php7.3 php-fpm.x86_64 7.3.6-1.amzn2.0.1 @amzn2extra-php7.3 php-json.x86_64 7.3.6-1.amzn2.0.1 @amzn2extra-php7.3 php-mysqlnd.x86_64 7.3.6-1.amzn2.0.1 @amzn2extra-php7.3 php-pdo.x86_64 7.3.6-1.amzn2.0.1 @amzn2extra-php7.3確認してみましょう。
$ php -vamazon-linux-extras でパッケージをインストールすると、拡張モジュールに関しては、yumを使って適切なパッケージをインストールしてくれるようになります。便利ですね!
実際にインストール可能な拡張モジュールを確認してみましょう。
$ sudo yum list php* | grep php7.3必要な拡張モジュールをインストールしていきます。必要に応じて各自検討ください。
php-xmlは、Laravelインストール時に、phpunitのインストールに必要になるようなので、事前にインストールしておきましょう。$sudo yum install php-mbstring php-pecl-memcached php-gd php-apcu php-xml次に、apache2.4をインストールします。
AmazonLinuxの場合は、2.4系を入れる場合は、 httpd24 でしたが、AmazonLinux2の場合は、httpd で、2.4系になるようです。
$ sudo yum install httpd起動します。
$ sudo systemctl start httpdコンソール上には何も出力されないので、実際に動いているか確認します。
下記のコマンドで、active (running) とか表示されているはずです。$ sudo systemctl status httpd実際にWEBブラウザから確認してみます。
起動したEC2に、パブリックDNSが割り振られているかと思うので、そのURLで確認すると、Apacche2.4のTestPageが表示されるかと思います。
IPv4 パブリックIPでも確認可能です。
ElasticIPを紐付けた方は、そちらのIP、もしくはRoute53で設定したドメインで確認してください。
PHPの設定
この時点で、Laravelのインストール自体は可能なのですが、PHP、Apacheの細かい設定もしていきましょう。
PHPの設定は、 /etc/php.ini で行います。設定した値を確認できるように、phpinfoの表示ページを作成しておきましょう。
設定内容は、各自調整してください。
私がよく変更する箇所は、この辺です。php.ini# HTTPヘッダにPHPのバージョンを記載しない(一応セキュリティ的にOffにしておいたほうが良い) # expose_php = On expose_php = Off # メモリ上限を引き上げる(結構デフォルトのメモリは少なめなので増やしておくことが多い) # memory_limit = 128M memory_limit = 256M # POST送信の許容サイズを引き上げる # post_max_size = 8M post_max_size = 16M # アップロードファイルの許容サイズを引き上げる(スマホの写真のサイズが大きくなっているので、2Mだとほぼ画像投稿できないので増やす) # upload_max_filesize = 2M upload_max_filesize = 16M # timezoneの設定 # date.timezone = date.timezone = Asia/Tokyo設定を反映します。
モジュール版のPHPの場合、通常httpdを再起動すると、php.iniの内容が反映されるのですが、AmazonLinux2で、PHP7.3とhttpd2.4を構築した場合、デフォルトでSever APIが FPM/FastCGI となるため、httpdでは、php.iniの設定が反映されず、php-fpmの再起動が必要となります。
php-fpmについて詳しく知りたい方は、「php-fpm」等のキーワードでお調べください。
$ sudo systemctl restart php-fpmLaravelインストール
DocumentRootにLaravelを配置することも可能なのですが、開発環境として構築するので、今回はVirtualHostの機能を利用して、ホームディレクトリに、htmlディレクトリを作成して、その配下にLaravelプロジェクトを配置します。
まずは、Composerをインストール
curl -sS https://getcomposer.org/installer | phpcomposer.phar がダウンロードされるので、composer のコマンドで実行できるように、PATHが通っている場所へ移動させます。
sudo mv composer.phar /usr/local/bin/composerこれで composer が利用できるようになったので、Laravelをインストールします。
$ cd ~/html/ $ composer create-project --prefer-dist laravel/laravel blogこれで、blogというLaravelプロジェクトが構築されます。
ただし、今回利用している t3.nano などのインスタンスタイプだと、メモリが足らずにインストールの途中に下記のエラーが出てしまいます。mmap() failed: [12] Cannot allocate memoryそこで、ハードディスクにswap領域を作成して、メモリ不足を補います。
最初に、現状確認
$ free total used free shared buff/cache available Mem: 470512 145504 118000 104 207008 290656とりあえず、1G程度用意すればLaravelのインストールは可能なのでswapを作成します。
$ sudo dd if=/dev/zero of=/swapfile bs=1M count=1024 $ sudo chmod 600 /swapfile $ sudo mkswap /swapfile $ sudo swapon /swapfile再度、freeコマンドでSwapが追加されていれば、OKです。
$ free total used free shared buff/cache available Mem: 470512 145504 118000 104 207008 290656 Swap: 1048572 27904 1020668これで、やっと準備が整ったので、再度
$ composer create-project --prefer-dist laravel/laravel blogあとは、Laravelのドキュメントに記載がある通りに設定をしていきます。
$ cd ~/html/blog $ composer update $ chmod -R 777 bootstrap/cache $ chmod -R 777 storage $ php artisan key:generate基本的には、設定ファイルは、.envになりますが、開発環境専用にする場合は、下記のようにリネームします。
$ mv .env .env.developmentApache VirtualHost 設定
最後に、ApacheのVirtualHost設定を行います。
Virtual Hostの記述は、自動で設定が読み込まれる /etc/httpd/conf.d 配下にファイルを作成して記述します。
ファイル名は任意ですが、vhost.confで作成します。$ sudo su - # cd /etc/httpd/conf.d # vim vhost.conf一旦必要な記述を記載しますが、Virtual Hostの詳しい記述方法については、他で調べてみてください。
アクセス予定のドメインは、blog.munakata.net を仮定しています。適宜変更ください。vhost.conf<VirtualHost *:80> DocumentRoot /home/munakata/html/blog/public ServerName blog.munakata.net ServerAlias blog.munakata.net <Directory "/home/munakata/html/blog/public"> #.htaccessを利用可能にする AllowOverride All # Laravelで利用する環境変数を development に設定 SetEnv APP_ENV development #アクセス許可 Require all granted </Directory> </VirtualHost>httpd再起動
$ sudo systemctl restart httpdRoute53で、設定したドメインを紐付けるか、ご自身のマシンのhostsを設定して、ブラウザでアクセスしてみてください。
Laravelのトップページが表示されていれば、一旦完了です。
- 投稿日:2019-07-09T14:57:29+09:00
Vueをマウントできなくて奮闘
プログラミング歴3か月くらいの初心者です、自分のメモ用。
bladeファイル内になどを記載して、コンポーネントを挿入しても
app.js:38008 [Vue warn]: Failed to mount component: template or render function not defined. found in ---> <コンポーネント名> <Root>上記のエラーが発生する。
app.jsにもしっかり記載している。
Vue.component('my-component', require('./components/MyComponent.vue'));いろいろな記事を見て、webpack.config.jsに追記だの devコマンドを実行したかだのいろいろ対処法が
書かれていたがどれでも解決できないところ以下の記事を見つけた。どうやら app.jsで
Vue.component('my-component', require('./components/MyComponent.vue').default);最後に.defaultを追記するだけでした。(意味は知らない)
- 投稿日:2019-07-09T13:03:14+09:00
Laravel+Nuxt.jsでDocker開発環境構築からHerokuデプロイまで
はじめに
本記事では「フレームワークをインストールして、それをインターネットに公開する」という0から1までのフェーズについて、Laravel+Nuxt.jsによって「蔵書管理」システムを構築して解説したいと思います。
また、実際に構築したシステムは下記になります。
- Heroku: https://frozen-castle-47874.herokuapp.com/
- Github: https://github.com/kon-shou/bcm-qiita-example目次
- システムアーキテクチャ
- Laravel/Nuxt.jsインストール
- Docker環境構築
- Nginx設定
- Typescript対応
- サーバーでのモデル/ビジネスロジック実装
- フロントでのモデル/ビジネスロジック実装
- Heroku設定
システムアーキテクチャ
下記の技術スタックを用います。
サーバーサイド: Laravel 5.8
フロントサイド: Nuxt.js 2.8.1 (SPA)
Webサーバー: Nginx
開発環境構築: docker
デプロイ: Herokuまた処理フローの図は下記になります。
「ブラウザにNuxt.jsで生成したSPAを返し、そのSPAからAPIサーバーであるLaravelにAPIリクエストを、Nginxを介して行う」というフローです。
Laravel/Nuxt.jsインストール
まずはLaravelドキュメントに従って Laravelでプロジェクトを作成します。
composer create-project --prefer-dist laravel/laravel book-collection-management作成したLaravelプロジェクトに
clientというディレクトリを新設して、そこにNuxt.jsドキュメントに従って Nuxt.jsをインストールします。yarn create nuxt-app client ./clientコマンド実行後の選択肢は各自の要望に応じて設定してください。
自分の場合、下記のように設定しました。? Project name client ? Project description My fantastic Nuxt.js project ? Author name kon-shou ? Choose the package manager Yarn ? Choose UI framework Buefy ? Choose custom server framework None (Recommended) ? Choose Nuxt.js modules Axios ? Choose linting tools ESLint, Prettier ? Choose test framework None ? Choose rendering mode Single Page Appコマンドによって
./clientにNuxt.jsがインストールされましたが、このままだとプロジェクトの二重管理になるため、下記を行います。
./client/node_moduleを削除
rm -rdf ./client/node_modules./clientの git 管理を解除
rm -rdf ./client/.git/./clientにある各種設定ファイルを laravel のルートに移動&更新設定ファイルの更新は各自の環境に合わせて行ってください。
ただ下記の2つのファイルは特に重要なので、注記します。
.package.json
- npm scripts に nuxt コマンドを追記
- nuxt に必要なライブラリを追加
- 修正後の.package.json
nuxt.config.js
srcDir: 'client/'を追記- nuxt build によって生成される
index.htmlの出力先をpublic/distに変更- 修正後のnuxt.config.js
index.htmlの出力先の変更は、ドキュメントルートであるpublic以下に配置することを意図しています。
詳細は「Nginx設定」で解説します。その
index.htmlの出力先の変更は https://github.com/nuxt/nuxt.js/issues/3217 に従ってnuxt.config.jsgenerate: { dir: 'public/dist' }を追記することで出力先を変更することができます。
以上を実行した後のディレクトリ構造は下記になります。
(root) ├── .editorconfig ├── .env ├── .env.example ├── .eslintrc.js ├── .git ├── .gitattributes ├── .gitignore ├── .idea ├── .prettierrc ├── .styleci.yml ├── app ├── artisan ├── bootstrap ├── client │ ├── README.md │ ├── assets │ ├── components │ ├── layouts │ ├── middleware │ ├── pages │ ├── plugins │ ├── static │ └── store ├── composer.json ├── composer.lock ├── config ├── database ├── node_modules ├── nuxt.config.js ├── package.json ├── phpunit.xml ├── public ├── readme.md ├── resources ├── routes ├── server.php ├── storage ├── tests ├── vendor ├── webpack.mix.js └── yarn.lockDocker環境構築
次は、DockerでWebサーバーとDBを構築して
docker-compose up -dでアプリが立ち上がるようにします。
追加するファイルは下記になります。(root) ├── Dockerfile => docker-compose.yml の app.build.dockerfile で参照される ├── docker │ ├── entrypoint-app.sh => docker-compose.yml の app.command で参照される │ ├── nginx.conf => docker-compose.yml の app.volumes で参照される │ └── php-fpm.conf => docker-compose.yml の app.volumes で参照される ├── docker-compose.yml └── scripts └── provisioning.sh => docker-compose.yml の app.command で参照されるdocker-compose.ymlは下記になります。
docker-compose.ymlversion: '3' services: mysql: image: mysql:5.7 volumes: # host/docker間で共有するデータを指定 - "${HOME}/book-management_mysql:/var/lib/mysql" environment: MYSQL_ALLOW_EMPTY_PASSWORD: 1 TZ: "Asia/Tokyo" ports: - "3306:3306" app: build: . # Dockerfileのディレクトリを指定 user: ubuntu volumes: # host/docker間で共有するデータを指定 - .:/srv - ./docker/nginx.conf:/etc/nginx/sites-enabled/bcm - ./docker/php-fpm.conf:/etc/php/7.2/fpm/pool.d/bcm.conf command: docker/entrypoint-app.sh # 起動処理を設定 depends_on: - mysql links: - mysql ports: - "8000:8000" working_dir: /srv
docker-compose up -dの処理の順番は下記になります。
- mysqlコンテナの起動
- appコンテナの起動
Dockerfile(provisioning.sh) からdocker imageの作成nginx.conf/php-fpm.confのマウントentrypoint-app.shの nginx / php-fpm の起動各ステップについて解説します。
1. mysqlコンテナの起動
mysqlコンテナは、mysql5.7のイメージを元にして、パスワード省略及びタイムゾーンを東京にして、ホスト/dockerの3306番ポートでアクセスを可能にしています。
ホストからmysqlコンテナへのアクセスは
mysql -h 127.0.0.01 -urootで行えます。
またappコンテナからmysqlへのアクセスはdocker-compose exec app bashでappコンテナに入りmysql -h mysql -urootで行えます。2. appコンテナの起動
appコンテナは、Dockerfileで必要なライブラリをインストールしたイメージを準備し、
/srvにプロジェクトディレクトリをマウントし、ホスト/dockerの8000番ポートでアクセスを可能にしています。2.1
Dockerfile(provisioning.sh) からdocker imageの作成Dockerfileは下記になります。
DockerfileFROM ubuntu:18.04 COPY scripts/provisioning.sh /tmp/provisioning.sh # provisioning.sh による必要ライブラリのインストール RUN /tmp/provisioning.sh # nginxの初期設定を削除 RUN rm /etc/nginx/sites-enabled/default # php-fpmの初期設定を削除 RUN rm /etc/php/7.2/fpm/pool.d/www.conf # ubuntuユーザーを追加 RUN useradd -m -s /bin/bash -u 1000 -g users ubuntu RUN apt install sudo # ubuntuでのsudoのパスワード要求をしないように RUN echo "ubuntu ALL=(ALL:ALL) NOPASSWD:ALL" >> /etc/sudoers RUN chown ubuntu:users /srvDockerfileにて
provisioning.shによってライブラリをインストールし、その後にユーザー関連の設定を行います。
provisioning.shは下記になります。provisioning.sh#!/usr/bin/env bash function package_install() { env DEBIAN_FRONTEND=noninteractive apt install -y $1 } apt-get update -y package_install php-fpm package_install php-mysql package_install php-imagick package_install php-gd package_install php-curl package_install php-mbstring package_install php-bcmath package_install php-xml package_install php-zip package_install php-redis package_install php-intl package_install nginx package_install composer package_install mysql-client package_install zip package_install unzip package_install jq package_install git package_install jq package_install vim package_install curlもし、appコンテナで追加のライブラリが必要になったら、この
provisioning.shに追記し、改めてイメージをビルドするのが良いかと思います。2.2
nginx.conf/php-fpm.confのマウントこの2つの設定ファイルを配置することで、nginx/php-fpmが正しく起動できるようになります。
php-fpm.confは下記になります。php-fpm.conf[bcm] user = ubuntu group = users listen = /run/php/bcm.sock listen.owner = www-data listen.group = www-data pm = dynamic pm.start_servers = 1 pm.max_children = 4 pm.min_spare_servers = 1 pm.max_spare_servers = 2 request_terminate_timeout = 300 chdir = /srv
php-fpm.confについては 公式のマニュアル でオプションを逐一調べていくのが早いかと思います。またソケットがどういうものか理解するのかは https://qiita.com/kuni-nakaji/items/d11219e4ad7c74ece748 の記事が非常に分かりやすく、参考にさせていただきました。
nginx.confの詳細については次の「Nginx設定」にて解説します。2.3
entrypoint-app.shの nginx / php-fpm の起動
entrypoint-app.shは下記になります。entrypoint-app.sh#!/bin/bash sudo service php7.2-fpm start sudo service nginx start tail -f /dev/null内容としては、php-fpm/nginxの起動と、dockerが落ちないようにする処理です。
これをdocker起動処理として行うことで
docker-compose up -dだけでphp-fpm/nginxの起動をさせることができます。なお
tail -f /dev/nullの詳細については http://kimh.github.io/blog/jp/docker/gothas-in-writing-dockerfile-jp/#hack_to_run_container_in_the_background のブログ記事が非常にわかりやすく、参考にさせていただきました。Nginx設定
Laravel/Nuxt.jsのインストールによって、初回アクセス時にブラウザに返却される
index.htmlと、そのindex.htmlからのAPIアクセスを受けるindex.phpを準備できるようになりました。そこで、下記の前提条件を元にして、Nginxの設定を行いたいと思います。
- ドキュメントルートは
/publicとするnuxt buildによって生成されるindex.html、及びそのindex.htmlから呼ばれる js ファイルはpublic/dist/以下に配置する- ブラウザに返された
index.htmlからのAPIリクエストはpublic/index.phpにルーティングするこの前提条件を満たす
nginx.confは下記になります。nginx.confserver { # Nginxが待ちうけるポートを指定 listen 8000 default_server; # ドキュメントルートを指定 root /srv/public; # {URL}/ の場合に {URL}/index.html を返す # index index.html (デフォルト) # /{任意の文字列} に前方一致するURLの場合に # 1. /srv/public/{任意の文字列} に一致するファイルが存在すればそれを返し、存在しなければ # 2. /dist/index.html?$query_string にリダイレクトする location / { try_files $uri /dist/index.html?$query_string; } # {任意の文字列1}/_nuxt/{任意の文字列2}に一致するURLの場合に # 1. /srv/public/{任意の文字列1}/_nuxt/{任意の文字列2} に一致するファイルが存在すればそれを返し、存在しなければ # 2. /dist/_nuxt/{任意の文字列2} にリダイレクトする location ~ /(_nuxt)/(.+)$ { try_files $uri /dist/$1/$2; } # {任意の文字列1}/api/{任意の文字列2}に一致するURLの場合に # 1. /srv/public/{任意の文字列1}/api/{任意の文字列2} に一致するファイルが存在すればそれを返し、存在しなければ # 2. /index.php?$query_string にリダイレクトする location ~ /api/ { try_files $uri /index.php?$query_string; } # {任意の文字列}.phpに一致するURLの場合に # 1. /srv/public/{任意の文字列}.php に一致するファイルが存在すれば、そのリクエストをFastCGIに渡し、存在しなければ404を返す # 2. /index.php?$query_string にリダイレクトする location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/run/php/bcm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } }実際のアクセスでどういう処理になるかは下記になります。
順番 どういうアクセスか Nginxはどういう処理をするか 1 初回アクセス URLが /に一致するためsrv/public/dist/index.htmlをブラウザに返却する2 index.htmlの<script>タグに記述された_nuxt/xxxx.jsを取得するアクセスURLが /(_nuxt)/(.+)$に一致するためsrv/public/dist/_nuxt/xxxx.jsをブラウザに返却する3 index.htmlからの /api/xxxというAPIアクセスURLが /api/に一致するためsrv/public/index.phpを経て、Laravelで実装したレスポンスをブラウザに返却するこの nginx.conf でルーティングがどうなるか読み解く鍵は下記かと思います。
- locationの前方一致と正規表現の優先順位の違い
- try_files の挙動
https://heartbeats.jp/hbblog/2012/04/nginx05.html の記事が非常に分かりやすく、参考にさせていただきました。
Typescript対応
必ずしも必要ではありませんが、Nuxt.jsの場合ではTypescriptの導入が容易であるため、ついでに導入します。
公式の導入手順 に従っていけば導入できます。
- 必要ライブラリのインストール
yarn add -D @nuxt/typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser yarn add ts-node vue-class-component vue-property-decorator touch tsconfig.json
nuxt.config.jsの修正
NuxtConfigurationを追記nuxt.config.jsへリネーム- 修正後のnuxt.config.ts
npm run dev後にtsconfig.jsonの修正
"exclude": ["node_modules", "vendor"]を追記"baseUrl": "./client"と修正- 修正後のtsconfig.json
lint対応
parser/pluginsの修正- 修正後の.eslintrc.js
.vueファイルをTypescript対応
- 修正後のindex.vue (
フロントでのモデル/ビジネスロジック実装で新規でpage/componentを作るのでここでの修正は必要ありませんが、参考までに)以上を実行すれば、Typescript でフロントを実装できると思います。
サーバーでのモデル/ビジネスロジック実装
サーバで実装するのは下記の5つになります。
- Bookモデル
- migrationファイル
- Bookレポジトリ
- Bookコントローラ
- ルーティング
(root) ├── app │ ├── Eloquent │ │ └── Book.php => Bookモデル │ ├── Http │ │ ├── Controllers │ │ │ └── BookController.php => Bookコントローラ │ └── Repository │ └── BookRepository.php => Bookレポジトリ ├── database │ └── migrations │ └── 2019_06_12_150818_create_books_table.php => migrationファイル └── routes └── api.php => ルーティングまた、本の登録のAPIリクエストがフロントから来た場合の、フローは下記になります。
それぞれについて解説します。
Bookモデル
Book.php<?php namespace App\Eloquent; use Illuminate\Database\Eloquent\Model; /** * Class Book * @package App\Eloquent * * @property int $id * @property int $title */ class Book extends Model { }本には「ID」と「タイトル」のみ存在するものとして、Bookモデルを定義します。
今回の場合、最低限の記述で良いかと思いますmigrationファイル
database/migrations/2019_06_12_150818_create_books_table.php<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateBooksTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('books', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('title'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('books'); } }BookモデルはActiveRecordであるため、モデルのプロパティと同様のカラムを持たせています。
Bookレポジトリ
BookRepository.php<?php namespace App\Repository; use App\Eloquent\Book; use Illuminate\Support\Collection; class BookRepository { public function find(int $id): ?Book { return Book::query()->find($id); } public function list(): Collection { return Book::query()->get(); } public function create(string $title): Book { $book = new Book(); $book->title = $title; $book->save(); return $book; } public function delete(Book $book) { return Book::query()->find($book->id)->delete(); } }コントローラで直接にデータを更新するのでなく、レポジトリでデータの更新を行い、コントローラはレポジトリのメソッドを呼び出すようにします。
このようにすることで「データが持たせ方が変わった場合(例: Bookの格納をRDSでなくNoSQLに変更する)に、修正範囲を限定させることが出来る」等々のメリットがあります。
Bookコントローラ
BookController.php<?php namespace App\Http\Controllers; use App\Repository\BookRepository; use Dotenv\Exception\ValidationException; use Illuminate\Http\Request; class BookController extends Controller { private $bookRepository; public function __construct(BookRepository $bookRepository) { $this->bookRepository = $bookRepository; } public function list() { return $this->bookRepository->list(); } public function create(Request $request) { $title = $request->get('title'); if (!$title) { throw new ValidationException('titleは必須です'); } return $this->bookRepository->create($title); } public function delete(int $id) { $book = $this->bookRepository->find($id); if (!$book) { throw new ValidationException('削除対象のbookが存在しません'); } $this->bookRepository->delete($book); return []; } }コントローラでは、基本的にレポジトリのメソッドを呼び出すことで、Bookモデルの取得/登録/削除を行います。
また、BookRepositoryはコンストラクタでDIを行うことで、コントローラとレポジトリの依存関係を薄くしています。
こうすることで「ユニットテストでは、特別に用意したBookRepositoryを呼び出すことができ、ユニットテストの書きやすさが向上する」等々のメリットが有ります。ルーティング
api.php<?php use Illuminate\Http\Request; Route::group(['prefix' => 'book'], function ($route) { $route->get('/', 'BookController@list'); $route->post('/create', 'BookController@create'); $route->delete('/delete/{id}', 'BookController@delete'); });本の取得/登録/削除の3つのエンドポイントを用意し、それぞれからコントローラの対応するメソッドを呼びます。
確認
実際にAPIリクエストによってDBが更新されるか確認するためには以下の手順を踏みます。
.envの更新- MySQL に database を追加
- migration実行
今回の場合だと mysqlコンテナの設定に合わせて、下記のように
.envを更新します。.envDB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE=laravel DB_USERNAME=root DB_PASSWORD=
.envを更新した後にphp artisan config:cacheとしてやることで.envの変更を反映させます。その後、appコンテナから mysqlに接続 (
docker-compose exec app mysql -h mysql -uroot) しcreate database laravel;とすることで laravel データベースを作成します。
そして
php artisan migrateと実行することで、migrationが実行できるかと思います。以上の準備をしたあとで、
curl http://localhost:8000/api/book/create -X POST -H "Content-Type: application/json" -d '{"title":"book_test"}'と叩くことで、更新が行われ、そのレスポンスが帰ってくることが確認できるかと思います。
フロントでのモデル/ビジネスロジック実装
フロントでは下記の6つを実装します。
- Bookモデル
- Bookレポジトリ
- plugins/dependency.ts
- index.d.ts
- Axios の proxy
- index.vue
(root) ├── client │ ├── domain │ │ └── Book │ │ ├── Book.ts │ │ └── BookRepository.ts │ ├── pages │ │ ├── index.vue │ ├── plugins │ │ └── dependency.ts │ └── static ├── index.d.ts └── nuxt.config.jsフロントにおけるBook取得/登録のフローは下記です。
なおフロントにレポジトリ層を持たせる設計については、拙作ではありますが こちらのスライド にて詳細を解説していますので、ご参考ください。
Bookモデル
Book.tsimport _ from 'lodash' export default class Book { constructor(protected properties: { [key: string]: any }) {} get id(): string { return _.get(this.properties, 'id') } get title(): string { return _.get(this.properties, 'title') } }サーバーと同様のBookモデルを定義します。
これによってnew Book({id: xxx, titile: yyy})という形式でモデルを作ることができ、このモデルで型宣言を行うことができるようになります。Bookレポジトリ
BookRepository.tsimport _ from 'lodash' import { AxiosInstance } from 'axios' import Book from '~/domain/Book/Book' export default class BookRepository { private axios: AxiosInstance constructor(axios: AxiosInstance) { this.axios = axios } public async listBooks(): Promise<Book[]> { const response = await this.axios.get('/book/') return _.map(response.data, bookData => new Book(bookData)) } public async createBook(title: string): Promise<Book> { const response = await this.axios.post('/book/create', { title: title }) return new Book(response.data) } public async deleteBook(book: Book) { await this.axios.delete(`/book/delete/${book.id}`) } }こちらもサーバーと同様にBookRepositoryを実装します。
メリットについてもサーバーと同様です。コンストラクタで axiso を注入をしていますが、これは後述する
plugins/dependency.tsにて注入を行っています。
注入によって axios の context を維持しつつAPIリクエストを投げられるようになります。plugins/dependency.ts
dependency.tsimport BookRepository from '~/domain/Book/BookRepository' export default (context, inject) => { const bookRepository = new BookRepository(context.$axios) inject('bookRepository', bookRepository) }BookRepositoryに
context.$axiosを渡し、生成されたbookRepositoryを$bookRepositoryとして Vue インスタンス内で呼び出せるように登録しています。このプラグインを
nuxt.config.tsにて呼び出します。nuxt.config.tsplugins: ['~/plugins/dependency'],なお、NuxtにおけるInjectionについては公式ドキュメント と https://tech.cydas.com/entry/nuxt-inject の記事が非常にわかりやすく、参考にさせていただきました。
index.d.ts
dependency.tsにて$bookRepositoryを登録しましたが、このままだと Typescript のチェックでエラーが出るので型定義ファイルに$bookRepositoryに追加します。index.d.tsimport BookRepository from '~/domain/Book/BookRepository' declare module 'vue/types/vue' { interface Vue { $bookRepository: BookRepository } }Axios の proxy
BookRepository にて axios を利用していますが、このままだと 例えば本の登録では、Nginxがリッスンしている
http://localhost:8000/api/book/createでなくhttp://localhost:3000/book/createに対してリクエストをしてしまうので、下記の修正を行います。
- axios.baseURL の指定
下記を追記することで、urlのプレフィックスとして
/apiを付与します。axios: { baseURL: '/api' },
@nuxtjs/proxyの利用nuxt の proxy-module を利用して、axiosからのリクエストを proxy します。
今回の場合は
.envにAPIサーバーのURLを追記して、それをnuxt.config.tsに呼び出すことにします。まず
.envに下記を追加して.envAPI_BASE_URL=http://localhost:8000/api次に
nuxt.config.tsを修正します。nuxt.config.tsimport NuxtConfiguration from '@nuxt/config' require('dotenv').config() const config: NuxtConfiguration = { ... modules: [ // Doc: https://buefy.github.io/#/documentation 'nuxt-buefy', // Doc: https://axios.nuxtjs.org/usage '@nuxtjs/axios', '@nuxtjs/eslint-module', '@nuxtjs/proxy' ], ... } if (process.env.API_BASE_URL) { config.proxy = [process.env.API_BASE_URL] } export default configこれによって axios のリクエストが
http://localhost:8000/apiに行われるようになります。index.vue
最後に index.vue の修正を行います。
index.vue では主に下記の3つの処理を行います。
- asynData() で
$bookRepository.listBooks()を実行し、本の一覧を取得- 登録ボタンクリックで
$bookRepository.createBook(this.title)を実行し、本を登録し、その後に一覧を再取得- 削除ボタンクリックで
$bookRepository.deleteBook(book)を実行し、本を削除し、その後に一覧を再取得index.vue<template> <section> <table class="table"> <tr> <th>ID</th> <th>タイトル</th> </tr> <tr v-for="book in books" :key="book.id"> <td>{{ book.id }}</td> <td>{{ book.title }}</td> <td> <button type="button" class="button is-primary" @click.prevent="deleteBook(book)" > 削除する </button> </td> </tr> </table> <section class="modal-card-body"> <b-field label="本のタイトル"> <b-input v-model="title" type="input" placeholder="タイトル" /> </b-field> <button type="button" class="button is-primary" @click.prevent="createBook()" > 登録する </button> </section> <p v-if="error">{{ error }}</p> </section> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator' import Book from '~/domain/Book/Book' @Component({ async asyncData({ app }) { const books = await app.$bookRepository.listBooks() return { books } } }) export default class extends Vue { private books private title = '' private error = '' async createBook() { try { await this.$bookRepository.createBook(this.title) this.books = await this.$bookRepository.listBooks() this.title = '' } catch (e) { this.error = e } } async deleteBook(book: Book) { try { await this.$bookRepository.deleteBook(book) this.books = await this.$bookRepository.listBooks() } catch (e) { this.error = e } } } </script>確認
以上のフロント/サーバーでの実装が完了した後に
- docker-compose up -d
- npm run dev
を実行すれば、
http://localhost:3000/にてシステムが動作してることが確認できるかと思います。Heroku設定
herokuの設定としては、下記の5つを実装します
infra/nginx/nginx.conf/Procfileの追加- heroku buildpack の追加
- Clear DB の設定
app/Providers/AppServiceProvider.phpの修正- 環境変数の設定
(root) ├── Procfile ├── app │ └── Providers │ └── AppServiceProvider.php └── infra └── heroku └── nginx.confなお
heroku createによるherokuとの連携は完了してる前提です。
infra/nginx/nginx.conf/Procfileの追加herokuで Nginx を使うために
infra/nginx/nginx.confを herokuの公式ドキュメント に従って、新しく作成します。
- location ディレクティブのみを使う
fastcgi_passはheroku-fcgiを指定するという点を反映させると、下記のようになると思います。
nginx.conflocation / { try_files $uri /public/dist/index.html?$query_string; } location ~ /_nuxt/(.+)$ { try_files $uri /public/dist/_nuxt/$1; } location ~ /api/ { try_files $uri /public/index.php?$query_string; } location ~ \.php$ { try_files $uri /public/index.php =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass heroku-fcgi; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }また、上記の
nginx.confを利用するように、下記のProcfileを追加します。Procfileweb: vendor/bin/heroku-php-nginx -C infra/heroku/nginx.confProcfileの役割については 公式ドキュメント に記載が有ります。
heroku buildpack の追加
heroku にデプロイは
git push heroku masterで行いますが、その push 後の起動処理を行うためにビルドパックを追加します。heroku buildpacks:add heroku/nodejs heroku buildpacks:add heroku/phpこれによって、フロントのビルドや必要ライブラリのインストールを実行されます。
Clear DB の設定
heroku でも mysql を利用するために ClearDB を利用したいと思います。
まず下記コマンドを叩きます。
heroku addons:add cleardbすると
CLEARDB_DATABASE_URLという変数がheroku configに設定されると思います。
このCLEARDB_DATABASE_URLは下記のように読み解けます。mysql://{DB_USERNAME}:{DB_PASSWORD}@{DB_HOST}/{DB_DATABASE}?reconnect=trueそこで下記のようにHerokuの環境変数にセットすることで、laravelからDBアクセスを行えるようになります。
heroku config:set DB_HOST={DB_HOST} heroku config:set DB_DATABASE={DB_DATABASE} heroku config:set DB_USERNAME={DB_USERNAME} heroku config:set DB_PASSWORD={DB_PASSWORD}以上で、本来ならば Mysql接続が完了してほしいところですが、laravel の想定してる Mysql のバージョンとHerokuのMysql のバージョンが異なっているためか、migration 実行時にエラーが発生します。
そのため、下記のように修正を行います。
AppServiceProvider.php<?php namespace App\Providers; use Illuminate\Support\Facades\Schema; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { Schema::defaultStringLength(191); } }こちらの対応は https://qiita.com/beer_geek/items/6e4264db142745ea666f を参考にさせていただきました。
確認
以上の準備が完了した後に
git push heroku masterheroku run php artisan migrateを実行すれば、実際にブラウザで動作していることが確認できると思います。
終わりに
以上で、Lareavel+Nuxt.jsで作ったwebアプリをHerokuにデプロイさせることが出来ました。
記事の内容で誤ってる点や追記が必要な点も多いと思いますが、その際にはコメントや編集リクエストでご指摘いただければと思います。
この記事がWeb開発における0から1へのフェーズに学習する方の参考になれば幸いです。
参考
- https://readouble.com/laravel/5.8/ja/
- https://ja.nuxtjs.org/
- https://www.php.net/manual/ja/install.fpm.configuration.php
- https://github.com/nuxt/nuxt.js/issues/3217
- https://heartbeats.jp/hbblog/2012/04/nginx05.html
- http://kimh.github.io/blog/jp/docker/gothas-in-writing-dockerfile-jp/#hack_to_run_container_in_the_background
- https://tech.cydas.com/entry/nuxt-inject
- https://devcenter.heroku.com/articles/custom-php-settings#using-a-custom-application-level-nginx-configuration
- https://qiita.com/kuni-nakaji/items/d11219e4ad7c74ece748
- https://qiita.com/beer_geek/items/6e4264db142745ea666f
- https://speakerdeck.com/kon_shou/shi-yun-yong-niokerularaveltonuxtdefalserepositoryfalsereiyafen-ge-falsehua





