20210108のlaravelに関する記事は9件です。

ルーティングちょっと書く(Laravel)

はじめに

今回はルーティング記述しついで対応するコントローラとメソッドをコマンドで作成するところまで、軽く基本をおさらいします。

ルーティングとは

ルーティングとは〜にアクセスされたら〜コントローラーの〜アクションメソッドを実行する、という処理の流れを表すものです。

routes/web.phpにRouteファサードを使って記述します。たとえば
Route::get('/user', 'UserController@index');
getメソッドを使うとこんな感じです。処理の流れとしては、/userにアクセスされた時、UserControllerコントローラのindexアクションメソッドが実行されます。

Route::get
Route::post
Route::put
Route::patch
Route::delete

などが同様に使えます。

書式は以下の通り
Route::get('path', クロージャ); // クロージャの中にpathに対応する処理を書く
Route::get('path', 'コントローラ名@アクション名');

ルーティングを記述

実はLaravelでは、よく使われる機能のルーティング(CRUDルーティング)をひとまとめにしたメソッドがデフォルトで用意されています!

Route::resourcelaravel/routes/web.phpに追加すると下記7つの処理を定義するルートが全て登録できます!

  • 一覧表示(index)
  • 個別表示(show)
  • 登録画面表示(create)
  • 登録処理(store)
  • 更新画面表示(edit)
  • 更新処理(update)
  • 削除処理(destroy)

ためしてみましょう。

laravel/routes/web.php
<?php

Auth::routes();
Route::resource('/articles', 'ArticleController'); //この行を追加

下記コマンドでルーティングが追加できたか確認・・

$ php artisan route:list

追加できてる!!

+--------+-----------+-------------------------+------------------+------------------------------------------------------------------------+------------+
| Domain | Method    | URI                     | Name             | Action                                                                 | Middleware |
+--------+-----------+-------------------------+------------------+------------------------------------------------------------------------+------------+
|        | POST      | articles                | articles.store   | App\Http\Controllers\ArticleController@store    登録処理                       | web        |
|        | GET|HEAD  | articles                | articles.index   | App\Http\Controllers\ArticleController@index    一覧表示                       | web        |
|        | GET|HEAD  | articles/create         | articles.create  | App\Http\Controllers\ArticleController@create   登録画面表示                       | web        |
|        | DELETE    | articles/{article}      | articles.destroy | App\Http\Controllers\ArticleController@destroy  削除処理                       | web        |
|        | PUT|PATCH | articles/{article}      | articles.update  | App\Http\Controllers\ArticleController@update   更新処理                       | web        |
|        | GET|HEAD  | articles/{article}      | articles.show    | App\Http\Controllers\ArticleController@show     個別表示                       | web        |
|        | GET|HEAD  | articles/{article}/edit | articles.edit    | App\Http\Controllers\ArticleController@edit   更新画面表示

{article}の部分には更新対象とするArticleモデルのid
例えば、idが3である記事の更新画面を表示するURLは、開発環境ではlocalhost/articles/3/editとなります。

コントローラーとメソッドも作る

php artisan make:controller ArticleController --resourceで対応するコントローラとメソッドを自動生成!

まとめ

今回はルーティング記述しついで対応するコントローラとメソッドをコマンドで作成するところまで。最近はLaravelを触りたくてしょうがないです。また投稿します。

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

Laravelでマイグレーション時にSQLSTATE[42000]: Syntax error or access violation: 1067 Invalid default valueとエラー時の解消法

はじめに

今回はわたしが遭遇したエラーと解消法についてご紹介したいと思います。
なかなか解決法が見つからなかったのですが、案外あっさりとした内容でした。備忘録の意味も含めて書きたいと思います。

-各バージョン
-laravel 6.x
-PHP 7.4.9
-mySQL 5.7.30

どのようなエラーが出たか

Laravelでマグレーションをした時に、発生したエラーです。
以下がエラー内容

Illuminate\Database\QueryException  : SQLSTATE[42000]: Syntax error or access violation: 1075 Incorrect table definition; there can be only one auto column and it must be defined as a key (SQL: create table `stock_cosmes` (`stock_id` bigint unsigned not null auto_increment primary key, `product` varchar(100) not null, `color` varchar(100) not null, `brand` varchar(100) not null, `price` int not null auto_increment primary key, `purchaseDate` date not null, `category` varchar(255) not null, `created_at` timestamp null, `updated_at` timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci')

  at /Users/yuzu/Desktop/jewelry/vendor/laravel/framework/src/Illuminate/Database/Connection.php:669
    665|         // If an exception occurs when attempting to run a query, we'll format the error
    666|         // message to include the bindings with SQL, which will make this exception a
    667|         // lot more helpful to the developer instead of just the database's errors.
    668|         catch (Exception $e) {
  > 669|             throw new QueryException(
    670|                 $query, $this->prepareBindings($bindings), $e
    671|             );
    672|         }
    673|

  Exception trace:

  1   PDOException::("SQLSTATE[42000]: Syntax error or access violation: 1075 Incorrect table definition; there can be only one auto column and it must be defined as a key")
      /Users/yuzu/Desktop/jewelry/vendor/laravel/framework/src/Illuminate/Database/Connection.php:463

  2   PDOStatement::execute()
      /Users/yuzu/Desktop/jewelry/vendor/laravel/framework/src/Illuminate/Database/Connection.php:463

  Please use the argument -v to see more details.
yuzunoMacBook-Air:jewelry yuzu$ -v
-bash: -v: command not found

そして、こちらが作成しようとしたテーブルです。

public function up()
    {
        Schema::create('stock_cosmes', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('product', 100);
            $table->string('color', 100)->nullable();
            $table->string('brand', 100)->nullable();
            $table->integer('price', 6)->nullable();
            $table->date('purchaseDate')->nullable();
            $table->string('category');
            $table->timestamps();
        });
    }

題名にあるエラー文でググってみたのですが、bigIncrementsは主キーにしなくてはいけないや、nullにできないといった解説が多く、わたしのエラー原因に一致するものが見つかりませんでした。
そこで、Laravelの公式サイトを読み直してみると、解決することができました!

解決法

とても初歩的な内容で恥ずかしいのですが、指定しているintegerには、第二引数を設定することができないというのが、今回のエラー原因でした。
integerにも字数制限をする場合には、length()で指定できるようです。

Laravel 6.x データベース:マイグレーション

以下、修正したテーブルです。

public function up()
    {
        Schema::create('stock_cosmes', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('product', 100);
            $table->string('color', 100)->nullable();
            $table->string('brand', 100)->nullable();
            $table->integer('price')->length(6)->nullable();
            $table->date('purchaseDate')->nullable();
            $table->string('category');
            $table->timestamps();
        });
    }

こちらで試したところ、問題なくエラーが解消されました!

おわりに

integerは第二引数を使用できない。とは書かれておらず、stringと同じ要領で使用してしまいましたが、改めて、公式サイトをきちんと読むことの大切さを思い知らされました。

同じようなエラーに悩まされている方のお力になれたら嬉しいです!

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

Laravel5/6/7でGoutteを使用する時はバージョン指定しないとエラーになるよ。

タイトル通りです。

Laravel5/6/7系にてGoutteを使用したい場合はインストール時にバージョン指定してあげないとエラーが出ます。

composer require weidner/goutte "1.6"

たぶんこれで、エラーなくインストールできるはず。

ちなみに、バージョン違いのエラーが出た時は使用したいライブラリのREADME.mdを見れば、大抵どっかに書かかれています。

今回の場合はこちらの最後に書かれていました!

感謝感謝(´ω`)ノ

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

laravelで画像を配列で保存し、vue.jsで表示

ポートフォリオでinstagramのような写真共有のアプリを作る際に初心者の俺はとりあえず思いついたアイデアで写真の保存と表示をやってみた。
今後改善していく予定なので、現在の仕様をとりあえず記念に掲載。

post.vue
<template>
    <div>
        <input ref="input" type="file" multiple @input="load">
    </div>
    <div>
        <button @click="submit">送信</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            base64Images: '',
        }
    },
    methods: {
        load(){
            const files = this.$refs.input.files;//ref="input"の要素のfiles
            var uploadedBase64Images = [];//複数のimageのアップロードを想定し、格納する配列を準備
            for (var i = 0; i < files.length; i++) {//for文をfilesの数だけ回すためにfiles.length
                const reader = new FileReader();
                let that = this;
                reader.onload = (function (event) {
                    postArray.unshift(event.target.result);//unshiftは先頭にデータを入れる。pushは末尾
                });
                reader.readAsDataURL(files[i]);
            }
            this.base64Images = postArray;
            //https://qiita.com/popo62520908/items/d707ff2010be78f97a50 でreaderの復習
        },
        submit(){
            let fd= new FormData();//fileを使うため
            fd.append("images", JSON.stringify(this.base64Images));//postを使うときは、JSON.stringify()を使って配列を文字列に
            axios.post('/api/create', fd)
            .then(
                response => (window.location.href = '/')//処理が完了後homeに戻す
            )
            .catch(function (error) {
                console.log(error);
            });
        },
    }
}
</script>

formでfileを含む時のContent-Typeは、multipart/form-dataになるので、new FormData()を使います(fileは文字、画像など様々な種類のデータを取り扱うため)。
とどこかのサイトに書いてあり、postはJSON.stringfy()で文字列に変換しなければと書いてあったのだが、以下のやり方でもいけた。

post.vue
<script>
export default {
    data() {
        return {
            base64Images: '',
        }
    },
    methods: {
        submit(){
            var param = {
                images: this.base64Images,
            }
            axios.post('/api/posts', param)
            .then(
                response => (window.location.href = '/')//処理が完了後homeに戻す
            )
            .catch(function (error) {
                console.log(error);
            });
        },
    }
}
</script>

次にサーバー側のデータ受け取り、保存。

PostController
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Post;
use Illuminate\Support\Facades\Storage;
class PostController extends Controller
{
    public function index() {

        return view('post');
    }

    public function create(Request $request)
    {
        $post = new Post;//インスタンス作成
        $decoded_images = json_decode($request->images);//文字列化されたjsonを配列に戻す。
        $src_array = [];//この中に保存するファイル名を格納し、データベースに保存する。
        for ($i=0; $i<count($decoded_images); $i++) {//vueで作ったload()関数と似た感じの処理。
            $base64_image = $decoded_images[$i];
            @list($type, $file_data[$i]) = explode(';', $base64_image);//explodeで$base64_imageないの文字列を分割し、list関数で分割したデータを変数$typeと$file_data[$i]に格納。
        //つまりdata:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAA....が
        //$type = 'data:image/jpeg;';
        //$file_data[$i] = 'base64,/9j/4AAQSkZJRgABAQEASABIAA....';
        //ということ
            @list(, $file_data[$i]) = explode(',', $file_data[$i]);
        //更にこの処理によって
        //$file_data[$i] = 'base64,/9j/4AAQSkZJRgABAQEASABIAA....';が
        //$file_data[$i] = '/9j/4AAQSkZJRgABAQEASABIAA....';
        //になる
            $imageName = str_random(10).'.'.'png';//$file_data[$i]の名前を作成。str_random()関数を作ってランダムに
            Storage::disk('local')->put($imageName, base64_decode($file_data[$i]));//diskは保存場所。詳しくは/config/filesystems.phpで設定。
//localに$imageNameという名前でbase64_decode($file_data[$i])のデータを保存しますということ。
//use Illuminate\Support\Facades\Storage;忘れずに
            $array[] = $imageName;//この$imageNameを配列化
            $src_array += $array;//格納
        }
        $post->image = json_encode($src_array);//$src_arrayは['RgABAQEASA.png','SkZJRgABAQ.png']こんな感じで、このままでは保存できないのでjson_encodeで文字列、"['RgABAQEASA.png','SkZJRgABAQ.png']"に変換。
        $post->save();//そして保存
    }
}

いい記事発見したので掲載
https://qiita.com/PlanetMeron/items/2905e2d0aa7fe46a36d4
list関数はこちらを
https://www.sejuku.net/blog/24406

ちなみにJSの new FormDataを使わない場合

PostController
    public function create(Request $request)
    {
        $post = new Post;
        $post->save();
        $decoded_images = $request->images;
//$decoded_imagesも何も、最初から文字列にエンコードされていないのだが、以下の書き換えが面倒なので変数$decoded_imagesに格納
        $src_array = [];
        for ($i=0; $i<count($decoded_images); $i++) {
            $base64_image = $decoded_images[$i];
            @list($type, $file_data[$i]) = explode(';', $base64_image);
            @list(, $file_data[$i]) = explode(',', $file_data[$i]);
            $imageName = str_random(10).'.'.'png';
            Storage::disk('local')->put($imageName, base64_decode($file_data[$i]));
            $array[] = $imageName;
            $src_array += $array;
        }
        $post->image = json_encode($src_array);
        $post->save();
    }

当時は配列ではなくオブジェクトで保存していた。
そもそも配列とオブジェクトの違いもわからなかった。
本当に懐かしい。笑

show.vue
<template>
    <div>
        <div v-for="(parsedUserPost, index) in parsedUserPosts">
            <v-carousel>
                <v-carousel-item v-for="(image,i) in parsedUserPost.image" :key="i" :src="'storage/image/' + image"></v-carousel-item>
            </v-carousel>
        </div>
    </div>
</template>
<script>
export default {
    data() {
        return {
            userPosts: '',//←propsおよびajaxでの値を受け取っている前提
        }
    },
    computed: {
        parsedUserPosts(){
            var userPosts = this.userPosts;
            if(userPosts.length == 0){
                return false;//this.userPostsがundefinedの歳エラーが出るためこの処理
                } else {
                for(var i=0; i<userPosts.length; i++){
                    userPosts[i].image = JSON.parse(userPosts[i].image);//JSON.parse()を使って文字列だった配列をもとに戻す。
//"['RgABAQEASA.png','SkZJRgABAQ.png']"こんな感じで保存されているので、['RgABAQEASA.png','SkZJRgABAQ.png']に戻してあげる。
                }
                return userPosts;
            }
        },
    }
}
</script>

JSON.parseを使っているが、コントローラーでjson_decode()を使っても行けるはず。
これだけですごい勉強になった気がするな。当時の思いつきで突き進んだ結果。

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

【Laravel】リクエストバリデーションをコントローラから分離させる!

コントローラーでのバリデーション処理の書き方

laravelでは以下のようにバリデーションを設定することができます。

SampleController.php
public function store(Request $request) 
{
    // validation ruleを作成
    $rules = [
        'name' => ['required', 'string'],
        'user_id' => ['required', 'integer'],
        'content_id' => ['sometimes', 'integer'] 
    ]

    $this->validate($request, $rules);

    // メイン処理へと続く
}

みたいな感じで書くことでバリデーションを行ってくれます。
各パラメータに対応する形でルールを書いていくことで一つ一つ細かくバリデーションすることができます。
細かいルールに関しては公式ドキュメント参照→https://readouble.com/laravel/5.8/ja/validation.html

バリデーションを通過すれば後続処理へと渡し、通過できなかった時は適切なレスポンスを生成して前のページへと戻してくれます。

バリデーション処理はコントローラーから分離させたい

上記のようにコントローラーやサービスクラスの中でバリデーションを記述しても良いですが、なるべくコントローラに書くコードは少なくした方がコード的にはイケてますよね。

また、バリデーションだけ分離すれば可読性が上がり、使い回しもしやすくなります。
具体的には下記のように書くことでバリデーション処理をコントローラーから分離させることができます。

まずはRequestClassを作成しましょう。下記のarstisanコマンドを打つとRequestクラスを継承したクラスを作成することができます。

php artisan make:request SampleRequest
SampleRequest.php
namespace App\Http\Requests;

use Illuminate\Http\Request;
use Illuminate\Foundation\Http\FormRequest;

class SampleRequest extends FormRequest
{
     /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        // 初期の状態ではfalseを返すようになっているため、機能させたい場合はtrueに変更してください
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
          'name' => ['required', 'string'],
          'user_id' => ['required', 'integer'],
          'content_id' => ['sometimes', 'integer'] 
        ];
    }
}

コントローラー側ではこのSampleRequestクラスを通過するように書いてあげれば元のコードと同じようにバリデーションをかけることができます。

SampleController.php
public function store(SampleRequest $request) 
{
    // メイン処理へと続く
}

これでコントローラーからリクエストバリデーションのロジックを分離させることができました。

複雑なバリデーション処理の指定の仕方

リクエストバリデーションクラスを作ったことで、複雑なバリデーションを書いてもコントローラの幅をとることはありません。

バリデーションクラスでの複雑な処理の書き方を紹介していきます。

SampleRequest.php
namespace App\Http\Requests;

use Illuminate\Http\Request;
use Illuminate\Foundation\Http\FormRequest;

class SampleRequest extends FormRequest
{
     /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        // 初期の状態ではfalseを返すようになっているため、機能させたい場合はtrueに変更してください
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
          'name' => ['required', 'string'],
          'user_id' => ['required', 'integer'],
          'content_id' => ['sometimes', 'integer'] 
        ];
    }

   // 下記のような書き方で、それぞれのバリデーションに対するメッセージのカスタマイズが設定できます。
    public function message()
    {
        return [
            'name.required' => '名前を入力してください',
            'name.string' => '名前は文字列で入力してください',
            'user_id.required' => 'ユーザーIDを入力してください'
        ]
    }
}

   // 下記のように書くことでバリデーションに通す前のデータを加工することができます。
    protected function validationData()
    {
        $data = $this->all();

        // 文字を切り取るなどの前処理
        $data['name'] = substr($data['name'], 0, -1);

        return data;
    }

より複雑なルールはRulesクラスに切り出そう

もっと細かなオリジナルルールを適用したい場合はRulesクラスを作成するとすっきり書くことができます。

SampleRules.php
<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class SampleRule implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        // オリジナルルール
        // $valueとすることで引数で渡した値を参照することができます。
    }

Rulesは以下のようにリクエストクラスから呼び出します。

SampleRequest.php
 public function rules(Request $request)
    {
        $useId = $request->input('user_id');

        return [
          'name' => ['required', 'string'],
          'user_id' => ['required', 'integer', new SampleRule($userId)],
          'content_id' => ['sometimes', 'integer'] 
        ];
    }

rulesクラスに渡す値は$useIdのように、一度変数に持たせてから、渡しています。

とはいえlaravelには様々なバリデーションを指定できるようになっているため、オリジナルのRuleを使う場面はなかなかないのかなと思っています。
実装したいルールは公式ドキュメントを読めば意外と用意されていたりするかもしれません。

読んでいただきありがとうございました。

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

Eloquentのwithで指定カラムのみの取得と条件指定を同時にできなかったというお話

私が所属している会社の業務でデータの抽出処理を実装するということになったのですが、その抽出条件が多数のテーブルとそのカラムに跨っているという物でした。
その実装方法を色々試している中、「なぜこれができないんだ」と困った話について書いていきたいと思います。

LaravelのEloquentでは、Modelクラス内でリレーションを設定することで、withメソッドを用いて関連するモデルの情報を同時に取得するということができます。

App\SomeData.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class SomeTable extends Model
{
    public function otherData()
    {
        return $this->hasOne('App\OtherData', 'otherdata_id');// hasManyも同様の記述
    }
}
App\SomeTable::with(['otherData']);
// SomeTableのデータと共に、対応するOtherDataのデータも同時に取得できる

この時、OtherDataの内一部のカラムの情報のみが欲しいという場合、そのカラムを記述することで取得するデータを絞ることができます。

App\SomeTable::with(['otherData:otherdata_id,another_column']);
// 対応するOtherDataのデータの内、主キーとanother_columnのみが取得される

また、OtherDataの内特定条件を満たすもののみを取得するということもできます。

App\SomeTable::with([
    'otherData' => function($query) {
        $query->where('another_column', '<', 10);
    }
]);
// 対応するOtherDataのデータの内、another_columnが10未満のもののみが取得される

今回実装したい抽出処理では多数のテーブルに必要な情報が跨っているものの、その内実際に利用するカラムは僅かです。そのため、PHP側での処理が重くならないように次のようなコードを書いてみました。

App\SomeTable::with([
    'otherData:otherdata_id,another_column' => function($query) {
        $query->where('another_column', '<', 10)
    }
]);
// 期待した通りには動かない

改めて考えてみたところ、カラム指定を行わないwithで取得するオブジェクトはModelクラスを継承しているものになっていたのですが、カラム指定をすると単なるCollectionクラスのオブジェクトになっていました。なので、カラム指定をした場合Modelに対するwhereではなく、Collectionに対するwhereとして動作してしまい、期待した動作にはならなかったのではないかと考えています。

今回の抽出処理の実装については、ひとまず愚直にテーブルをjoinすることで実装することにしました。何かいい方法があるとなればコメント等宜しくお願い致します。

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

FormRequestで自作のファイル拡張子バリデーションを作ってみた

環境

Laravel 6.20.8
PHP7.4

はじめに

コントローラーにもバリデーションは書けますが、複雑なバリデーションになればなるほど、コントローラーが肥大化してしまうので、FormRequestクラスを活用します。
FormRequestに自作でバリデーションをどのように書くのか、画像ファイルの拡張子チェックを例にします。

Laravelのバリデーションルールは豊富なので、rulesメソッドにmimesを指定してしまえば、アップロードしようとしているファイルのMIMEタイプはチェックしてくれます。ですが、MIMEタイプも改ざんできてしまうので、同時に拡張子のチェックもしたいと思います。

準備

下記コマンドで、App\Http\Requests配下にFormRequestクラスを生成します。

php artisan make:request CreateItemRequest

すると、下記のようなファイルが作られます。authorize()は、認証関係の判定の処理を書きますが、今回は不要なのでtrueを返すようにします。バリデーションはrules()に書いていき、前述の通りmimesを指定します。(今回はjpgとpngのみにします)

app/Http/Requests/CreateItemRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateItemRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules() 
    {
        'name' => 'required|string|max:255',
        'picture_name' => 'required|file|mimes:jpg,png', 
    }

}

コントローラーの変更点は2点です。作成したFormRequestクラスをuseして、store()などPOST送信を受けるメソッドの引数に、作成したcreateItemクラスを渡すだけです。

ItemController.php
//中略

use App\Http\Requests\CreateItemRequest;

//中略

public function store(CreateItemRequest $request)
{
    //
}

自作で拡張子バリデーションを作成する

下記にバリデーションを書いていきます。withValidatorメソッドは、rules()で指定したバリデーション評価前に実行されるようです。

app/Http/Requests/CreateItemRequest.php
public function withValidator($validator)
{
    //ここにバリデーションを書いていきます
}

app/Http/Requests/CreateItemRequest.php
public function withValidator($validator)
{
    $validator->after(function ($validator)
    {
        $file_data = $this->file('picture_name');
        $file_extension = $file_data->getClientOriginalExtension(); //拡張子取得
        $lower_case_extension = strtolower($file_extension); //拡張子を小文字に変換

        if($lower_case_extension !== 'jpg' && $lower_case_extension !== 'png'){
            $validator->errors()->add('', 'アップロードされたファイルは画像ファイルではありません。');
        }
    });
}

拡張子は、大文字でも小文字でもチェックできるよう、一度拡張子を小文字に変換します。エラー時のメッセージは直接書きます。

最終的なファイル

app/Http/Requests/CreateItemRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateItemRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules() 
    {
        'name' => 'required|string|max:255',
        'picture_name' => 'required|file|mimes:jpg,png', 
    }

    public function withValidator($validator)
   {
        $validator->after(function ($validator)
        {
            $file_data = $this->file('picture_name');
            $file_extension = $file_data->getClientOriginalExtension(); //拡張子取得
            $lower_case_extension = strtolower($file_extension); //拡張子を小文字に変換

            if($lower_case_extension !== 'jpg' && $lower_case_extension !== 'png'){
                $validator->errors()->add('', 'アップロードされたファイルは画像ファイルではありません。');
            }
        });
    }

}

おわりに

もともとLaravelが用意してくれているバリデーションルールを使えば、かなりのバリデーションができると思いますが、独自のバリデーションが必要なこともあると思うので、withValidatorメソッドの使い方のメモを残しました。

参考

https://readouble.com/laravel/6.x/ja/validation.html
https://www.ritolab.com/entry/41

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

3分で実装できる Laravelでユーザ編集、退会機能(忙しい人向け)

前書き

useredit/laraveluiのレポジトリ
当ライブラリはMITライセンスです
laravel6系にて動作確認しております。
機能を追加したい場合はご自身でソースコード読んでください

laravel6 パッケージ開発ハンズオン(パッケージを作ったことない人向け)で同様の機能パッケージ開発ハンズオンあるので時間があれば是非

前提条件

laravel/uiをインストール
php artisan ui vue --authでUIを実装

インストール方法

プロジェクト直下で以下コマンドを実行

composer require useredit/laravelui

インストール完了後下記コマンドで各種controllerやRequestを実装

php artisan useredit

http://ドメイン名(localhost等)/userにアクセスし以下画面が出力できれば完成です

スクリーンショット 2021-01-08 10.39.24.png

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

Laravel6 独自のリクエストクラスを作成してpostしたらエラーが出た

目的

  • Laravel6のアプリでバリデーションをリクエストクラスに記載するために独自のリクエストクラスを作成してデータをpostしたらエラーが出たので回避方法をまとめておく

環境

  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)
プロセッサ 2 GHz クアッドコアIntel Core i5
メモリ 32 GB 3733 MHz LPDDR4
グラフィックス Intel Iris Plus Graphics 1536 MB
  • ソフトウェア環境
項目 情報 備考
PHP バージョン 7.4.11 Homebrewを用いてこちらの方法で導入→Mac HomebrewでPHPをインストールする
Laravel バージョン 6.X commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.21 for osx10.15 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

情報

  • 本エラーはAuthの認証機能を付与しているLaravelアプリだと発生するらしい。例にもれずエラーが出た筆者のアプリもAuthによる認証機能が付与されている。

経緯

  1. 下記コマンドを実行してリクエストクラスを作成した。

    $ php artisan make:request ContentRequest
    
  2. 作成されたリクエストクラスのファイルを下記のように記載した。

    アプリ名ディレクトリ/app/Http/Requests/ContentRequest.php
    <?php
    
    namespace App\Http\Requests;
    
    use Illuminate\Foundation\Http\FormRequest;
    
    class ContentRequest extends FormRequest
    {
        /**
         * Determine if the user is authorized to make this request.
         *
         * @return bool
         */
        public function authorize()
        {
            return false;
        }
    
        /**
         * Get the validation rules that apply to the request.
         *
         * @return array
         */
        public function rules()
        {
            return [
                'content' => 'required',
            ];
        }
    }
    
  3. データがpostされるコントローラのメソッド(アクション)の引数にContentRequest $contentRequestを追加してデータをpostした。

エラー

  • 「403 This action is unauthorized.」というエラーが発生した。

    Forbidden.png

解決策

  1. 作成したリクエストクラスのauthorize()メソッド内部を下記のように書き換える。

    アプリ名ディレクトリ/app/Http/Requests/ContentRequest.php
    <?php
    
    namespace App\Http\Requests;
    
    use Illuminate\Foundation\Http\FormRequest;
    
    class ContentRequest extends FormRequest
    {
        /**
         * Determine if the user is authorized to make this request.
         *
         * @return bool
         */
        public function authorize()
        {
            //下記を修正する
            return true;
        }
    
        /**
         * Get the validation rules that apply to the request.
         *
         * @return array
         */
        public function rules()
        {
            return [
                'content' => 'required',
            ];
        }
    }
    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む