20201117のlaravelに関する記事は8件です。

【Laravel基礎】フォームの入力内容を制限する方法【バリデーションとフォームリクエスト】

概要

Laravel学習で自分がつまずいた部分を備忘録としてまとめています。
今回はフォームの入力内容の制限に関する記事です。

つまずいたポイント

指定条件以外のフォーム入力を弾いてくれるバリデーション
そんなバリデーションですが、アクション事に毎回指定するのが面倒くさい。。
効率的な方法が無いか調べました。

どうやら、フォームリクエストなるものを使うと
一括でバリデーションを指定出来るとのこと。

バリデーションの復習もかねて、下記の2つを記事にしました。

①バリデーション

②フォームリクエスト

以下、記事内容です!

バリデーション

まずはバリデーションの復習。
保存や編集などの、フォーム内容を受け取るアクション内で、
$this->validate($request,[条件]);を入力。
第2引数の入力内容が、フォームの入力条件になります。

■コントローラ

PostsController
    public function store(Request $request) {  

      //バリデーションで条件を指定
       $this->validate($request, [
        'title' => 'required|min:3',
        'body' => 'required'
      ]);

      $post = new Post();
      $post->title = $request->title;
      $post->body = $request->body;
      $post->save();
      return redirect('/');
    }

上記の場合だと、
title => 3文字以上 , body => 入力必須
て感じですね。
この条件以外の内容がフォームから送信されると、バリデーションが弾いてくれます。

フォームリクエスト

次はフォームリクエストです。
バリデーションはアクション毎にフォームの入力内容を指定していましたが、
フォームリクエストではそれを一括で指定することが出来ます。
以下、手順です。

ターミナル
php artisan make:request PostRequest

フォームリクエストはphp artisan make:request 〜Requestで作成出来ます。
今回はPostRequestと名付けました。
作成したRequestはApp\Http\Requestsの中にあります。

■フォームリクエスト

PostRequest.php
   //①
   public function rules()
    {
        return [
            'title' => 'required|min:3',
            'body' => 'required'
        ];
    }

    //②
    public function messages() 
    {
        return [
            'title.required' => 'タイトルを入力して下さい。'
       ];
    }

フォームリクエスト上での主な指定内容は2つ。
1つは入力内容の条件、もう1つはバリデーションのメッセージです。

まずは入力内容の条件から。rulesメソッドを使う事で条件指定できます。(①)
中身は先ほどのバリデーションと同じですね。

そしてmessagesメソッドでは、
バリデーション適用時のメッセージを指定する事ができます。(②)
デフォルトでは英文が指定されているので、わかり易くするには日本語に変えておいた方が良いかもですね。

■コントローラー

PostsController
//①
use App\Http\Requests\PostRequest;

    //②
    public function store(PostRequest $request) {   
      $post = new Post();
      $post->title = $request->title;
      $post->body = $request->body;
      $post->save();
      return redirect('/');
    }


コントローラでは、フォームリクエストをuseで使えるようにしましょう。(①)
そしてアクションの引数にフォームリクエストを入力。(②)
フォームリクエストで指定した内容が適用されるようになります。
バリデーションを書かないで良いので楽ですね。

フォームリクエストは以上です!
1.ターミナルからフォームリクエストを作成
2.フォームリクエストの条件を指定
3.コントローラ+アクションでフォームリクエストを適用
という流れですね。

ちなみに、何でアクションの引数にrequestを入れるだけでフォーム内容が受け取れるのか?
疑問に思って調べましたが、「サービスコンテナ」やら「メソッドインジェクション」やら「DI(Dependency Injection = 依存性注入)」なる単語まで出てきて、脳がフリーズしたので一旦忘れます。いつかちゃんと調べて記事にします。多分。。。

まとめ

以上、Laravelでフォームの入力内容を制限する方法でした。
最後まで読んで頂きありがとうございました。
フォームリクエストの方がコードの量が少なくなるので楽ちんですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

5分で作るDocker+Laravel PHP+Vue.js開発環境構築

開発環境
macOS Big Sur

下記の記事を参考に環境構築を行ったのですが、
やれwgetコマンドが無いだの、apt-getコマンドが無いだの初心者の方への導入への障壁がいくつかあったため、
それを乗り越えるための記事にしたいと思います。(※注:下記の記事は決して悪くありません。)

タイトル リンク
5分で作るLaravel+Vue.js開発環境(docker-compose) https://qiita.com/yusukeito58/items/37bd551560e495dbd1b8

wgetコマンドインストール

タイトル リンク
Mac - wgetコマンドをインストール(使えるようにする) https://qiita.com/th4inf/items/f85c1b91065d85af67b9

apt-getコマンドインストール ←apt-getコマンドが無いかと思い調べてが結果的にいらないのでやらんで良い

タイトル リンク
install apt-get to Mac https://qiita.com/th4inf/items/f85c1b91065d85af67b9

docker-compose up -d --build時にmysql-clientのエラーがでたら

エラー内容

E: Package 'mysql-client' has no installation candidate
ERROR: Service 'app' failed to build : The command '/bin/sh -c apt-get update     && apt-get install -y zlib1g-dev mysql-client libpng-dev libjpeg-dev gnupg curl wget     && docker-php-ext-configure gd --with-png-dir=/usr/include --with-jpeg-dir=/usr/include     && docker-php-ext-install zip pdo_mysql gd' returned a non-zero code: 100
タイトル リンク
docker-compose buildするときにbundle installやmysql-clientでコケた話 https://qiita.com/aseanchild1400/items/d3580366054fee3d2703

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS BeanstalkでLaravelをデプロイするときにCloudwatch Logsにログを転送する

前提のBeanstalk環境

PHP 7.4 running on 64bit Amazon Linux 2/3.1.3

やること

  • lavavel.logのパーミッション設定
  • EC2にCloudwatch Logsのロググループ作成のためのサービスロールを追加
  • 設定ファイル(.ebextensions)の作成

1. lavavel.logのパーミッション設定

$ chmod 0664 /var/www/html/storage/logs/laravel.log

2. lavavel.logのパーミッション設定

  • Beanstalk環境のIAM インスタンスプロフィールに設定されているIAMロールに、CloudWatchLogsFullAccessのポリシーを追加 (Codepipelineでデプロイしている場合は、そのロールにも同様にCloudWatchLogsFullAccessのポリシーを追加)

3. 設定ファイル(.ebextensions)の作成

  • PHP Platformでデフォルトで追加されるログの設定

参考: https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/AWSHowTo.cloudwatchlogs.html

.ebextensions/4-eblog.config
option_settings:
  - namespace: aws:elasticbeanstalk:cloudwatch:logs
    option_name: StreamLogs
    value: true

  - namespace: aws:elasticbeanstalk:cloudwatch:logs
    option_name: DeleteOnTerminate
    value: false

  - namespace: aws:elasticbeanstalk:cloudwatch:logs
    option_name: RetentionInDays
    value: 7
  • カスタムログでlaravel.logを設定する

参考: https://github.com/awsdocs/elastic-beanstalk-samples/blob/master/configuration-files/aws-provided/instance-configuration/logs-streamtocloudwatch-linux.config

.ebextensions/5-laravellog.config
packages:
  yum:
    awslogs: []

files:
  "/etc/awslogs/awscli.conf" :
    mode: "000600"
    owner: root
    group: root
    content: |
      [plugins]
      cwlogs = cwlogs
      [default]
      region = `{"Ref":"AWS::Region"}`

  "/etc/awslogs/awslogs.conf" :
    mode: "000600"
    owner: root
    group: root
    content: |
      [general]
      state_file = /var/lib/awslogs/agent-state

  "/etc/awslogs/config/logs.conf" :
    mode: "000600"
    owner: root
    group: root
    content: |
      [/var/www/html/storage/logs/laravel_log]
      log_group_name = `{"Fn::Join":["/", ["/aws/elasticbeanstalk", { "Ref":"AWSEBEnvironmentName" }, "var/www/html/storage/logs/laravel_log"]]}`
      log_stream_name = {instance_id}
      file = /var/www/html/storage/logs/laravel*

commands:
  "01":
    command: systemctl enable awslogsd.service
  "02":
    command: systemctl restart awslogsd

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel passportのログイン失敗のエラーを飛ばさないようにする

Laravel passportを使うときデフォルトでは、ユーザーのログイン認証に失敗するだけでエラーを投げるようになっている。
エラートラッキングツール(Sentryなど)を使っていると、ユーザーがログイン失敗するだけで通知が来てしまうのが面倒。

Handler.pnpに、下記のように追記するとエラーが飛ばないようにできる。

app/Exceptions/Handler.php
protected $dontReport = [
    \Laravel\Passport\Exceptions\OAuthServerException::class,
];
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel基礎】データの受け渡し方 【①ルートパラメータ②Implicit Binding】

概要

Laravel学習で自分がつまずいた部分を備忘録としてまとめています。
今回はデータの受け渡しに関する記事です。

つまずいたポイント

記事投稿型の簡易アプリ作成時につまずいた問題です。

記事一覧ページ→記事の詳細ページに移る際、
ビュー→コントローラに投稿のデータを渡すにはどうしたら良いのか?
下記2つの方法で解決出来たので記事にしてみました。

①ルートパラメータ

②Implicit Binding

以下、記事内容です!

ルートパラメータ

まず渡し方の1つがルートパラメータを用いた方法です。

■ビュー

index.blade.php
 @foreach ($posts as $post)
    <li>
       <a href="/posts/{{ $post->id }}">{{ $post->title }}</a>
    </li>
 @endforeach

まず、ビュー上でURLを用いて、ルーティングに投稿のIDを渡します。

■ルーティング

web.php
Route::get('/posts/{id}','PostsController@show')

ここからがルートパラメータを用いた方法です。URLの中に{ }を設定します。
この{ }の中身がパラメータと言う扱いになり、アクション内でこのパラメータが使用可能になります。
仮で{ }の中身を{id}と設定していますが、中身にはビューで設定した$post->idが入ってきます。
※{ }は変数扱いなので、中身の文字はわかりやすい単語でOK

【例】'/post/test'でアクセスした場合 → 「test」がパラメータ扱い
   '/post/1'でアクセスした場合  → 「1」がパラメータ扱い

■コントローラ

PostsController
    //アクションの引数にパラーメータを設定
    public function show($id) {
          //パラメータをアクション内で使用出来る
          $post = Post::find($id);
          return view('posts.show')->with('post', $post);
        }

アクションの引数の中に先ほどのパラメータを代入する事で、
アクション内でそのパラメータが使用可能になります。

上記のshowアクションでは、
パラメータを元にfindメソッドで該当の記事データを再取得→
記事データを渡しつつ投稿の詳細画面を表示することが出来ました。

Implicit Binding

次はImplicit Bindingです。
ルートパラメータの場合、URLに書かれた文字列だけをコントローラに渡しました。
Implicit Bindingの場合は、投稿を丸ごと渡すことが出来ます。
下記、その手順です。

■ビュー

index.blade.php
@foreach ($posts as $post)
    <li>
       <a href="/posts/{{ $post }}">{{ $post->title }}</a>
    </li>
@endforeach

URLに$postを指定します。

■ルーティング

web.php
Route::get('/posts/{post}', 'PostsController@show')

先述のルートパラメータで{post}を指定します。

■コントローラー

PostsController
public function show(Post $post) {
              return view('show')->with('post',$post);
            }

アクションの引数を(Post $post)に指定→投稿の中身を$postで使用できます。

先ほどと違い、アクション内で投稿を再取得せずに$postをそのまま記事詳細画面に渡すことが出来ました。楽ちんですね。

まとめ

以上、Laravelにおけるビュー→コントローラへデータの渡し方でした。
Implicit Bindingの方が書くコードの量が少なくなるので効率的ですね。

最後まで読んで頂きありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

InterventionImageで編集した画像ファイルをbase64変換してMySQLに保存する

やりたいこと

InterventionImageで編集した画像ファイルをバイナリーに変換してMySQLに保存したい
もちろん画像ファイルは/public配下やS3などのファイルシステムに保存するのが定石なのは承知の上で、今回は手軽さを優先してDBに画像ファイル自体をtextカラムに保存する

環境


% sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.7
BuildVersion:   19H2

% docker version
Client: Docker Engine - Community
 Cloud integration: 1.0.1
 Version:           19.03.13
 API version:       1.40

# php -v
PHP 7.4.7 (cli) (built: Jun 11 2020 18:41:17) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Xdebug v2.9.6, Copyright (c) 2002-2020, by Derick Rethans

# php artisan -v     
Laravel Framework 7.26.1

# mysql --version
mysql  Ver 14.14 Distrib 5.7.30, for Linux (x86_64) using  EditLine wrapper

Intervention Image とは

ImageMagikの上位互換?のモダンな画像編集パッケージ
リサイズ、トリミング、フィルターとなんでも手軽にできるっぽい
詳しくは公式doc参照

MySQLに画像バイナリを保存する

UserController
    public function update(Request $request, User $user)
    {
        // InterventionImageで加工&保存
        $file = $request->file('profile_img');
        $img = Image::make($file);
        $img->fit(400); //400px * 400px にトリム&リサイズ
        $bin = base64_encode($img->encode('png'));

        $user->profile_img = $bin;
        $user->save();
        return redirect()->route('user.index');
    }

ポイントは$bin = base64_encode($img->encode('png'));の1行。
base64_encodeする前に一度InterventionImageインスタンスをエンコードしている。
この処理がないとbase64_encodeしても空文字が生成される。なぜかはよくわからない。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google App Engine x Laravel でroute:cache config:cacheを使う

キャッシュしてサイト高速化がしたかった

php artisan config:cache
php artisan route:cache

しかし、サイトにアクセスするとエラーになる

Read-onlyの場所に書き込みがあったぞ的な。

PHP message: PHP Fatal error:  Uncaught ErrorException: file_put_contents(/workspace/storage/dxcea6x78780x7e92x554x5f6dxade76x0c0x0ce.php): failed to open stream: Read-only file system in /workspace/vendor/laravel/framework/src/Illuminate/Filesystem/Filesystem.php:135

app.yamlに追記すれば解決

app.yaml
APP_SERVICES_CACHE: /tmp/services.php
APP_PACKAGES_CACHE: /tmp/packages.php
APP_CONFIG_CACHE: /tmp/config.php
APP_ROUTES_CACHE: /tmp/routes.php

無事、キャッシュ利用してサイト高速化ができました。
参考サイト:https://kido0617.github.io/php/2019-12-17-service-env/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelのget()とfirst ()が曖昧だったので取得データを見てみた

LaravelのDB検索した後のデータの扱い時、getとfirstの扱いが曖昧だったため確認して見ました。

前提

例えばid、title、bodyを持つpostデータのテーブルがあったとします。
で、そのidを持つpostを検索し、そのpostのtitleを返すコントローラーを作りたいとします。

最初の状態。get()でデータを取得した。

public function post($id){
        $post = Post::where('id',$id)->get();//whereで引数の$idと同一のpostを検索。
        Log::debug($post);//このログの出力:[{"id":5,"title":"hoge","body":"hoge"}]
        return $post->title;
       }
//詳細は省略しております

が、上記のように「$post->title」を返そうとするとそんなプロパティないよ!と怒られエラーとなりました。

local.ERROR: Property [title] does not exist on this collection instance.

なぜだ?ちゃんと出力データの中には「title」あるのに!と思ってgetやfirstの取って来ているデータを調べました。

原因

で、get()ではなくfirst()にするとうまくいったので、
それぞれのwhere直後の$postを見てみると、こんな状態になってました。

first()で取得した時のデータ

{"id":5,"title":"hoge","body":"hoge"}

get()で取得した時のデータ

[{"id":5,"title":"hoge","body":"hoge"}]

何が違う?

get()の方もfirstの方も、今回「id」でwhere検索しているので、1件しか取得してないのですが
getの方は[]で囲われています。
例えば
[
{"id":9,"title":"hoge","body":"hoge"},
{"id":1o,"title":"fuge","body":"fuge"},
]
のように数件取得して、foreachで一件ずつに「->title」で呼び出すなら問題ない、
複数件前提のデータということになります。

今回のように1件だけ決めうちで取りたいならfirst()を使うべきでした。
こうするとうまく動きました!

   $post = Post::where('id',$id)->first();

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む