Eagerロードとは。N+1問題を解決する

Eagerロードとは。に関して簡潔に回答している情報があまりないと感じています。

公式サイトには下記と記述されていますが。初見の人が意味を理解できるのか…少し怪しいと思います。

プロパティとしてEloquentリレーションへアクセスすると、関連するモデルは「遅延読み込み」されます。つまりこれは、最初にプロパティへアクセスするまで、リレーションデータが実際にロードされないことを意味します。ただし、Eloquentは、親モデルにクエリを実行するときに、関係を「Eager(積極的)ロード」できます。Eagerロードにより、「N+1」クエリの問題が軽減されます。N+1クエリの問題を説明するために、Authorモデルに「属する(belongs to)」Bookモデルについて考えてみます。

https://readouble.com/laravel/8.x/ja/eloquent-relationships.html#eager-loading

Eagerローディング とは。

簡潔に回答すると、下記かなと思います。

積極的なローディング。
Eloquentを利用したデータ取得に際して、関連する(リレーション先)データを一緒に取得してくる手法のこと。

Eagerローディングを利用することで、N+1問題の解消が可能です。

以前、孫リレーション先に対して、N+1問題を解消する方法を解説しているので、合わせてご確認頂けたらと思います。

Laravelで使うクロージャー/Closure/無名関数

クロージャー、またの名を無名関数と言います。
初学者の方には理解しづらい概念なのでは無いでしょうか。
私もクロージャーを利用する機会はありますが、正しく理解せずに利用していました。

様々な記事で紹介されていますが、いまいち分かりづらいですよね…. 

おそらく、理解を難しくしている1番の原因は「何となく分かるが、何のために存在しているのか分からない」ということに尽きると思います。

今回は、クロージャーが何のために存在するのか。また、Laravelだとどのように使われているのかを紹介していきます。 少しでも、クロージャーのイメージを掴んで頂けたらと思います。

Q1. クロージャー(無名関数)とは

名称の通り無名関数です。名もなき関数。
ただの処理の塊です。

Q2. クロージャー(無名関数)の書き方

function( 引数 ){ 
//処理
};

この部分がクロージャー(無名関数)です。名が存在しません。

通常のLaravel関数であれば下記のように、hoge、piyoなどの関数名が付きます。が、クロージャーは名もなき処理の塊です。

private function hoge(){
//処理
}
private function piyo(){
//処理
}

Q3. クロージャーの利用用途

色々な使われ方をします。
が、そこを全て把握しようとすると理解を難しくすると思います。

ここでは、Laravelでよく利用されている使われ方を利用することによって、敢えて限定的な利用用途を紹介します。

Q3-1. Laravelでよく使われるクロージャーの利用用途

コールバックの処理に使われるのが、クロージャー(無名関数)の代表的な利用用途だと思います。

コールバック処理での、クロージャー利用用途を理解するとクロージャーの存在価値がよく分かると思います。

Q3-2. Laravelでコールバックで使われるクロージャー利用例

実際に、Laravelのコールバック処理内で利用されるクロージャーの利用例を紹介します。

例:Route
Route::get('/', function () {
     return view('welcome'); 
});

必ず利用するRouteの処理もクロージャーを利用しています。
Route::get( 第一引数 , 第二引数 ); という処理です。

  • 第一引数:
    • ルーティングの文字列
  • 第二引数:
    • ルーティングに応じた処理(viewを返す OR Controllerのメソッドを呼び出す) -> クロージャーの処理で記述
Routeの深堀り

Route::get( ); の処理は下記で記述されています。

// ディレクトリ
app/vendor/laravel/framework/src/Illuminate/Routing/Router.php

//メソッド
    /**
     * Register a new GET route with the router.
     *
     * @param  string  $uri
     * @param  array|string|callable|null  $action
     * @return \Illuminate\Routing\Route
     */
    public function get($uri, $action = null)
    {
        return $this->addRoute(['GET', 'HEAD'], $uri, $action);
    }

addRoute()に $uri, $actionが渡されています。
$action → クロージャー(無名関数)で渡している処理となっています。

addRoute をもう少し深掘る
//addRoute
    public function addRoute($methods, $uri, $action)
    {
        return $this->routes->add($this->createRoute($methods, $uri, $action));
    }

//createRoute
protected function createRoute($methods, $uri, $action)
{

    if ($this->actionReferencesController($action)) {
        $action = $this->convertToControllerAction($action);
    }

    $route = $this->newRoute(
        $methods, $this->prefix($uri), $action
    );

    if ($this->hasGroupStack()) {
        $this->mergeGroupAttributesIntoRoute($route);
    }

    $this->addWhereClausesToRoute($route);

    return $route;
}

こちらのRouterの処理を全てを理解する必要はありません。

抑えておきたいのは、Route::get( 第一引数 , 第二引数 )で引き渡した、クロージャーが、$actionとして、最終的に$routeインスタンスに代入され、その後の処理に続いていくということです。

このように、クロージャーは処理自体をコールバック処理の中に含めて渡していくために用いられるイメージを持つと、存在価値がイメージ掴みやすいかと思います。

Q3-3. Laravelでコールバックで使われるクロージャー処理を言語化すると…

イメージしやすくするために、敢えて 強引に言語化すると

  • 処理が途中まで決まっている(コールバック)処理
  • 処理の一部分だけ、利用者(プログラム記述者)が勝手に決めて、処理を渡して!
  • その渡す処理を クロージャー(無名関数)で渡して!

みたいな感じです。

Q4. クロージャーのまとめ

クロージャーの利用用途は幾つもありますが、今回はコールバック処理の中で利用されるクロージャーのみを紹介しました。
コールバック内での利用され方は、下記でした。

処理が途中まで決まっている(コールバック)処理
処理の一部分だけ、利用者(プログラム記述者)が勝手に決めて、処理を渡して!
その渡す処理を クロージャー(無名関数)で渡して!

ざっくりと、まとめると。

定形の処理の一部を、可変にして自由に利用させたい。
その可変部分の処理を名もなきクロージャー(無名関数)で記述して下さい。

という感じです。

イメージしやすくするために省略した部分もありますが、イメージを掴めたでしょうか?

[Nuxt.js/Vuex] store/index.js”以外”のファイルへアクセスする方法

Nuxt.jsにて、Vuexを利用する際にstore/index.jsはデフォルトで設定されております。
基本的にはこちらのindex.jsにVuexの処理を記述していくことになります。が、index.js以外のファイルに分割して管理したいケースが発生すると思います。

今回は、Nuxt.js において store/index.js以外のファイルにアクセスする方法を解説します。

前提

今回はstoreの記述方法は解説しません。index.js以外に記述するとしても、index.jsへの記述方法と同様です。

todo.jsがstore配下に存在し、こちらにアクセスしていく方法を考えています。

store
 L index.js
 L todos.js

Q. stateの場合

this.$store.state.{ファイル名}.{state名}

例:

this.$store.state.todos.list

Q. mutationsの場合

this.$store.commit(“{ファイル名}/{mutations名}”)

例:

this.$store.commit('todos/add', e.target.value)

Q. actionsの場合

this.$store.dispatch(“{ファイル名}/{actions名}”)

例:

this.$store.dispatch('todos/addList')

Q. gettersの場合

下記、記事にまとめております。

【SQL】””特定テーブル””の全カラムデータ取得する

SQLで全てのカラムデータを取得する際に 「*」を利用されると思います。

SELECT
*
FROM
table

複数のテーブルをJOINしている際の、特定のテーブルだけの全てのカラムデータを取得したい場合はどうするでしょうか??

下記のように、「テーブル名.*」です。

SELECT
a.*
FROM
tableA a
LEFT JOIN
tableB b
ON a.id = b.a_id

これで、特定のテーブルの、全カラムデータを取得出来ます。

[Nuxt×Laravel] API通信のValidationをLaravelで処理する方法

Nuxt×Laravel でアプリケーションを作成する際に、バックエンド側のLaravelでValidationを設定する方法を紹介します。

幾つか設定方法がありますが、今回は下記の方法にてValidationを設定していきます。

  1. Laravel >> FormRequestを作成
  2. Laravel >> FormRequestのrulesを設定
  3. Laravel >> FormRequest内で、failedValidationをオーバーライド
  4. Laravel >> Postリクエスト内にFormRequestを適用
  5. Nuxt >> APIレスポンスにて、Validationメッセージを取得

0. 前提

NuxtからのAPIリクエストは axiosを利用しています。axiosに利用方法に関して詳細は省きます。

1. Laravel >> FormRequestを作成

コマンドで生成します。

php artisan make:request PostRequest // PostRequest部分は任意. FormRequestの命名

コマンドによって下記ファイルが生成されます。

server/app/app/Http/Requests/PostRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class PostRequest 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 [
        ];
    }
}

2. Laravel >> FormRequestのrulesを設定

Validationのrulesを設定していきます。

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

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

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'user_id' => 'required',
            'post_id' => 'required',
        ];
    }
}
'user_id'、'post_id'が必須というシンプルなrulesにしておきます。

3. Laravel >> FormRequest内で、failedValidationをオーバーライド

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

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

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

/**
* [override] バリデーション失敗時ハンドリング
*
* @param Validator $validator
* @throw HttpResponseException
* @see FormRequest::failedValidation()
*/
protected function failedValidation( Validator $validator )
{
$response['data'] = [];
$response['status'] = 'NG';
$response['summary'] = 'Failed validation.';
$response['errors'] = $validator->errors()->toArray();

throw new HttpResponseException(
response()->json( $response, 422 )
);
}
}

FormRequestファイル内に、failedValidationを追加しました。

これによって、下記のファイル > メソッドをオーバーライドします。

server/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php > failedValidation()

また、failedValidation()がいつ呼び出されるかというと。下記です。

server/app/vendor/laravel/framework/src/Illuminate/Validation/ValidatesWhenResolvedTrait.php > validateResolved()

4. Laravel >> Postリクエスト内にFormRequestを適用

FormRequestをContollerのメソッドに適用します。

    public function postAction(PostRequest $request) { // 適用
        //省略
        //user_id, post_id が受け取る
    }

apiのroute

server/app/routes/api.php
<?php

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


/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::post('postAction', [任意のController::class, 'postAction']);
    
});

5. Nuxt >> APIレスポンスにて、Validationメッセージを取得

const payload = {
  user_id: XXXX,
  post_id: YYYY,
};
const access_token = ZZZZ;
this.$axios.$post('/api/postAction', payload , { headers: { Authorization: `Bearer ${access_token}` } })
    .then(
      res => {
          console.log(res);
      }
    )
    .catch(error => {
      console.log(error.response.data);
      })

APIリクエスト/レスポンスの処理のみ記載しています。

レスポンスを受け取る際は、error.response で受け取ります。

Laravelでtry~catchとthrowを実験.Exception処理の動きを理解

Laravelでの例外処理について解説していきます。

Q. try ~ catch 処理とは

下記の記法です。

try {
    // 例外が発生する可能性のあるコード
} catch (Exception $e) {
    // 例外が発生した場合に行う処理
}

例外が発生する可能性がある場合に、
例外が発生すると処理が中断したり・停止することを防ぐために
try{ } で判断して → catch{ } で対処します。

Q. try ~ catch 例外処理を実施してみる

public function testExcetion() {
    try {
        // 例外が発生する可能性のあるコード
        $error = 5/0; // errorが発生する処理を実行
throwする必要有り。
    } catch (Exception $e) {
        // 例外が発生した場合に行う処理
    }
}

この処理を実施した場合、何も発生しません。
本来であれば、$error = 5/0; が実施されると Division by zero error のエラーが発生します。しかし、上記のtry ~ catch 処理の場合は何も表示されません。

それは、tryで例外が発生したものをキャッチして、catch内で処理させています。catch内では、受け取った例外に対して何も扱っていないので、変化無しです。

Q. try ~ catch 例外処理を受け止め

public function testExcetion() {
    try {
        // 例外が発生する可能性のあるコード
        $error = 5/0;
throwする必要有り。
    } catch (Exception $e) {
        // 例外が発生した場合に行う処理
        echo $e->getMessage(); // error messageを取得 (echo で出力)
    }
}

Division by zero error が表示されます。

Q. try は何をエラーと判断しているのか

public function testExcetion() {
    try {
        // 例外が発生する可能性のあるコード
        throw new Exception("error発生です!!!!");
throwする必要有り。
    } catch (Exception $e) {
        // 例外が発生した場合に行う処理
        echo $e->getMessage(); // error messageを取得 (echoで出力)
    }
}

error発生です!!!! が出力されます。

try{}内では、Exception を throwされることをきっかけに検知して、catchへ処理を移します。 (Exceptionは様々なClassあり)

[ Vue ]データバインディング・双方向データバインディングとは

Vueを形成している超重要な概念であるデータバインディング・双方向データバインディングについて理解されているでしょうか。理解せずとも、感覚的に使っていらっしゃる方もいるかと思います。

データバインディングは様々な記事でも紹介されいています。
今回は、データバインディングのデータの流れをVue.js Devtoolsを用いながら確認していきます。

Q.データバインディングとは

データ操作 → データ描画を同期的に処理してくれる機能のことです。

データバインディングでは無い状態とは。例えば、jQueryでのDOM操作は基本的にはデータバインディングとは言えません。

 var word = "Hello"
 var wordEl = $("#word")
 var buttonEl = $("button")
 wordEl.text(word)
 buttonEl.on("click", function(){
   word = "クリックされた"
   wordEl.text(word) // ①
 })

①:wordEl.text(word) // ここで、反映処理実行することによって描画を実行しています。

この様にデータ変更と描画処理が同期していない場合はデータバインディングとは言いません。

Q.Vueのデータバインディングする処理を実行

では、Vue.jsにてデータバインディングを実施していきます。

v-bindを利用します。

<template>
  <div>
    <div>
      <p>Label:</p>
      {{ bind1 }}
      <p>Input:</p>
      <p>
        <input type="text" v-bind:value="bind1">
      </p>
    </div>
  </div>
</template>

<script>

export default {
  data () {
    return {
      bind1: "データバインディング",
    }
  },
}
</script>

変数:bind1のデータが → 描画されいているのが分かるかと思います。

Q.同期処理されている?

データバインディングが同期処理されていることを体感していきます。

Devtoolsからbind1 の変数を操作してみます。

するとbind1の変更に応じて同期的に描画されました。

Q.双方向データバインディングとは.

データバインディングとは、
・データ操作 → データ描画を同期的処理してくれる機能

対して、
双方向データバインディングとは、
・データ操作 → データ描画を同期的処理してくれる機能

・データ描画 → データ操作を同期的処理してくれる機能

というデータ描画とデータ操作が両方向で同期処理を実現してくれる機能です。

Q.Vueの双方向データバインディングする処理を実行

v-modelにて、双方向 データバインディングを確認していきます。

<template>
  <div>
    <div>
      <p>Label:</p>
      {{ bind1 }}
      <p>Input:</p>
      <p>
        <input type="text" v-bind:value="bind1">
      </p>
      <p>Label:</p>
      {{ bind2 }}
      <p>Input:</p>
      <p>
      <input type="text" v-model="bind2">
      </p>
    </div>
  </div>
</template>

<script>

export default {
  data () {
    return {
      bind1: "データバインディング",
      bind2: "双方向data-bind",
    }
  },
}
</script>

ここまででは、データバインディングとの違いは分からないかと思います。

input:データ内を変更していきます。
bind1のinput、bind2のinput両方変更することによってデータバインディング、双方向データバインディングの違いが鮮明になります。

bind1
inputを変更しましたが、data -> bind1は変更されていません。よって、Labelの値も「データバインディング」のままです。

対して

bind2
inputの変更をしたとろこ、data -> bind2 も同期的に変更されています。 よって、Labelの値も同期的に変更されています。

このように、Vue の dataをViewから同期的に変更できるのが双方向データバインディングの特徴です。

※リロードすると、data()で設定している値に戻ります。

Nuxtのcontextとは. contextへアクセスを実験

Nuxtのcontext(コンテキスト)について解説 / 実験していきます。

細かく・正確な解説はNuxtの公式サイトを確認頂けたらと思います。
が、
実際にcontextを呼び出して中身を確認しつつ、アクセス方法を実験していきたいと思います。

Q. Nuxtのcontext(コンテキスト)とは

Vueオブジェクトのプロパティ、メソッドへアクセス、操作可能な変数です。
特定のライフサイクル内でのみ利用可能です。
(極端に解釈すると、特定の場所から呼び出せるグローバルなNuxt(Vue)情報を含んだ変数です。)

context内には様々な情報を含んでいます。 様々なキー

注意 「Nuxtのcontext」と「Vuexのcontext」は別物なので混合しないようにしてください。

Q. Nuxtのcontextを呼び出す場所

context は、Nuxt から Vue コンポーネントに追加のオブジェクト/パラメータを提供し、asyncData 、fetch 、plugins 、middleware そして nuxtServerInit のような特別な Nuxt ライフサイクル内で使用できます。

https://develop365.gitlab.io/nuxtjs-2.8.X-doc/ja/api/context/

上記あるように「asyncData 、fetch 、plugins 、middleware 、nuxtServerInit」からcontextを呼び出すことが出来る。

Q. Nuxtのcontextを呼び出す方法

では、実際にcontextを呼び出してみます。

上記のライフサイクルのみで呼び出し可能です。

A, fetchから呼び出す。Component内(ページ、レイアウトカラム内でも)
<script>
export default {
  // 省略
  fetch(context) {
    console.log(context);
  },
  // 省略
}
</script>

下記のような結果が取得出来ました。
インストールしているライブラリなどによってもcontextで取得できる内容は変わります。

$axiosƒ wrap()
$config: {_app: {…}}
$vuetify: {…}
app: {head: {…}, store: Store, router: VueRouter, nuxt: {…}, render: ƒ, …}
base: "/"
env: {}
errorƒ ()
from: {name: 'index', meta: Array(1), path: '/', hash: '', query: {…}, …}
isDev: true
isHMR: false
isStatic: false
nextƒ ()
nuxtState: {config: {…}}
params: {}
payload: undefined
query: {}
redirectƒ (status, path, query)
route: {name: 'index', meta: Array(1), path: '/', hash: '', query: {…}, …}
store: Store {_committing: false, _actions: {…}, _actionSubscribers: Array(1), _mutations: {…}, _wrappedGetters: {…}, …}
_errored: false
_redirected: false
A, middlewearから呼び出す。
app/middleware/test.js
export default function(context){
  console.log(context);
}
app/layouts/default.vue

layoutのmiddlewearにtest.jsを適用

<script>
  export default {
//省略
  middleware: ['test'],
//省略
</script>

同じ結果が取得出来ました。

Q. 指定のライフサイクル以外で取得は不可?

出来ません。mountedから呼び出してみます。

<script>
export default {
  // 省略
  mounted(context) {
    console.log(context);
  },
  // 省略
}
</script>

結果は undefined が返ってきます。

Q. contextデータ大きすぎて、使いづらい?

分割代入を利用することで、必要なデータのみを取得することが可能です。

例えば、context内に存在する、appオブジェクトのみを取得したい場合には、下記のように分割代入で取得

A, fetchから呼び出す。Component内(ページ、レイアウトカラム内でも)
<script>
export default {
  // 省略
  fetch({app}) {
    console.log(app);
  },
  // 省略
}
</script>

下記のようなappの中身のみ取得

{head: {…}, store: Store, router: VueRouter, nuxt: {…}, render: ƒ, …}
$axiosƒ wrap()
$config: {_app: {…}}
beforeCreateƒ beforeCreate()
components: {NuxtLoading: {…}}
computed: {isOffline: ƒ, isFetching: ƒ}
context: {isStatic: false, isDev: true, isHMR: false, app: {…}, store: Store, …}
createdƒ created()
dataƒ data()
head: {title: 'app', htmlAttrs: {…}, meta: Array(4), link: Array(3), style: Array(0), …}
methods: {refreshOnlineStatus: ƒ, refresh: ƒ, errorChanged: ƒ, setLayout: ƒ, loadLayout: ƒ}
mountedƒ mounted()
nuxt: {…}
renderƒ render(h, props)
router: VueRouter {app: Vue, apps: Array(1), options: {…}, beforeHooks: Array(2), resolveHooks: Array(0), …}
store: Store {_committing: false, _actions: {…}, _actionSubscribers: Array(1), _mutations: {…}, _wrappedGetters: {…}, …}
vuetify: Vuetify {framework: {…}, installed: Array(7), preset: {…}, userPreset: {…}}
watch: {nuxt.err: 'errorChanged'}
[[Prototype]]: Object

function({ }) 引数の中に中括弧を含める分割代入とは. /JavaScript

分割代入(Destructuring assignment) といいます。

目的は、オブジェクトの変数を必要なものだけ宣言して取得します。大きなオブジェクトの中に様々な変数が存在します。すべて取得すると煩雑ですし、メモリ利用も多くなります。必要なものだけを取得します。

分割代入利用方法

分割代入を宣言した関数

function foo({ a, b }) {
  console.log(a, b);
}

分割代入を宣言した関数を利用結果

foo({ a: 1, b: 2, c: 3 }); // 1 2

関数に変数がa,b,cと渡ってきたが、元々利用することを宣言していたa,bのみを取得している。

Sequel Aceでcopy with column nameが操作出来ない問題

SQLクライアントサービスは何を使っていますでしょうか。
Macであれば、Sequel Ace が非常に使い勝手が良いかと思います。

SQL書き出しした後に、データと一緒にカラム名も一緒にコピーしたいことがあるかと思います。 デフォルト機能でカラムと一緒にコピーが可能です。

しかし、グレースケールしており、選択出来ない状態となっていました。

そこで、解決方法としては、ショートカットを利用するとコピー出来ましたので紹介します。

下記にてCopy selected row(s) with column namesの機能が使えます。

⌥ ⌘ C

SequelAceショートカットはこちらに様々ありましたので、ご確認頂けたらと思います。