Docker環境構築 / Vue.jsをさくっと起動させるまで.

ディレクトリ構成

/Vue-Test
 L Dockerfile
 L docker-compose.yml

①:Dockerfile/docker-compose ファイルを準備

①-1:Dockerfile
FROM node:17-alpine3.14

WORKDIR /work

RUN apk update && \
    npm install -g npm && \
    npm install -g @vue/cli

EXPOSE 8080
ENV HOST 0.0.0.0
①-2:docker-compose.yml
version: '3'
services:
  vue:
    container_name: vue
    build: 
      context: .    
      dockerfile: ./Dockerfile
    ports:
      - "8080:8080"
    volumes:
      - ./server:/work:delegated      
    tty: true

②:docker コンテナの起動

②-1:ディレクトリに移動

cd Vue-Test

②-2:コンテナ起動

docker-compose up -d

③:コンテナに入り、Vueプロジェクトを作成

③-1:コンテナへ移動

docker-compose vue exec vue sh

③-2:Vueプロジェクトを作成

first-app 名称でプロジェクトを作成

vue create first-app

③-3:Vueのバージョンを選択

Default ([Vue 3] babel, eslint) 
❯ Default ([Vue 2] babel, eslint) 

③-4:package manageを選択

Use Yarn 
❯ Use NPM

これでインストールが開始します。

④:プロジェクトの起動

④-1:プロジェクトへ移動

cd first-app

④-2:serve

npm run serve

⑤:ブラウザで確認

下記へアクセスして、確認して下さい。

http://localhost:8080/

パラメータの使わけ出来ていますか? ルート/クエリ/リクエスト /Laravel

パラメータの使い分け出来ていますでしょうか。
私は恥ずかしながら、感覚で使っている部分が多かったです。 Getの場合はとりあえず、ルートパラメータかな?? POSTの追加処理にもクエリパラメータを利用したり….

それぞれの違い・役割と、Requestの処理の中身を見ていきたいと思います。

①:ルートパラメータ

文字通り、URLに含めるパラメータです。
ccccのようにパラメータを設定して、ルートの出し訳をします。

domain.com/user/{cccc}/show

②:クエリパラメータ

Queryにパラメータを追加したパラメータです。

domain.com/user/show?post_id=50

これは、ControllerからGET/POSTする際に設定する機会が多いかと思います。

<form method="POST" action="{{ route('users.post', ['user_id' => $user_id]) }}">
        <input type="text" name="comment">
</form>

[‘user_id’ => $user_id] こちらが、クエリパラメータとして送信されます。
<input type=”text” name=”comment”> こちらは、リクエストパラメータとして送信されます。

②-1:Requestの中身からクエリパラメータを確認

dd($request)で確認頂くと。

 +query: Symfony\Component\HttpFoundation\InputBag {#1297 ▼
    #parameters: array:4 [
      "_token" => "XXXXXXXXXXXXXX"
      "user_id" => "1"
    ]
  }

  +request: Symfony\Component\HttpFoundation\InputBag {#1298 ▼
    #parameters: array:4 [
      "_token" => "XXXXXXXXXXXXXX"
      "comment" => "こんにちは"
    ]
  }

query >> parameters・request >> parameters にそれぞれ含まれます。

③:リクエストパラメータ

リクエストのBodyタグの中身に含めるパラメータです。

<form method="POST" action="{{ route('users.post') }}">
        <input type="text" name="comment">
        <input type="hidden" name="user_id" value="{{$user->id}}">
</form>

下記がリクエストパラメータとして送信されます。

    <input type="text" name="comment">
    <input type="hidden" name="user_id" value="{{$user->id}}">

③-1:Requestの中身からクエリパラメータを確認

dd($request)で確認頂くと。

  +request: Symfony\Component\HttpFoundation\InputBag {#1298 ▼
    #parameters: array:4 [
      "_token" => "XXXXXXXXXXXXXX"
      "user_id" => "1"
      "comment" => "こんにちは"
    ]
  }

+request >> parametersに含まれています。

私よりも何倍も詳細に、解説されてる記事を見つけたのでご参考にしてください。

[RESTful API]パスパラメータ、クエリパラメータ、リクエストボディの違いと設計

リレーション先に制約を設けた状態でCount / Laravel withCountにクエリ制約

リレーション先に制約を設けた状態でCountする方法を解説していきます。

withCountを使っていきますが、withによるリレーション先のデータに制約を設けたデータをcountしていきます。

School Modelを準備します。 Student ModelとhasManyでリレーションしていることとします。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class School extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
    ];

    public function student()
    {
        return $this->hasMany(Studnet::class, 'id', 'school_id');
    }

}

任意のControllerにて、制約を設けたwithCountをします。
リレーション先( student )の sexカラム = woman であるデータを限定して、countしてくれます。

$school_with_student_count = 
School::withCount(['student' => function ($query) {
    $query->where('sex', 'woman');
}])
->get();

これで、School modelに
student_count というカラム、countデータが追加されています。

checkboxのPOST処理/validation / Laravel

LaravelでcheckboxのPOST処理と、そのValidation方法を解説していきます。

①:checkboxのPOST処理

name、valueをcheckboxに応じた書き方で処理していきます。

<section class="">
    @foreach($tags as $tag)
        <label class="inline-flex items-center mt-3">
            <input type="checkbox" name="tag_id[]" value="{{$tag->id}}" class="form-checkbox"><span>{{$タグ名称}}</span>
        </label>
    @endforeach
</section>

checkboxで使われる処理、name=”tag_id[]”

tagを3つ選択して、POSTした場合下記のようなデータがrequestに含まれます。

value=”{{$tag->id}}”で指定したtag_idが配列に含まれます。

    #parameters: array:2 [▼
    "_token" => "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    "tag_id" => array:3 [▼
      0 => "14"
      1 => "15"
      2 => "16"
    ]

②:配列で受けたPOSTに対してValidation

適用したいルール
・データが空ではない (tag_id がデータ有り)
・tag_idが数字である (value=”{{$tag->id}}”)

下記のようなvalidationメソッドの記述をします。

        $validated = $request->validate([
            'tag_id' => 'required',
            'tag_id.*' => 'required|integer',            
        ]);

ポイントはこちらです。

'tag_id.*' => 'required|integer',            

“tag_id” => array:3 [▼ に含まれる 配列の中身に対してvalidationを適用しています。

Laravel Modelを””複数条件””で検索を色々/whereIn/orWhere/orWhereHas/function($query)

Modelを複数条件で抽出際の方法解説します。
複数条件といっても様々な複数条件が存在するかと思います。
使用用途別に解説していけたらと思います。

①:複数のカラムで、’どれか’のカラムの検索条件に合致する場合

orWhereを利用します。

sex, age, 複数カラムで検索をしています。

Human::
    where('sex', 'men')
    ->orWhere('age', '>=', 40)
    ->get();

②:特定のカラムで、複数の検索条件の’どれか’に該当

whereInを利用します。

Human::
    whereIn('age', [30,31,32,33,40,41,42,43])
    ->get();

ageカラムの中で、該当するものを抽出します。

③:複数のカラムで、複数の検索条件に該当する場合

whereInをorWhereで接続して利用します。 

$nationality = ['japan', 'china', 'korean'];

Human::
    whereIn('age', [30,31,32,33,40,41,42,43])
    ->orWhere(function($query) use($nationality) {
            $query->whereIn('nationality',$nationality);
        })
    ->get();

④:複数テーブルで、’どれか’の検索条件に該当

下記記事にて記述しています。

複数テーブルと書いていますが、厳密には1つのModelからリレーション先のデータを他のテーブルデータとして捉えています。

⑤:複数テーブルで、複数カラムの検索条件に該当

EducationalBackground
 の国が$countries含まれる OR 専攻が$magersに含まれる
OR
Job
 の専門性が$specialtiesに含まれる

Human::
    whereHas('EducationalBackground', function ($query) use($countries, $magers) {

        return $query->whereIn('country',$countries);
                    ->orWhere(function($query) use($magers) {
                        $query->whereIn('mager',$magers);
                    });
    })
    ->orWhereHas('Job', function ($query) use($specialties) {
        return $query->whereIn('specialty',$specialties);
    })
    ->get();

AjaxのPOST処理に認可処理を追加/Policy/Laravel

LaravelでAjax POST処理のPolicyを作っていきます。今回は、Ajaxの詳細には触れていきません。

Userが、AjaxでCommentの編集をすることを想定した流れとなっています。

①:Policy作成の流れ

①-1:Policyの作成

$ php artisan make:policy CommentPolicy

App\Policiesの配下にCommentPolicy.php が作成される。

①-2:Policyを登録

App\Providers\AuthServiceProvider

protected $policies = []に 作成したPolicyを登録

    protected $policies = [
        Comment::class => CommentPolicy::class,
    ];

①-3:Policyのメソッド記述 (True OR Falseを返す)

実際のPolicyの処理を書いていきます。
①-1:Policyの作成 で作成した CommentPolicy.php に処理を記述していきます。

App\Policies\CommentPolicy.php
<?php

namespace App\Policies;

use App\Models\User;
use App\Models\Comment;
use Illuminate\Auth\Access\HandlesAuthorization;

class CommentPolicy
{
    use HandlesAuthorization;

    /**
     * Create a new policy instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    public function update(User $user, Comment $comment)
    {
        return $user->id === $comment->user_id;
    }
}

メソッドの内容としては、非常にシンプルかと思います。

Userのid と commentのuser_idが
・同じ場合はTrueを返し、
・違う場合はFalseを返します。

①-4:Policyの呼び出し

任意のコントローラーにて、①-3:Policyのメソッド記述 で作成したupdateメソッドを呼び出します。
詳細は 「②:AjaxでPolicyを呼び出し/認可」 以降にて記述していきます。

②:AjaxでPolicyを呼び出し/認可

commentの編集をするメソッドに、認可の処理を追加していきます。

CommentController の edit メソッド で実行していきます。

前提として
・ $requestにcomment_idを含めている
・ $requestにinput_textでコメントが含まれている
・ JSに $commentで返す

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

use App\Models\Comment;
use App\Models\User;

class CommentController extends Controller
{

    public function edit(Request $request)
    {

        // comment_idから編集予定のComment modelを取得
        $comment = Comment::where('id', $request->comment_id)->get()->first();

        // 編集処理をするUserの取得    
        $user = Auth::user();

        // 編集User と 編集予定のCommentを作成したUserが 同一なのかを認可
        if($user->can('update',$comment)){

            $comment->comment = $request->input_text;
            $comment->save();

            // JSに$commentを返す。  
            return $comment;
        } else {
            abort(403);
        }
    }

}

③:Ajaxの処理で躓いたところ

正直、上記の処理は、Ajax関係なくない??と思うかと思います。
確かに、上記の処理であれば、Ajax関係ない処理として通用します。

私は、authorize() メソッドを利用した際に躓きました。
authorizeメソッドでは、 Userのインスタンスを渡す必要が無く、自動的に読み込んでくれています。

ここが、AjaxだとUserインスタンスが自動で読み込まれません。

孫リレーション先のカラムを合計する方法/Laravel/N+1問題

孫リレーション先のカラムを合計する方法を解説してきます。

今回は詳しく解説していきませんが、この処理をすることでN+1問題を解決できる機会も多くあるかと思います。

子リレーションの場合は、withSum(‘{{子リレーション}}’, ‘{{対象カラム}}’)を利用して合計していけると思います。

同様に、withSum(‘{{子リレーション.孫リレーション}}’, ‘{{孫の対象カラム}}’)でいけるかな?と思っていたらNGでした。

■解決方法

①:親→孫リレーションを作成
②:親→孫リレーションにて、withSumで集計

という2ステップで実施します。

親子孫のモーチーフとしては、
学校毎のテスト点数の合計を集計する方法を考えていきます。
School → Student → Score とつながっていきます。

①:親→孫リレーションを作成

まずは、親から孫を繋ぐためのリレーションを作成します。

Modelを親、子、孫 2つ用意します。

子:Student Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Student extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
        'school_id',
    ];

    public function score()
    {        
        return $this->hasMany(Score::class, 'student_id', 'id');
    }

}

孫:Score Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Score extends Model
{
    use HasFactory;

    protected $fillable = [
        'score',
        'student_id',
    ];

    public function student()
    {    
        return $this->belongsTo(Student::class, 'id', 'student_id');
    }
}

親:School Model

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class School extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
    ];

    // このリレーションが今回の本題です。 親→孫へのリレーションを作成
    public function studentscore()
    {
        return $this->hasManyThrough(
            Score::class, // 孫 Model
            Student::class, // 子 Model
            'school_id', // 子テーブルの親ID
            'student_id', // 孫モデルの子ID
            'id',    // 親テーブルのローカルキー
            'id'    // 子テーブルのローカルキー
        )
    }

}

ポイントは、School Model の hasManyThroughを利用したリレーションメソッドです。
(親→子、子→孫へのリレーションもお忘れなく作成しておいてください。)

上記にて、親→孫リレーションを作成が出来ました。

②:親→孫リレーションにて、withSumで集計

②-1:孫からデータを合計

学校毎の学生の点数を合計する処理を書いていきます。
任意のControllerにて、

$school_score = School::
    withSum('student_score', 'score')
    ->get();

こちらで、学校毎に学生の点数合計が取得出来ます。

②-2:合計データはどこに?

合計データはどこに含まれるのでしょうか。

A. 親データに新しいカラムが追加されます。 カラム名は、

{{親孫のリレーション名}}_sum_{{合計したカラム名}}

$school_scoreの中身に、下記のカラムが追加さています。

studentscore_sum_score

ddでご確認を!

Ajaxの通信でパラメーターデータの送受信する方法 | Laravel / jQuery

jQueryを利用したAjaxの通信でデータの送受信方法を解説していきます。

Laravelにて処理しています。
今回は、LaravelでjQueryを利用する方法などは省略しております。パラメーターデータを送受信する方法に限定しております。また、LaravelでのAjax×jQury処理の全貌をまとめたいと思います。

方法としては1パターンあります。

①:URLに含める
②:Requestに含める

今回は、②:Requestに含める で進めたいと思います。

データ送信 / 受信

$.ajaxのパラメータのdataを利用していきます。

①:Ajaxの処理

今回のポイントはdataパラメーターです。

①-1://–①

・idがedit_commentを含む要素をクリックされた際にイベント発火します。

①-2://–②

・HTMLでdata-id=”XXXX” のXXXXの情報を取得します。 ※後述

①-3://–③

comment_id という名目で、 comment_id //–② のデータを送信します。 $request->comment_id でデータ取得します。  ※後述

$( function ()
{
    $(document).on('click', '[id^=edit_comment]', function(){ //--①

        var comment_id =  $(this).data('id'); //--②
        
        $.ajax({
            type: "get",
            url: "/comment/get",
            data : {
                comment_id:comment_id, //--③
            },
            cache: false,
            dataType: 'json',
        })
        .done((res)=>{
                 console.log( res ); //--成功した時
          })
        .fail((error)=>{
                 console.log( error ); //--失敗した時
          });
    });
})

②:Laravelのルーティング処理

①-1でクリックした際に発火したAjaxイベントに対してルーティング処理してくれる部分です。

url: “/comment/get”, なので、Route::get(‘/comment/get’ でgetで処理します。

web.php
Route::get('/comment/get', [CommentController::class, 'commentget'])->name('comment.get');

③:Contoroller / Ajaxからデータ受信

ルーティング処理で呼び出された、CommenntControllerのcommentgetで処理を書いていきます。

CommentController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Comment;

class CommentController extends Controller
{
    public function commentget(Request $request)
    {
        $comment_id = $request->comment_id; // --①
        $comment = Comment::where('id', $comment_id)->get()->get();
        return $comment; // --②
    }
}

③-1://–①

①-1://–① で設定したcomment_idのデータを受け取ります。
・$request->comment_id

③-2://–②

retuen で $coomentを Ajaxに返します。

問題なく処理出来ていれば、下記の部分でconsoleに$commentが出力されます。

        .done((res)=>{
               console.log( res ); //--成功した時
})

FormRequestで複数カラムでユニーク制約する/Laravel/Rule

標題の通りです。 FormRequestを利用して、ruleを作る際に複数カラムでユニークにする設定を解説していきます。

例えば、moviesテーブルで、movie_id の中で、actor_idをuniqueにする場合です。(ただし、別のmovie_idには、actor_idが存在していもOK)

■前提

・FormRequestとして、MovieRequest を 作成

・movies テーブルのカラムは下記3つ

'publish_date' 
'movie_id'
'actor_id'

・カラム制約
 → 全て requierd
 → movie_id と actor_id の両カラム合わせて uniqe

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class MovieRequest extends FormRequest
{

    public function authorize()
    {
        return true;
    }

    public function rules()
    {

        return [
            'publish_date' => 'required',
            'movie_id' => 'required',
            'actor_id' => 
                ['required',Rule::unique('movies', 'actor_id')->where('movie_id', $this->input('movie_id'))],            
        ];
    }

}