20210118のlaravelに関する記事は14件です。

Execute some action before saving on Laravel-Admin

This is note for me.

$form->saving(function (Form $form) {

    $before  = $form->model()->note; // value before change
    $after   = $form->note; // value after edit

    $note = "This is an article on Qiita!";
    $form->note = $note;

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

LaravelのキューでSQSを使う設定

設定方法

.envファイルの以下を環境変数を指定する。

.env
QUEUE_DRIVER=sqs

AWS_ACCESS_KEY_ID=AAAAAAAAAAAAAAAAAAAA
AWS_SECRET_ACCESS_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
AWS_SQS_PREFIX=https://sqs.ap-northeast-1.amazonaws.com/000000000000
AWS_SQS_QUEUE=QueueName
AWS_SQS_REGION=ap-northeast-1

AWS_SQS_PREFIXは作成したキューのPrefixを指定。
AWS_SQS_QUEUEは作成したキューの名前を指定。
AWS_SQS_REGIONは自分のリージョン名を指定。

AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYについてはTOPの右上のセキュリティ認証情報からアクセスキー新しいアクセスキーの作成から作成する。


これらの環境変数はこのファイルで使用されています。
一時的にsyncからSQSに変更したい場合はこちらを直接変更しても良いかと思います。

config/queue.php
'sqs' => [
    'driver' => 'sqs',
    'key' => getenv('AWS_ACCESS_KEY_ID'),
    'secret' => getenv('AWS_SECRET_ACCESS_KEY'),
    'prefix' => env('AWS_SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
    'queue' => env('AWS_SQS_QUEUE', 'your-queue-name'),
    'region' => env('AWS_SQS_REGION', 'us-east-1'),
],

その他

syncと違ってSQSの場合は以下のコマンドでワーカーを動かす必要があります。

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

【Laravel】 Sass(Scss)の使い方実例。Laravel Mixを使ってコンパイルする。

Laravel Mixというツールを使うとSassをCSSにコンパイルすることができる。

出力先のディレクトリ設定も超簡単。

目次

  1. Laravel Mixの機能
  2. Node.jsとnpmのインストール
  3. Laravel Mixのインストール
  4. コンパイル先の指定
  5. ファイルの作成
  6. コンパイルの実行
  7. ブラウザに表示
  8. 変更反映の自動化
  9. 本番用のコンパイル
  10. URLの自動変換機能(オフにする方法も)
  11. 複数ファイルをワイルドカードで指定する


Laravel Mixの機能

SassをCSSにコンパイルするだけでなく、Less, Stylus, PostCSSにも対応している。複数のcssファイルを1つのcssファイルに統合することもできる。

また、CSS以外にもJSの圧縮、.vue(ビューJS)、.jsx(リアクトJS)などのコンパイルにも対応している。

今回は、Sass -> CSS機能のみにフォーカス。


コンパイル手順

1. Node.jsとnpmのインストール

node -v 
#v12.18.3

npm -v
#6.14.8

バージョンが表示されればインストール済み。
ない場合は、node.js公式ページからダウンロード。


2. Laravel Mixのインストール

Laravelのプロジェクトを作成した時点でルートディレクトリ直下にpackage.jsonが存在する。

ここにはnodeの依存パッケージが記述されており、以下コマンドで記載のパッケージをインストールする。

$npm install



package-lock.jsonが作成されたらインストール完了。このファイルはインストール済みのnpmパッケージが記載されている。(自動生成されるファイルなので編集しない!)

image.png
laravel mixもインストールされている。

$npm install [パッケージ名]で指定したパッケージをインストールする。
パッケージ名を指定しない場合は、package.jsonに従ってインストールを行う。


3. コンパイル先の指定

デフォルトでルートディレクトリにwebpack.mix.jsがある。この中にコンパイルする形式や出力先の情報を記述する。

mix.メソッド名('コンパイルするファイルパス', '出力先');

webpack.mix.js(デフォルト)
const mix = require('laravel-mix');

mix.js('resources/js/app.js', 'public/js')
    .postCss('resources/css/app.css', 'public/css', [
        //
    ]);

↓ 以下に変更

webpack.mix.js
mix.js('resources/js/app.js', 'public/js')
    .sass('resources/sass/app.scss', 'public/css');

resources > sass の app.scssファイルをコンパイルして、public > css 配下に出力する。



▼(補足)別の指定方法
メソッドチェーンでつなげず、別々に記述しても機能する。

php
mix.js('resources/js/app.js', 'public/js');
mix.sass('resources/sass/app.scss', 'public/css');


4. ファイルの作成

実際にコンパイルするファイルを作成する。

app.scssファイルの作成

resources > sass > app.scss

app.scss
h1 {
  color: skyblue;
}

テストなのでシンプルに、、

image.png

ビュー

resorces > views > for-scss.blade.php

for-scss.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="{{ asset('css/app.css') }}">
</head>
<body>
  <div>
    <h1>SCSSのテスト</h1>
  </div>
</body>
</html>

▼cssファイルの読み込み
コンパイルされたcssファイルを読み込む記述を記載する。

<link rel="stylesheet" href="{{ asset('css/app.css') }}">

URLの呼び出しはassetヘルパー関数を使用。

(参考)assetヘルパー関数とは?


ルーティング

routes > web.php

web.php
Route::get('scss', function () {
    return view('for-scss');
});


5. コンパイルの実行

以下コマンドでコンパイルが開始する。

npm run dev



▼実行例

$ npm run dev

99% done plugins BuildOutputPlugin

   Laravel Mix v6.0.10   

✔ Compiled Successfully in 1143ms
┌───────────────────────────────────────────────────────┬──────────┐
│                                                  File │ Size     │
├───────────────────────────────────────────────────────┼──────────┤
│                                            /js/app.js │ 596 KiB  │
│                                           css/app.css │ 25 bytes │
└───────────────────────────────────────────────────────┴──────────┘

webpack.mix.jsの指示通り、jsファイルとcssファイルが作成される。
image.png

コンパイル結果の確認

生成されたapp.cssを確認する。今回はapp.scssと同じ内容が記述されていればOK。

app.css
h1 {
  color: skyblue;
}


6. ブラウザに表示

サーバーを起動する。

php artisan serve



URLを入力。http://127.0.0.1:8000/scss

image.png

狙い通りコンパイル後のCSSが反映されている。


7. 変更反映の自動化

$ npm run watchを実行すると、指定したコンパイル元のファイルに変更があった場合に、自動でコンパイルを実行する。

$ npm run watch

> @ watch /Users/s01386/projects/laravel/test-pj
> mix watch



ctrl + cでサーバーを停止すれば自動監視(mix watch)も停止する。


8. 本番用のコンパイル

$npm run productionを実行すると、本番用の圧縮したファイルを出力してくれる。

圧縮版は、改行やスペース、コメントアウトなどを削除したもの。

$ npm run production

> @ production /Users/s01386/projects/laravel/test-pj
> mix --production

99% done plugins BuildOutputPlugin



   Laravel Mix v6.0.10   


✔ Compiled Successfully in 5416ms
┌───────────────────────────────────────────────────────┬──────────┐
│                                                  File │ Size     │
├───────────────────────────────────────────────────────┼──────────┤
│                                            /js/app.js │ 84.8 KiB │
│                                           css/app.css │ 14 bytes │
└───────────────────────────────────────────────────────┴──────────┘

▼出力されたファイル

app.css
h1{color:red}

改行やスペースが削除されている。


9. URLの自動変換機能(オフにする方法も)

Laravel Mixの便利機能の一つで、urlで画像リソースを指定した場合に、対象のファイルをpublic/imagesフォルダにコピーし、コンパイル後のURLをコピー後のファイルのパスに変更する機能がある。

▼例

css
.example {
  background: url('../../public/img/cat3.png');
}

↓ コンパイル

$ npm run dev

99% done plugins BuildOutputPlugin

   Laravel Mix v6.0.10   

✔ Compiled Successfully in 888ms
┌──────────────────────────────────────────────────────┬───────────┐
│                                                 File │ Size      │
├──────────────────────────────────────────────────────┼───────────┤
│                                           /js/app.js │ 596 KiB   │
│                                          css/app.css │ 105 bytes │
│     images/cat3.png?204c4068c53d4775b6e9cb23998276cb │ 158 KiB   │
└──────────────────────────────────────────────────────┴───────────┘

image.png

▼コンパイル後のCSSファイル

css
.example {
  background: url(/images/cat3.png?204c4068c53d4775b6e9cb23998276cb);
}

画像がパラメータで指定されている。(画像名にパラメータはついていない)

自動コピー&URL変換機能をオフにする

この機能をオフにして、指定したURLをそのままコンパイルしたい場合は、webpack.mix.js内にオプションメソッドで以下を追記する。

webpack.mix.js
.options({
      processCssUrls: false
   });



▼実際のコード例

webpack.mix.js
 mix.sass('resources/sass/app.scss', 'public/css').options({
    processCssUrls: false
 });


10. 複数ファイルをワイルドカードで指定する

scssファイルがいくつもある場合、コンパイル元のファイルと出力先を個別に設定するのは手間。

npmのパッケージの一つであるglobを使うことでファイルをワイルドカードで指定できる。

const mix = require('laravel-mix');
const glob = require('glob');

glob.sync('resources/sass/*.scss').map(function(file) {
  mix.sass(file, 'public/css');
});

・冒頭にconst glob = require('glob');を記述。



▼scssファイルが多いPJ例
image.png



▼jsのコンパイルもグローバル指定した場合の例

webpack.mix.js
const mix = require('laravel-mix');
const glob = require('glob');

glob.sync('resources/sass/*.scss').map(function (file) {
  mix.sass(file, 'public/css').options({
    processCssUrls: false,
  });
});

glob.sync('resources/js/*.js').map(function (file) {
  mix.js(file, 'public/js');
});


参考リンク

Laravel公式 Laravel Mix アセットのコンパイル

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

LaravelでAuthモデルを呼び出す時って、なんでuse App/Authじゃないの?

Q. LaravelのAuthってなんでApp/Authじゃないの?

Laravelでモデルを呼び出すときuse App/User;とかuse App/〇〇なのに、なんでAuthだけuse Auth;で使えるの?
image.png

A. Authに関する色々な機能はLaravelが作ってるからじゃない?

use Auth;で使える直接の理由は、(プロジェクト名)/routes/web.appで、こう(↓)書かれているから。
image.png

認証機能を有効化した段階、ターミナルで$ php artisan ui vue --authを実行した際に記入される。

『なんでそれができるか』という問いに対する個人的な認識、仮定としては、
Laravelで他のモデルはエンジニアが作るけど、AuthLaravel側が作成するからuse Auth;で使えるようになっている

…としか言いようがなくない?

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

LEMPスタックを使ってECS上にLaravel 5.6アプリケーションをデプロイする

このチュートリアルでは、LEMPスタック(Laravel, Nginx, MySQL, PHP)を使用して、Alibaba ECSインスタンス上にLaravel 5.6アプリケーションをデプロイします。

Alibaba Cloud Tech Share 執筆、 Himanshu Gupta。Tech Shareは、技術的な知識やベストプラクティスをクラウドコミュニティ内で共有することを奨励するAlibaba Cloudのインセンティブプログラムです。

Laravelは、シンプルで複雑なWebアプリケーションを開発するために使用されている人気のPHPフレームワークの一つです。Laravelは、API開発やダッシュボードの生成に適したフレームワークであることが判明しています。

前提条件

1、Alibaba Cloudアカウント。Alibaba Cloudアカウントにサインアップすると、新規ユーザー無料クレジットをお楽しみいただけます。アカウントの設定方法がわからない場合は、こちらのステップバイステップのチュートリアルに従ってください。
2、SSHログインのためのsudoアクセスが可能なユーザーアカウント(ルートユーザーでも動作します)
3、このチュートリアルでは、Laravel 5.6アプリケーションを実行するためにLEMPスタックが必要です。LEMPスタックの設定方法がわからない場合は、この記事の手順に従ってください:Alibaba ECSインスタンスでLEMPスタックを設定

注: ただし、Laravel 5.6アプリケーションを実行するには、PHP 7.2を使用する必要があります(上記の記事では、PHP 7.0をインストールするのに役立ちます)。UbuntuにPPAリポジトリを追加してPHP 7.2をインストールし、updateコマンドを使用してUbuntu上のパッケージインデックスを更新します。チュートリアルに従う前に、これらのコマンドを実行してください。

$ sudo apt-get install -y software-properties-common 
$ sudo add-apt-repository ppa:ondrej/php
$ sudo apt-get update

他の必要なPHPモジュールと一緒にPHPをインストールします。

$ sudo apt install php7.2-fpm php7.2-mysql php7.2-common php7.2-mbstring php7.2-xmlrpc php7.2-xml php7.2-intl php7.2-cli php7.2-zip php7.2-curl
php -v

PHP ファイルのみを fpm で提供するには、root 権限で php-fpm 設定ファイルを開きます。

$ sudo nano /etc/php/7.2/fpm/php.ini

パラメータ cgi.fix_pathinfo を探して、プレフィックスのセミコロンを削除してコメントを外し、値を 0 に変更してください。

$ cgi.fix_pathinfo=0

php-fpmを再起動して設定を反映させます。

$ sudo systemctl restart php7.2-fpm

さて、上記の記事の続きですが、Nginx ServerをPHPリクエスト用に設定します。php7.2のバージョンでロケーションブロックの行をphp7.2-fpm.sockに変更してください。

4、サーバーにデプロイする準備ができたLaravelアプリケーションです。どんなLaravelアプリでも構いませんが、ここではサーバー上にデプロイするために、このクイックスタートLaravelアプリケーションを使用します。

ステップ1: 依存関係のインストール

Laravel 5.6アプリケーションをスタック上で実行するために、いくつかの依存関係をインストールします。Alibaba Cloud ECSインスタンスで以下のコマンドを実行します。

root権限を持つユーザーアカウントでサーバーインスタンスにSSHログインします。(Windows用のPutty/Mobaxtermを使用してサーバーインスタンスにログインします。)

$ ssh root@server_ip_address

インスタンスのパッケージマネージャキャッシュを更新します。

$ sudo apt-get update

Composerをインストールして、Laravelアプリケーションに必要なマルチバイト文字列やXMLなどのパッケージと一緒に、LaravelでPHPパッケージを管理します。ComposerはPHPアプリケーションのパッケージの依存関係管理に使用します。

公式ドキュメントでLaravel 5.6のサーバー要件を確認することができます。

$ sudo apt-get install composer unzip zip php7.2-mbstring php7.2-xml php7.2-common php7.2-curl

ステップ2:MySQLサーバの設定

LEMPスタックのインストール時には、MySQLによって作成されたroot管理アカウントを持っていなければなりません。そのため、必要な権限を持つLaravelアプリケーション専用の新しいMySQLユーザーを作成します。

MySQLの管理ルートアカウントにログインします。アクセスにはMySQLのrootパスワードが必要です。

$ mysql -u root -p

アプリケーションの新しいデータベースを作成します。

$ mysql> CREATE DATABASE laravel_db DEFAULT CHARACTER SET utf COLLATE utf8_unicode_ci;

新しいユーザーを作成し、データベースに権限を付与します。私たちは、名前を'laravel_user'とパスワード'password123'でユーザーを作成しています。

$ mysql> GRANT ALL ON laravel_db.* TO 'laravel_user'@'localhost' IDENTIFIED BY 'password123’;

特権をフラッシュして終了します。

$ mysql>  FLUSH PRIVILEGES;
$ mysql>  Exit;

しかし、次のようにデータベースの資格情報をメモしておきます。

  • データベース名: laravel_db
  • データベースユーザー: laravel_user
  • データベースホスト:DBサーバのホスト(同一サーバの場合はlocalhost)
  • データベースパスワード: password123

ステップ3:サーバーにLaravelのアプリケーションコードをアップロードする

これで、Laravelアプリケーションのデータベースが設定されたので、サーバーディレクトリにアプリケーションコードをアップロードします。gitを使ってGithubやBitbucketからリポジトリをクローンすることができます。

Laravelが提供するデモLaravelアプリケーションを使って、Alibaba ECSのインスタンスにインストールします。このデモアプリケーションは、学習目的で使用するシンプルなメモ取りアプリケーションです。

htmlフォルダ内にlaravelappという名前のディレクトリを作成します。

$ sudo mkdir -p /var/www/html/laravelapp

新しいディレクトリlaravelappの所有権を私たちのユーザーに変更するように、私たちのユーザーjohnは、プロジェクトフォルダ内のファイルやフォルダを管理し、操作することができるようになります。

$ sudo chown john:john /var/www/html/laravelapp

laravelappディレクトリに変更し、gitリポジトリからサンプルのLaravelアプリケーションをクローンします。

$ cd /var/www/html/laravelapp
$ git clone https://github.com/laravel/quickstart-basic .

プロジェクトのファイルやフォルダが laravelapp フォルダにコピーされたコンソールには、以下のような出力が表示されます。

------- Git Output-----
Cloning into '.'...
remote: Counting objects: 263, done.
remote: Total 263 (delta 0), reused 0 (delta 0), pack-reused 263
Receiving objects: 100% (263/263), 92.75 KiB | 0 bytes/s, done.
Resolving deltas: 100% (72/72), done.
Checking connectivity... done.

次に、プロジェクトフォルダ内のcomposer installコマンドを実行して、プロジェクトの様々な依存関係をインストールします。Composerは、様々なPHPフレームワークが依存関係を管理するために使用しています。

$ composer install

これは必要なパッケージをインストールしますが、完了するまでに1分ほどかかるかもしれません。composer.jsonファイルに記載されているすべてのパッケージのインストールを示す出力が表示されます。

これでLaravelアプリケーションがデプロイされたので、本番環境、ステージング環境、開発環境などのアプリケーション環境を設定する必要があります。

ステップ4: アプリケーション環境を設定する

Laravelのアプリケーション環境を設定して、アプリケーションがデータベースに接続したり、様々な環境変数を管理できるようにしていきます。
Laravelアプリケーションの環境ファイルを開きます。

$ sudo nano /var/www/html/laravelapp/.env

下図のように変更してください。(変更はカラーと斜体で表示されます)

APP_ENV=production
APP_DEBUG=false
APP_KEY=b809vCwvtawRbsG0BmP1tWgnlXQypSKf
APP_URL=http://yourdomain.com

DB_HOST=127.0.0.1
DB_DATABASE=laravel_db
DB_USERNAME=laravel_user
DB_PASSWORD=password123
...
...

CTRL+Xを押し、Yを押し、Enterを押してファイルを保存して終了します。
データベースの移行を実行して、私たちのアプリケーションのテーブルとインデックスを作成します。本番環境なので、確認のためのプロンプトが表示されます。

$ php artisan migrate

移行の実行について確認を求められる場合は、「はい」と入力してください。

Artisan output
**************************************
*     Application In Production!     *
**************************************

Do you really wish to run this command? [y/N](yes/no) [no]:> yes

Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table

ストレージ、キャッシュ、セッションなどを含むLaravelアプリケーションのファイルやフォルダのパーミッションを設定します。

所有者をwww-dataに変更します。www-dataはウェブサーバーを管理するためのデフォルトのユーザーグループです。

$ sudo chgrp -R www-data storage bootstrap/cache
$ sudo chmod -R ug+rwx storage bootstrap/cache

最後に、アプリケーション内に存在するファイルやフォルダをサーバー化するためにnginxの設定を微調整する必要があります。

ステップ5: アプリケーションのためにNginxサーバーを設定する

ケース1: ドメイン名を持っている場合は、/etc/nginx/sites-availableフォルダに新しいファイルを作成し、デフォルトの設定を上書きすることで、新しいサーバーブロックを作成することができます。

ケース2: 一方、ドメイン名を持っていない場合は、LaravelアプリケーションのためのNginxのデフォルト設定ファイルの変更を行います。

デフォルトのnginx設定ファイルを開きます。

$ sudo nano /etc/nginx/sites-available/default

以下のようにデフォルトファイルの変更をイタリック体と赤色で行います。

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root /var/www/html/laravelapp/public;
    index index.php index.html index.htm index.nginx-debian.html;

    server_name _;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.2-fpm.sock;
    }

    location ~ /\.ht {
        deny all;
    }
}

nginxの設定をテストします。

$ sudo nginx -t

変更を反映させるためにNginxの設定を再起動します。

$ sudo service nginx restart

おめでとうございます。Alibaba Cloud ECSインスタンスにLaravel 5.6アプリケーションをデプロイしました。

ブラウザのIPアドレスにアクセスして、タスクを追加するオプションを持つWebページを表示します。クイックスタートアプリケーションはタスクアプリケーションで、タスクを追加したり削除したりすることができます。

image.png

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

【Laravel】画面に画像を表示する方法。assetsの使い方とurlヘルパ関数との違いについて。

Laravelでブラウザに画像を表示する方法。

1. publicディレクトリに画像をおく

ブラウザが直接読み取るデータなのでpublicディレクトリに設置する。

imgディレクトリを作成しその中に設置。
image.png

2. assetヘルパーで呼び出す

imgタグのsrc属性で、assetsヘルパー関数を使って指定の画像を呼び出す。

<img src="{{ asset('img/cat3.png') }}" alt="">

以上で完了。

image.png


assetヘルパー関数とは?

publicディレクトリ配下の素材へのURLを生成する。

assets('ファイルパス')

ディレクトリの中にある場合は、ディレクトリ名.でつなぐ。

image.png

ビューで呼び出す

ビュー(拡張子 .blade.php)の中で呼び出す場合は、{{ }}をつける。

.blade.php
{{ asset('img/cat3.png') }}
<br>
{{ asset('robots.txt') }}

▼ブラウザの表示

image.png

自分のサイトのpublicフォルダのパスが表示される。

あとは、aタグやimgタグのsrcで指定すれば、各アセットにアクセスできる。

公式ページには以下のように説明されている。(違いはよくわからない)


urlヘルパとassetヘルパの違い

URLを表示するヘルパには、url()という関数もある。

asset()
asset関数は、現在のリクエストのスキーマ(HTTPかHTTPS)を使い、アセットへのURLを生成

url()
url関数は指定したパスへの完全なURLを生成。

結論からいうと、urlassetもほぼ同じ。ただし、urlは引数でパスを渡せるので動的にURLを生成しやすい。


urlヘルパとassetヘルパは同じ使い方ができる

.blade.php
asset: {{ asset('img/cat3.png') }} <br>
<img src="{{ asset('img/cat3.png') }}" alt="">

<hr>

url: {{ url('img/cat3.png') }} <br>
<img src="{{ url('img/cat3.png') }}" alt="">
image.png

全く同じURLが表示される。


urlに特化した使い方

url('prefixのパス', ['後ろに続くパス'])

例1
{{ url('img', ['cat3.png']) }}

image.png


例2
{{ url('column', ['technology', 2020, 1, 10, 'monday']) }}

image.png

動的にURLを指定する際に何かと便利。


参考

Laravel公式 ヘルパ一覧

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

【Laravel】publicとresourcesディレクトリのassets(img, css, js)の違いについて

Laravelのimageファイルやjs, cssファイルの置き場所を探したときに、publicとresourcesディレクトリに混在していることがある。

このpublicとresourcesディレクトリの違いについて。

▼イメージ
image.png

publicとresourcesディレクトリの違い

公開範囲が異なる。

・publicディレクトリ
名前(public)の通り、中身が公開されており、ブラウザが直でアクセスできるデータ。

・resourcesディレクトリ
公開はされておらず、コンパイル前のデータが置かれている。

viewsディレクトリのビュー(.blade.php)もコンパイル前はPHPを含んでいるが、コンパイル後の公開するファイルはHTMLのみとなる。


publicとresourcesディレクトリの使い分け

publicファイルにはブラウザが直でアクセスしてもいいデータを置く。

resoucesの使い方は主に2つ。

(1)サブの画像などを置いておく
公開されないので何を置くかは自分次第。

(2)コンパイルが必要なファイルをおく
.blade.phpやscssなどコンパイル前の元ファイルを置いておく。

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

Laravelで画像をアップロードし、パスをDBに保存する方法

はじめに

Laravelで画像をアップロードし、DBに保存する方法について解説していきます。

まず、今回は画像はプロジェクト内のディレクトリに保存をし、画像のパスをデータベース(以下DB)に保存していきます。
データベースに画像を保存することもできますが、その場合はサイズが大きくなってしまい処理に時間がかかってしまいます。
そのため、画像のパスのみをDBに保存するのが主流のようです。

また、今回はLaravelのプロジェクトは作成し、DBへ接続済であることを前提に進めていくのでご了承ください。

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

モデルとテーブルを作成する

今回は読書の記録をするアプリを推定して作っていきます。データはシンプルに画像、本の題名、著者名にしたいと思います。

早速、モデルとテーブルをコマンドで作成します。

php artisan make:model Book -m

上記のコマンドを実行すると、Bookモデルとbooksテーブルが作成されます。

続いて、booksテーブルにDBに保存する項目を入力していきます。

books_table_php
public function up()
    {
        Schema::create('books', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title', 100);
            $table->string('author', 100);
            $table->string('image');
            $table->timestamps();
        });
    }

そのままマイグレーションします。

php artisan migrate

問題なくマイグレーションできれば、DBにbooksというテーブルが作成されているはずです。

ビューを作成する

続いて、入力フォームを作っていきます。

upload.blade.php
<form method="post" action="{{ route('upload_image') }}" enctype="multipart/form-data">
   @csrf
   <input type="file" name="image">
   <label for="title">題名</label>     
   <input id="title" type="text" name="title">
   <label for="author">作者</label>     
   <input id="author" type="text" name="author">
   <input type="submit" value="登録">
</form>

まず、画像ファイルをアップデートするには必ずformenctype="multipart/form-dataが必要になります。
また、@csrfはCSRFトークンというもので、外部からの悪意のある攻撃から守るための記述であり、Laravelのフォームには記述が必須となっています。

input内を見ていきます。
画像をアップロードする場合はtype属性をfileに指定します。そしてname属性には先ほどbooks_table_phpで指定したカラム名を指定します。

コントローラーを作成する

まずはコマンドでコントローラーを作成します。

php artisan make:controller BookController

BookControllerが作成されたら、利用するモデル名と、使用するヘルパ関数名を冒頭に記述します。もちろん、あとからその都度記述することも可能です。

BookController.php
use App\Book;
use Illuminate\Support\Str;

そして、画像を保存するための処理を書いていきます。

php
public function store(Stock $request)
  {
      $book = new Book();

      $book->title = $request->input('title');
      $book->author = $request->input('author');

       // 保存されているデータを$formに格納する
       $form = $request->all();
       // もし$formにimageデータがあったら
       if (isset($form['image'])) {
         // $fileにイメージデータを格納する
         $file = $request->file('image');
         // getClientOrientalExtension()でファイルの拡張子を取得する
         $extension = $file->getClientOriginalExtension();
         $file_token = Str::random(32);
         $filename = $file_token . '.' . $extension;
         // 表示を行うときに画像名が必要になるため、ファイル名を再設定
         $form['image'] = $filename;
         $file->move('uploads/books', $filename);
        }
        $book->save();

        return redirect('home');
  }

コードにコメントを書いていないところを解説していきます。

まず、getClientOriginalExtension()でファイルの拡張子を取得した後に、Str::random()でランダムな文字列を取得します。Str::random()は( )内に指定された文字数分、ランダムな文字列を作成することができる関数です。今回は32文字を指定しました。
こうして$filenameには画像のパスが「ランダムな32文字 . 拡張子」の状態で保存されていることになります。

続いてこちらを、move()で任意のファイルに保存するように設定します。move('指定したファイル', 保存するファイル名)の順番で記述をすると、Publicフォルダ内に指定したファイルが自動生成され、アップロードした画像が保存されるようになります。

最後に save()メソッドでデータを保存し、今回はredirect()でhomeに戻るように指定しています。

ルーティングを設定する

upload.blade.php内でformactionroute('upload_image')と指定したので、今回はupload_imagename()で指定し、アクセスをしたら、BookControllerのStoreアクションにつながるように記述します。

web.php
Route::post('/upload', 'BookController@store')->name('upload_image');

これで完成です!

さいごに

今回はLaravelで画像をアップロードし、DBに保存する方法について解説しました。
次回以降、保存したデータを表示する方法や編集方法に関しても解説していけたらと思います!

参考

https://youtu.be/il9gsKH9V-8

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

【Laravel】変数の前の(int)や(string)の意味。変数の型を簡単に変換する方法。

Laravelで変数の前に(int)(string)などが使われていることがあるこの意味について。

(型)$変数名の意味

(型)$変数名で変数の型を変換することができる。
これを型キャストと呼ぶ

つまり、(int)は後に続く変数の方を整数にする処理となる。

型キャストの例
$x = "12000"

$y = (int) $x
echo gettype($y) //integer

$z = ( array  ) $x;
var_export($z);  //array (0 => '2000')

カッコ内や型キャストと変数の間にスペースがあっても機能する。



型キャストでbooleanに変換する場合、値が存在すればtrueに変換される。

$x = "12000"
$z = (boolean)$x;
var_export($z);  //true

使用可能な型キャスト一覧

型キャスト 処理
(int) 整数へのキャスト。(integer)と同じ。
(bool) 論理値へのキャスト。(boolean)と同じ。
(float) float へのキャスト。(double), (real)と同じ
(string) 文字列へのキャスト
(array)  配列へのキャスト
(object) オブジェクトへのキャスト
(unset) NULL へのキャスト

https://www.php.net/manual/ja/language.types.type-juggling.php

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

【Laravel】アンダースコア2つの意味。__()の記述は何をしているのか?

Laravelの__( )の処理内容について。

▼こういうの

例1
@if(__('career.'. $career_slug .'.is_recruiting') === "false")
  //処理
@endif
例2
@include('partials.jumbotron', ['title' => __('page.recruit.career.'. $career_slug .'.title')])

__()の意味

  • __ヘルパ関数と呼ぶ。
  • Lang::getの省略形。

現在設定中の言語ファイルから、指定したデータを呼び出すときに使用する。

現在設定中の言語

config > app.php の中の localeを参照。

image.png

ja(日本語)の場合
 'locale' => 'ja',

言語ファイルの場所

resources > lang配下。

image.png

enはデフォルトでプロジェクトを作成した状態。
jaが後から追加したもの。


__()の使い方

例えば、言語設定が'locale' => 'ja'のときに使うと、jaディレクトリの指定したファイルのデータを取得する。

__('ファイル名')
ファイルの中のデータをごっそり取得

__('ファイル名.プロパティ名')
指定したプロパティのデータを取得


実例

@php
  $career_slug = 'editor' 
@endphp

@if(__('career.'. $career_slug .'.is_recruiting') === "false")
  //処理
@endif

上記処理を確認すると、

__('career.'. $career_slug .'.is_recruiting')`

carrer.phpの、editorプロパティ($career_slug)の、is_recruitingプロパティの値を取得している。

image.png

carrer.php
<?php

return [
    'editor' => [
        'is_recruiting' => 'false',
        'position' => "編集",
        'job_description' => "ライティング業務です"
    ],
];

つまり、取得したデータはfalseとなる。


@if(__('career.'. $career_slug .'.is_recruiting') === "false")

@ifの条件式がtrueになるので処理が実行される。


参考

Laravel公式 ヘルパ関数一覧

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

【Laravel】コンポーネントとスロットの使い方を実例で理解する(@component & @slot)

Laravelで使われるコンポーネント(@component)とスロット(@slot)の使い方について。

Laravel公式のコンポーネントとスロットを参照したがコードにslotが複数あったりで機能がわかりにくいので、要素を分解し確認してみた。


目次

  1. ファイルの準備
  2. コンポーネントの中身をそのまま表示
  3. 変数slotでデータを渡す
  4. 指定したslot名でデータを渡す


ファイルの準備

用意するファイルは2つ。

(1)ビュー
viewsフォルダ直下にcomp-slot.blade.phpを作成。

ディレクトリ
app > resources > views > comp-slot.blade.php



(2)コンポーネント
componentsフォルダの中にtest.blade.phpを作成。

ディレクトリ
app > resources > views > components > test.blade.php

ルーティングの設定

app > routes > web.php にルーティングを追記する。

web.php
Route::get('test', function () {
    return view('comp-slot');
});

パスがtestのURIにアクセスがあったら、ビューファイルcomp-slot.blade.phpを開く。


コンポーネントの使い方

コンポーネントの中身を表示する方法は大きく3つ。

1. コンポーネントの中身をそのまま表示
2. 変数slotでデータを渡す
3. 指定したslot名でデータを渡す

2と3でslotが登場するのがわかりにくい、、それぞれ用途が異なる。


1. コンポーネントの中身をそのまま表示

コンポーネントをそのまま呼び出して表示する。(データの受け渡しをしない)

▼コンポーネント

test.blade.php
<div>
    <p>test.blade.phpです</p>
</div>

▼メインビュー

comp-slot.blade.php
@component('components.test')
@endcomponent

@component('コンポーネントファイル名')

  • ファイル名はviewsからの絶対パスを記載。
  • ディレクトリの中にある場合はディレクトリ名をドットで繋ぐ。


画面表示

comp-slot.blade.phpを開くと、コンポーネントの内容が表示された。

image.png


2. 変数slotでデータを渡す。

次に、元のビューファイルの@componentの中に記載したデータを渡す方法について。

▼メインビュー

comp-slot.blade.php
@component('components.test')
  <p>コンポーネントの中身</p>
@endcomponent

@componentの中にある要素をコンポーネントに{{$slot}}を記載することで好きな場所で呼び出すことができる。

▼コンポーネント

test.blade.php
<div>
    <p>test.blade.phpです</p>
    {{$slot}}
</div>
image.png



▼複数呼び出す

test.blade.php
<div>
    {{$slot}}

    <p>test.blade.phpです</p>

    {{$slot}}
</div>
image.png



{{$slot}}を呼び出さない
{{$slot}}を記述しない場合は、@component内の記載は無視される。

▼コンポーネント

test.blade.php
<div>
    <p>test.blade.phpです</p>
</div>
image.png

注意点

@componentの中身が@slotなどで分断して記述してある場合でも、@slotを除いた、ひとまとまりの要素として出力される。

▼メインビュー

comp-slot.blade.php
@component('components.test')
  コンポーネントの中身

  @slot('xxx')
    slotディレクティブのxxx
  @endslot

  コンポーネントの中身
@endcomponent

▼コンポーネント

test.blade.php
<div>
    {{$slot}}
    <p>test.blade.phpです</p>
</div>
image.png


3. 指定したslot名でデータを渡す

@component内に複数のデータのまとまりを作成し、コンポーネントに渡すには、slot('スロット名')を使う。

呼びだしは{{$スロット名}}とする。

▼メインビュー

comp-slot.blade.php
@component('components.test')
  @slot('xxx')
    slotディレクティブの中身
  @endslot
@endcomponent

▼コンポーネント

test.blade.php
<div>
    {{$xxx}}
    <p>test.blade.phpです</p>
</div>
image.png

@slotを複数設置

ひとまとまりのデータを複数作成することができる。

▼メインビュー

comp-slot.blade.php
@component('components.test')
  <p>コンポーネントの中身</p>

  @slot('xxx')
    slotディレクティブの中身
  @endslot
@endcomponent

▼コンポーネント

test.blade.php
@component('components.test')
  @slot('xxx')
    slotディレクティブのxxx
  @endslot

  @slot('yyy')
    slotディレクティブのyyy
  @endslot
@endcomponent
image.png


まとめ

コンポーネントの中身を表示する方法を一つづつ確認すればだいぶわかりやすい。

1. コンポーネントの中身をそのまま表示
2. 変数slotでデータを渡す
3. 指定したslot名でデータを渡す

最後に、3つを複合すると以下のようなコードになる。

▼メインビュー

comp-slot.blade.php
@component('components.test')
  <p>コンポーネントの中身</p>

  @slot('xxx')
    slotディレクティブのxxx
  @endslot

  @slot('yyy')
    slotディレクティブのyyy
  @endslot

  <hr>
  <p>コンポーネントの中身</p>
@endcomponent

▼コンポーネント

test.blade.php
<div>
    {{$yyy}}

    {{$slot}}
    <p>test.blade.phpです</p>

    {{$xxx}}

    {{$slot}}
</div>
image.png
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】プロジェクトの作成からブラウザ経由のDB操作までを実際にやってみる(アプリ作成の手順詳細)

Laravel学習のため、プロジェクト作成しブラウザからのDB操作(新規追加、編集、削除)を自分でやってみる。

目次

  1. composerのインストール
  2. laravelのインストール
  3. laravelサーバーの起動
  4. DBにsqliteを設定
  5. モデルとマイグレーションの作成
  6. マイグレーションでDBのテーブルにカラムを追加
  7. コントローラの作成
  8. ルーティングの設定
  9. seederを使ってテストデータをDBに設定
  10. 一覧画面の作成(ブラウザにDBの全データを表示)
  11. 詳細ページの作成
  12. 編集機能の作成
  13. 削除機能の作成


1. composerのインストール

composerのインストール方法を参照。


2. laravelのインストール

$ composer create-project laravel/laravel [プロジェクト名] --prefer-dist

指定したプロジェクト名でフォルダが作成される。

--prefer-distオプション
高速ダウンロード用。

このオプションをつけると圧縮されていないファイルを優先してダウンロードする。また、すべてのVSC(バージョン管理ソフト)を取得しないので軽量となる。


3. laravelサーバーの起動

php artisan serve

$ php artisan serve
Starting Laravel development server: http://127.0.0.1:8000

表示されたURLにアクセス
http://127.0.0.1:8000

image.png

上記画面が表示されればOK。
サーバーの停止はctrl + c


DBにsqliteを設定

使用が簡単かつmacにデフォルトでインストールされているsqliteをDBに使用する。

laravelインストール時のデフォルトのDBはMySQLのため、環境ファイルの設定を一部変更する。

.envファイルの変更

ルートディレクトリ直下にある.envファイルを開き、以下コードを変更する。

.env(変更前)
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

↓ 以下に変更

.env(変更後)
DB_CONNECTION=sqlite

DB用のファイルを作成する

database/database.sqliteを作成する。

プロジェクトのルートディレクトリで以下を実行。

$ touch database/database.sqlite

image.png

モデルとマイグレーションの作成

DBのテーブルに対応するモデルと、テーブルにカラムを追加したり型指定するマイグレーションファイルを作成する。

(参考)モデルとは?マイグレーションとの違い

$ php artisan make:model [モデル名] -m
  • artisanコマンドのmake:modelでモデルを作成する。
  • -mオプションでマイグレーションファイルも同時生成。(--migrateの省略形)
  • モデル名は冒頭大文字の単数形。単語をつなげる場合はキャメルケースにする(例:MakerCode)



▼モデルの命名規則
DBのテーブル名は複数形とし、Model名はその単数形とする。

・DBのテーブル名: 複数形のスネークケース
・対応するModel名: 冒頭大文字のキャメルケース

<例1>
・DBのテーブル名: articles
・対応するModel名: Article

<例2>
・DBのテーブル名: maker_codes
・対応するModel名: MakerCode



▼(例)Productモデルを作成

$ php artisan make:model Product -m

Model created successfully.
Created Migration: 2021_01_15_022833_create_products_table

2つのファイルが生成される。

・モデル
app > Models > モデル名.php
image.png

・マイグレーション
database > migrations > タイムスタンプcreateテーブル名_table.php
image.png

(補足)モデルとマイグレーションファイルを別々に作る場合

マイグレーションファイルは作成用のartisanコードがあるので、それを使って生成できる。

$ php artisan make:model Product
$ php artisan make:migration create_products_table


マイグレーションでDBのテーブルにカラムを追加する

マイグレーションファイルの編集

作成したいカラムを追記する。デフォルトでidとtimestampsのカラムがある。

up関数の中に追記する。

$table->型('カラム名')

2021_01_15_022833_create_products_table.php
    public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('product_name')->unique(); //追加
            $table->integer('price'); //追加
            $table->boolean('is_stocked')->nullable(); //追加
            $table->timestamps();
        });
    }

必要に応じて、unique()nullable()などの修飾子をつけることができる。


カラムタイプと修飾子

他にもたくさんのカラムタイプが用意されている。
image.png
続きは以下リンク参照。

Laravel公式 使用できるカラムタイプ一覧

▼インデックス修飾子
image.png

▼カラム修飾子
image.png


マイグレーションの実行

作成したマイグレーションファイルの内容をテーブルに反映する。

php artisan migrate



▼migrationの実行例

$ php artisan migrate

Migration table created successfully.
Migrating: 2021_01_15_022833_create_products_table
Migrated:  2021_01_15_022833_create_products_table (0.90ms)

テーブルの作成に成功。

エラー発生時
Illuminate\Database\QueryException
Database (/laravel/test-pj/database/database.sqlite) does not exist. (SQL: PRAGMA foreign_keys = ON;)

対策:$ touch database/database.sqlite を実行


コントローラの作成

DBにデータを追加・読み込み・編集・削除(CRUD操作)するためにコントローラを作成する。

作成時にオプションで-rをつけると、CRUD操作のためのアクションが記載されたコントローラを作成することができる。

$ php artisan make:controller [コントローラ名] -r
  • -r--resourceの省略形
  • コントローラ名は冒頭大文字で、後ろにControllerをつける(例:ProductController)
  • app > Http > Controllerの中にファイルが生成される

--resourceオプションで作成したコントローラを特別にリソースコントローラと呼ぶ。



▼ProcuctControllerの作成例
create, show, editなどCRUD操作に必要なアクションの外枠を既に記述してくれている。
あとは実際の処理を各アクションの中に記述するだけの状態。

$ php artisan make:controller ProductController -r
Controller created successfully.

image.png

▼ファイルの中身

ProductController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProductController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
    }
}


ルーティングの設定

先ほど作成したコントローラの各アクションをルートに登録する。

ルーティングは、app > routes > web.php に記述する。

image.png

リソースコントローラのルート登録は、Routeファサードのresourceメソッドで簡単にできる。

web.php
Route::resource('$URI', 'コントローラ名');
  • $URIは複数形のパスを指定する。



▼実例

web.php
Route::resource('products', 'ProductController');

▼ルートの登録状況を確認

$ php artisan route:list

$ php artisan route:list
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+
| Domain | Method    | URI                     | Name             | Action                                         | Middleware |
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+
|        | GET|HEAD  | api/user                |                  | Closure                                        | api        |
|        | GET|HEAD  | products                | products.index   | App\Http\Controllers\ProductController@index   | web        |
|        | POST      | products                | products.store   | App\Http\Controllers\ProductController@store   | web        |
|        | GET|HEAD  | products/create         | products.create  | App\Http\Controllers\ProductController@create  | web        |
|        | GET|HEAD  | products/{product}      | products.show    | App\Http\Controllers\ProductController@show    | web        |
|        | PUT|PATCH | products/{product}      | products.update  | App\Http\Controllers\ProductController@update  | web        |
|        | DELETE    | products/{product}      | products.destroy | App\Http\Controllers\ProductController@destroy | web        |
|        | GET|HEAD  | products/{product}/edit | products.edit    | App\Http\Controllers\ProductController@edit    | web        |
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+

リソースコントローラの各アクションがルート登録されている。

Laravel8.xでエラーが出た場合は、RouteServiceProvider.phpのコメントアウトを解除する

(補足)sqliteにテーブルが作成されているか確認する

sqliteにアクセスするためには、sqlite3コマンドで、database.sqliteを開く。

ルートディレクトリで以下を実行。
$ sqlite3 database/database.sqlite

$ sqlite3 database/database.sqlite
SQLite version 3.28.0 2019-04-15 14:49:49
Enter ".help" for usage hints.

#テーブル一覧を表示
sqlite> .table
failed_jobs      password_resets  users          
migrations       products  

#productsテーブルの構造(schema)を表示
sqlite> .schema products
CREATE TABLE IF NOT EXISTS "products" (
  "id" integer not null primary key autoincrement,
  "product_name" varchar not null,
  "price" integer not null,
  "is_stocked" tinyint(1) not null,
  "created_at" datetime null,
  "updated_at" datetime null
);

productsテーブルがあり、指定したカラムを持っていることがわかる。

▼sqlite3の主なコマンド

コマンド 内容
.tables テーブル一覧を表示
.schema [テーブル名] 指定したテーブルのスキーマ定義を表示
.exit sqliteを終了
.help ヘルプを表示


seederを使ってテストデータをDBに設定する

seeder(シーダ)とは、DBを手軽にテストするために、テストデータをDBに設定する機能。

$ php artisan make:seeder シーダ名
  • シーダ名は、[テーブル名]TableSeeder とするのが一般的
  • 保存先: app > database > seeders

なかなか便利な機能で、deleteやedit機能などでデータをあれこれ変更した後に、seederをDBに送ればまた初期のデータを復活させることができる。



▼実例

$ php artisan make:seeder ProductTableSeeder
Seeder created successfully.

image.png

ProductTableSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class ProductTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        //
    }
}

run関数を持ったクラスが生成される。

seederの編集

seederファイルのrun関数の中に処理を記述する。
今回はクエリビルダで記述する。冒頭でDBファサードのuse宣言をが必要。

ProductTableSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; //追加

class ProductTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        //以下追加
        DB::table('products')->insert([
            [
                'product_name' => 'iPhone6s',
                'price' => 62000,
                'is_stocked' => false
            ],
            [
                'product_name' => 'iPhone8',
                'price' => 84000, 
                'is_stocked' => true        
            ],
            [
                'product_name' => 'iPhone12',
                'price' => 120000,
                'is_stocked' => true               
            ]           
        ]);
    }
}

今回はクエリビルダで記述しているが、Eroquentでも問題ない。

(参考)Eroquetとクエリビルダとは?


seederをDBに反映する

seederをDBに反映する際は、基本的にDatabaseSeeder.phpを使う。
そのため、作成したクラスをDatabaseSeeder.phpの中で呼び出す。

DatabaseSeeder.php
public function run()
    {
        $this->call(ProductTableSeeder::class);
    }

DatabaseSeeder.phpをメインとして使うとseederファイルを複数に分割している場合も容易にDBに反映できる。

#Composerのオートローダを再生成する
$ composer dump-autoload

#DatabaseSeeder.phpを実行する
$ php artisan db:seed



▼実際の処理例

$ composer dump-autoload
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: fruitcake/laravel-cors
Discovered Package: laravel/sail
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
Generated optimized autoload files containing 4618 classes

$ php artisan db:seed
Seeding: Database\Seeders\ProductTableSeeder
Seeded:  Database\Seeders\ProductTableSeeder (9.16ms)
Database seeding completed successfully.

以上でseederで作成したデータをDBに反映完了。



▼(補足)別のseederファイルを直接DBに反映する
DatabaseSeeder.phpではなく、別途作成したseederファイルのみ読み込ませたい場合は、--classオプションでseederを指定する。

php artisan db:seed --class=ProductTableSeeder


DBのデータを確認する

・DBのデータを抽出してみる

artisanコマンドのtinkerを使って、Laravelの対話モードに入り、Eloquentのallメソッドで指定したモデルのすべてのデータを取得する。

$ php artisan tinker
>>>[モデルの完全な名前空間]::all();

対話モードはexitで抜ける。

tinkerの元の意味は小さな改善をすること。phpの対話モードでコマンドを一つづつ実施することが由来している?



▼実例

ターミナル
$ php artisan tinker
Psy Shell v0.10.5 (PHP 7.3.11  cli) by Justin Hileman
>>>
>>> App\Models\Product::all();
=> Illuminate\Database\Eloquent\Collection {#4309
     all: [
       App\Models\Product {#4308
         id: "1",
         product_name: "iPhone6s",
         price: "62000",
         is_stocked: "0",
         created_at: null,
         updated_at: null,
       },
       App\Models\Product {#4307
         id: "2",
         product_name: "iPhone8",
         price: "84000",
         is_stocked: "1",
         created_at: null,
         updated_at: null,
       },
       App\Models\Product {#4298
         id: "3",
         product_name: "iPhone12",
         price: "120000",
         is_stocked: "1",
         created_at: null,
         updated_at: null,
       },
     ],
   }
>>>
>>> exit
Exit:  Goodbye

指定したデータが取得できている。


・sqliteで確認する

ルートディレクトリで以下を実行。
$ sqlite3 database/database.sqlite

テーブルの中のデータをすべて表示(SQL)
select * form [テーブル名]

$ sqlite3 database/database.sqlite

sqlite> select * from products;
1|iPhone6s|62000|0||
2|iPhone8|84000|1||
3|iPhone12|120000|1||

sqlite> .exit


一覧画面の作成(ブラウザにDBの全データを表示)

取得した全データを表示する一覧画面を作成する。

コントローラのindexアクションを編集する

以前に作成したコントローラのindexアクションの戻り値にDBのすべてのデータを指定する。

Eloquentを使うため冒頭に使用するモデルのクラスをuse宣言する。

ProductController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Product;  //追記

class ProductController extends Controller
{
    public function index()
    {
        $productsInfo = Product::all(); //追記
        return $productsInfo; //追記
    }

//以下略

(参考)Eroquetとは?

サーバーを起動する

$ php artisan serveを実行する。

$ php artisan serve

Starting Laravel development server: http://127.0.0.1:8000


ブラウザで表示する

URIにindexに対応するルートを入力する。(ここでは /products)

ルートは、$php artisan route:listコマンドで確認できる。

image.png

取得したデータの表示に成功。取得データをそのまま返しているのでデータはJSON形式で表示される。


ビューの作成

取得データを見やすく表示するためにビューを作成する。
ビューは resources > views 配下に、拡張子.blade.php
で保存する。

今回は、productsディレクトリを作成し、外側となるbase.blade.phpとメイン部分となるindex.phpを用意する。

▼ディレクトリ構造
image.png

レイアウトの作成

layoutsディレクトリの中にbase.blade.phpを作成する。

base.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>@yield('title')</title>
</head>
<body>
  <h1>@yield('title')</h1>

  @yield('content')
</body>
</html>

メインビューの作成

index.blade.phpを作成する。

index.blade.php
@extends('.products.layouts.base')

@section('title', '製品一覧')

@section('newLink')
  <a href="/products/create">新規作成</a>
  <hr>
@stop

@section('content')
  @foreach ($products as $product)
    <div>
      <h2>{{$product->product_name}}</h2>
      <p>製品価格{{number_format($product->price)}}</p>
      <p>在庫
        @if ($product->is_stocked) あり 
        @else なし
        @endif
      </p>
      <a href="/products/{{$product->id}}/edit">編集する</a> | 
      <a href="/products/{{$product->id}}">商品詳細</a>
      <hr>
    </div>
  @endforeach
@stop

後々作成する、新規作成、編集、商品詳細ページへのリンクも設置してある。

(参考)section, yield, extendsについて


コントローラでビューを指定

ProductControllerのindexアクションで、作成したビュー(index.blade.php)を開くように処理を変更する。

ProductController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Product;  //追記

class ProductController extends Controller
{
    public function index()
    {
        $productsInfo = Product::all();
        return view('products.index', ['products' => $productsInfo]);   //変更
    }

//以下略
  • viewをviewヘルパーと呼び、指定したビューを開く。その際、第2引数でデータを渡せる。
  • resource > products > index.blade.phpは、products.indexとして指定。
  • 第2引数の書き方: ['変数名' => 渡すデータ]



▼ブラウザの表示
保存して、サーバー起動php artisan serve
し、URIを入力する。

http://127.0.0.1:8000/products

image.png

DBから取得したデータが成形して表示されている。


詳細ページの作成

商品詳細ページを作成する。

コントローラの編集

ProductControllerのshowアクションを編集する。

ProductController.php
    public function show($id)
    {
        //受け取ったidの製品情報を格納
        $product = Product::find($id);
        return view('products.show', ['product' => $product]);
    }

URLのproductsの後ろのデータが$idとして渡される。
findメソッドを使って、指定したidのデータを変数$productに格納する。


ビューの編集(show.blade.php)

resources > products 配下にshow.blade.phpを作成する。

show.blade.php
@extends('.products.layouts.base')

@section('title', '製品詳細')

@section('content')
  <div>
    <h2>{{$product->product_name}}</h2>
    <p>製品価格{{number_format($product->price)}}</p>
    <p>在庫
      @if ($product->is_stocked) あり 
      @else なし
      @endif
    </p>
    <a href="/products/{{$product->id}}/edit">編集する</a> |  
    <a href="/products">製品一覧に戻る</a> |
    @component('components.delete-txt', ['product'=>$product])
    @endcomponent
    <hr>
  </div>
@stop

あとは、http://127.0.0.1:8000/products/1 のように、末尾でidを指定すると製品詳細ページが開く。

image.png

編集と削除リンク先は後ほど作成。



▼(補足)削除機能のボタン
削除機能はcomponentとして読み込んでいる。詳細の機能は最後の削除機能で作成する。

    @component('components.delete-txt', ['product'=>$product])
    @endcomponent


新規追加機能の作成

ルーティングの確認

DBに保存するには、createアクションを実行し、storeアクションを行う。

ルートを確認すると、/products/createでcreateアクションを実行、/productsにPOSTでアクセスするとstoreアクション実行となる。

$ php artisan route:list
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+
| Domain | Method    | URI                     | Name             | Action                                         | Middleware |
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+
|        | POST      | products                | products.store   | App\Http\Controllers\ProductController@store   | web        |
|        | GET|HEAD  | products/create         | products.create  | App\Http\Controllers\ProductController@create  | web        |
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+


createビューの作成

新規作成ページとして、create.blade.phpを作成する。

create.blade.php
@extends('.products.layouts.base')

@section('title', '製品の新規追加')

@section('content')
  <form action="/products" method="post">
    @csrf
    <div>
      <label for="product_name">製品名</label>
      <input type="text" name="product_name" required autoforcous>
    </div>
    <div>
      <label for="price">製品価格
      </label>
      <input type="text" name="price" required>
    </div>
    <div>
      <label for="stock">在庫有無</label>
      <select name="stock">
        <option value="1">あり</option>
        <option value="0">なし</option>
      </select>
    </div>
    <div>
    <div>
      <input type="submit" value="送信">
    </div>
  </form>
@stop

@csrf
クロスサイトリクエストフォージェリ対策として必須

(参考)Laravel公式 CSRF保護

@csrfの記述がないと、submit後に419のエラーページに飛ばされる。(419はLaravel独自のエラーページ)

image.png


コントローラの編集

新規追加のためには、createアクションとstoreアクションの2つに処理を追加する。

createアクション

createアクションでは、上記で作成したビューを表示させるのみ。

ProductController.php
    public function create()
    {
        return view('products.create');
    }

storeアクション

storeアクションでは、submitで送られてきたデータを受け取り、DBに保存する処理を記述する。

ProductController.php
    public function store(Request $request)
    {
        $product = new Product;

        $product->product_name = $request->product_name;
        $product->price = $request->price;
        $product->is_stocked = $request->stock;

        $product->save();

        return redirect('products/'.$product->id);
    }

$product = new Product;
Productモデルからインスタンスを作成し、変数に代入。
この変数に各データを保存していく。

$request->[name属性]
submitで送られてきたデータは$requestに保存されている。
name属性を指定してデータを取得する。

$product->save();
すべてのデータを格納したら、saveメソッドでDBに保存する。

return redirect('products/'.$product->id);
新たに作成した製品詳細ページにリダイレクトする。


ブラウザで開く

ブラウザでcreateページを開く。
http://127.0.0.1:8000/products/create

image.png

↓ 送信

image.png

新規保存し、新しい製品詳細ページの表示に成功。


編集機能の作成

ルーティングの確認

既存商品の情報を編集するには、editアクションとupdateアクションを使う。

$ php artisan route:list
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+
| Domain | Method    | URI                     | Name             | Action                                         | Middleware |
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+
|        | PUT|PATCH | products/{product}      | products.update  | App\Http\Controllers\ProductController@update  | web        |
|        | GET|HEAD  | products/{product}/edit | products.edit    | App\Http\Controllers\ProductController@edit    | web        |
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+

products/{product}/editにアクセスして、商品の編集を行い、products/{product}にPUT|PATCHでアクセスすればDBの情報を更新できる。


編集ページの作成

編集用のビュー、edit.blade.phpを作成する。
基本的にはcreate.blade.phpと同じ。PUTでアクセスするために擬似メソッドを設置する。

▼create.blade.phpとの違い

  • formタグのactionでパスが変わる。
  • 擬似フォームメソッドの@method('PUT')を設置。
  • inputタグにvalue属性で既存の設定データを表示。
  • 在庫の有無に合わせてselectタグの初期値を設定。
  • submitボタンのvalueを更新に変更。
edit.blade.php
@extends('.products.layouts.base')

@section('title', '製品の編集')

@section('content')
<form action="/products/{{$product->id}}" method="post">
    @csrf
    @method('PUT')
    <div>
      <label for="product_name">製品名</label>
      <input type="text" name="product_name" value="{{$product->product_name}}" required autoforcous>
    </div>
    <div>
      <label for="price">製品価格
      </label>
      <input type="text" name="price" value="{{$product->price}}" required>
    </div>
    <div>
      <label for="stock">在庫有無</label>
      <select name="stock">
        @php 
          $selected = "selected";
          if ($product->is_stocked)
            $selected = ""
        @endphp
        <option value="1">あり</option>
        <option value="0" {{$selected}}>なし</option>
      </select>
    </div>
    <div>
      <input type="submit" value="更新">
    </div>
  </form>
@stop



・擬似フォームメソッド
HTMLフォームはPUT, PATCH, DELETEリクエストを作成できないため(対応していない)、_methodを偽装する必要がある。

<input type="hidden" name="_method" value="リクエストメソッド名">

type="hidden"をつけると画面上に表示していないデータを送信することができる。_methodというキー名で、指定したリクエストメソッドを値として送信している。

▼putを使う方法
送信ボタンの上に以下タグを設置する。

edit.blade.php
<input type="hidden" name="_method" value="put">
<input type="submit" value="更新">

これを、laravelの擬似フォームメソッドを使うととても簡単にかける。

@method('PUT')

・(参考)Laravel公式 擬似フォームメソッド


(補足)PUTとPATCHの違い
PUTはすべてのデータをまるごと変更する場合に使い、PATCHは一部のデータを変更する場合に使うらしい。
ここでは、PUTとPATCHどちらを使っても同じ結果になる。


コントローラの編集

editアクションとupdateアクションを編集する。

editアクション

Productモデルの指定したidのデータを取得してビューに渡す。

ProductController.php
    public function edit($id)
    {
        $product = Product::find($id);
        return view('products.edit', ['product'=>$product]);
    }

updateアクション

記述はstoreアクションとほぼ同じ。
最初にProductモデルの新規インスタンスではなく、指定したidのデータを取得する。

受け取ったデータ$requestを代入し、saveでDBに保存する。

ProductController.php
    public function update(Request $request, $id)
    {
        $product = Product::find($id);

        $product->product_name = $request->product_name;
        $product->price = $request->price;
        $product->is_stocked = $request->stock;

        $product->save();

        return redirect('products/'.$product->id); 
    }


削除機能の作成

ルーティングの確認

$ php artisan route:list
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+
| Domain | Method    | URI                     | Name             | Action                                         | Middleware |
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+
|        | DELETE    | products/{product}      | products.destroy | App\Http\Controllers\ProductController@destroy | web        |
+--------+-----------+-------------------------+------------------+------------------------------------------------+------------+

products/{product}にDELETEでアクセスすればデータを削除できる。

削除コンポーネントを作成する

削除ボタンは編集と詳細ページに設置する。laravelの機能であるコンポーネント化し簡単に呼び出しを行う。

コンポーネントはボタンとテキストの2種類を用意する。

▼編集
image.png

▼詳細
image.png

ファイルの作成

resources > views > componentsの配下に、ファイルを2つ作成する。

image.png

▼削除ボタンコンポーネント

delete-btn.blade.php
<form action="/products/{{$product->id}}" method="post" id="delete-btn">
      @csrf
      @method('DELETE')
      <input type="submit" name="" value="削除">
</form>

・acrtion属性の値に"/products/{{$product->id}}"を設定。

formはDELETEメソッドをサポートしていないため、ここではpostを指定。

@csrf
クロスサイトリクエストフォージェリ防止機能を追加(必須!)

@method('DELETE')
擬似フォームメソッドでDELETEを指定。これでdeleteメソッドで通信できるようになる。

▼削除テキストコンポーネント

delete-txt.blade.php
<form action='/products/{{$product->id}}' method='POST' id='dlt-txt'>
  @csrf
  @method('DELETE')
  <a onclick='document.getElementById("dlt-txt").submit()'>削除する</a>
</form>

inputタグをテキストに変更するため、JavaScriptのsubmitメソッドを使用。

formタグにid="dlt-btn"を指定し、sbumitの対象をdocument.getElementById(dlt-btn)とする。

以上でコンポーネントが完成。


componentの仕組み

前述のコードで既にcomponentの呼び出し処理は記述してあるが、componentの仕組みについて。

Laravelではblade.phpファイルをコンポーネントとして呼び出すことができる。

extends, yield, sectionの簡易的な機能のようなもの。

コンポーネントの呼び出しはとても簡単で、呼び出したいところに以下のコードを記述する。

  @component('ビューのパス', ['変数名'=>渡したいデータ])
  @endcomponent

ビューのパスは相対ではなく、viewsフォルダからの絶対パスとなる。

image.png

このため、products > edit.blade.php から components >
delete-text.blade.phpを呼び出したい場合は、components.delete-txtとする。

第2引数はデータを渡したいときに使う。読み込むのみでデータを渡す必要がない場合は不要。


ブラウザの表示(削除機能の確認)

image.png

↓ 商品詳細(iPhone8)

image.png

↓ 削除

image.png

削除成功。



バリデーション、CSSの調整はないが、LaravelのCRUD操作の基本はおさえられた。

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

laravel session 仕組み

Laravel sessionの仕組みを追う

SessionServiceProvider.phpでSessionの仕組みがLaravelで使えるようになる。
ここでは、
1. SessionManagerの登録
2. SessionDriverの登録
3. StartSession::classのDI登録
を行う。

SessionServiceProvider.php
    public function register()
    {
        $this->registerSessionManager();

        $this->registerSessionDriver();

        $this->app->singleton(StartSession::class, function () {
            return new StartSession($this->app->make(SessionManager::class), function () {
                return $this->app->make(CacheFactory::class);
            });
        });
    }

registerSessionManager

sessionをキーにSessionManagerを登録する。

    protected function registerSessionManager()
    {
        $this->app->singleton('session', function ($app) {
            return new SessionManager($app);
        });
    }

registerSessionDriver

session.storeでセッションのドライバーが返るようにする。

    protected function registerSessionDriver()
    {
        $this->app->singleton('session.store', function ($app) {
            // First, we will create the session manager which is responsible for the
            // creation of the various session drivers when they are needed by the
            // application instance, and will resolve them on a lazy load basis.
            return $app->make('session')->driver();
        });
    }

StartSession

StartSessionはミドルウェアでKernel.phpの$middlewareGroupsで指定されている。
StartSessionでセッション処理の起動を行う

    protected $middlewareGroups = [
        'web' => [
            \Illuminate\Session\Middleware\StartSession::class,
            ...
        ],

handle関数では以下を行っている。
1. session設定されているか確認し、設定されていないなら次のミドルウェアに処理を進める
2. sessionドライバーを取得
3. ブロックの有無に合わせてセッション管理

    public function handle($request, Closure $next)
    {
        if (! $this->sessionConfigured()) {
            return $next($request);
        }

        $session = $this->getSession($request);

        if ($this->manager->shouldBlock() ||
            ($request->route() instanceof Route && $request->route()->locksFor())) {
            return $this->handleRequestWhileBlocking($request, $session, $next);
        } else {
            return $this->handleStatefulRequest($request, $session, $next);
        }
    }

getSession

getSessionではsessionIdをセットしたドライバーを返す。
$this->manager->driver()ではSessionManagerインスタンスのdriver関数を実行する。
driver関数ではconfigのsession.phpのdriverで指定したdriverインスタンスを返す。

    public function getSession(Request $request)
    {
        // $session->getName()はconfigのsession.cookieの値。それを元にcookie値を取得する。
        return tap($this->manager->driver(), function ($session) use ($request) {
            $session->setId($request->cookies->get($session->getName()));
        });
    }

driverとしてどのインスタンスが返されるかはSessionManagerをみると分かる。
ドライバー作成時にcreateOOOODriver()関数が実行される。OOOはドライバー名。
SessionManagerには↓のような関数があり、具体的なインスタンスが分かる。

    protected function createFileDriver()
    {
        return $this->createNativeDriver();
    }

createNativeDriverではbuildSession関数で暗号するしないの設定に合わせてstoreインスタンスを返します。
ドライバーごとにhandlerインスタンスを作成し、storeインスタンスに渡す。

    protected function createNativeDriver()
    {
        $lifetime = $this->config->get('session.lifetime');

        return $this->buildSession(new FileSessionHandler(
            $this->container->make('files'), $this->config->get('session.files'), $lifetime
        ));
    }

handleStatefulRequest

handleStatefulRequest関数では以下を行う
1. セッションを開始
2. 一定の確率でゴミ掃除
3. 次のmiddlewareを実行
4. 必要ならsessionに現在のurlを設定
5. cookieにsessionを登録

    protected function handleStatefulRequest(Request $request, $session, Closure $next)
    {
        // If a session driver has been configured, we will need to start the session here
        // so that the data is ready for an application. Note that the Laravel sessions
        // do not make use of PHP "native" sessions in any way since they are crappy.
        $request->setLaravelSession(
            $this->startSession($request, $session)
        );

        $this->collectGarbage($session);

        $response = $next($request);

        $this->storeCurrentUrl($request, $session);

        $this->addCookieToResponse($response, $session);

        // Again, if the session has been configured we will need to close out the session
        // so that the attributes may be persisted to some storage medium. We will also
        // add the session identifier cookie to the application response headers now.
        $this->saveSession($request);

        return $response;
    }

startSession

setRequestOnHandlerで必要な場合はハンドラーにリクエストをセットする。(現状CookieSessionHandlerのみ)
start関数では以下を行う
1. sessionをload
2. _tokenがあればtokenを再作成

    protected function startSession(Request $request, $session)
    {
        return tap($session, function ($session) use ($request) {
            $session->setRequestOnHandler($request);

            $session->start();
        });
    }

    public function start()
    {
        $this->loadSession();

        if (! $this->has('_token')) {
            $this->regenerateToken();
        }

        return $this->started = true;
    }

addCookieToResponse

cookieにsessionIdを登録する

    protected function addCookieToResponse(Response $response, Session $session)
    {
        if ($this->sessionIsPersistent($config = $this->manager->getSessionConfig())) {
            $response->headers->setCookie(new Cookie(
                $session->getName(), $session->getId(), $this->getCookieExpirationDate(),
                $config['path'], $config['domain'], $config['secure'] ?? false,
                $config['http_only'] ?? true, false, $config['same_site'] ?? null
            ));
        }
    }

試しにLaravelページのcookieをみるとsessionIdが登録されているのが分かる。
スクリーンショット 2021-01-18 1.38.59.png

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

Axios interceptorsでアクセストークンのリフレッシュを共通処理として実装する

はじめに

Axiosは、HTTP通信を実行するためにJavaScriptで記述されたPromiseベースのHTTPクライアントです。インターセプターと呼ばれる強力な機能が1つあります。
Axios interceptorsを使用すると、リクエストやレスポンスが宛先に到達する前に、リクエストやレスポンスが宛先に到達する前に、コードを実行したり、リクエストやレスポンスを変更したりできます。

今回は認証が必要なエンドポイントにリクエストを行った際に、アクセストークンの有効期限が切れてエラーが返ってきた場合に、トークンのリフレッシュを行い、再度エラーが返されたエンドポイントにリクエストを行う処理をAxios interceptorsを使って実装します。

この記事で説明する範囲

今回はAxios、アクセストークンなどの詳細な説明は省きます。またアクセストークンを保持する場所などの認証に関する説明も省きます。(Axios interceptorsで行いたい目的は変わらない為)
またheaderの部分の実装などはご利用の環境に合わせてください。

1. Axiosにinterceptorsを追加する

以下のように実装すると、1回目のリクエストのレスポンスが返ってきた際に、成功と失敗をハンドリングすることができます。

axios.js
const axios = Axios.create({
   baseURL: 'http://api.localhost',
   headers: {
      'Content-Type': 'application/json',
       Authorization: 'Bearer' + token
   },
   responsetype: 'json'
})

 axios.interceptors.response.use(
   response => {
      // 成功時はレスポンスを呼び出し元に返す
      return response
   },
   function(error) {
      // エラー時
   }
)

2. 401のステータスコードなのか・リトライかをチェックする

本来アクセストークンのリフレッシュを行いたいのは、はじめにの部分で上述しましたが、認証が必要なAPIにリクエストを行った際に、アクセストークンの有効期限切れでエラーが起きた場合です。なのでエラー時にステータスコードを判定します。

axios.js
   function(error) {
      // エラー時
      if (error.response.status === 401) {}
   }

しかしこの判定式の中で、アクセストークンのリフレッシュを行うAPIにリクエストを行うと、リフレッシュが失敗した場合に永遠に処理が続いてしまうことになります。なので、リトライかどうかを条件式に追加し、リトライだった場合はPromiseでエラーオブジェクトを返します

axios.js
 let isRetry = false
 axios.interceptors.response.use(
   response => {
      // 成功時はレスポンスを呼び出し元に返す
      return response
   },
   function(error) {
      // エラー時
      const originalRequest = error.config
      if (error.response.status === 401 && !isRetry) {
          isRetry = true
      } else {
          return Promise.reject(error)
      }
   }
)

3. リフレッシュを行うエンドポイントにリクエストを行う

ここではリフレッシュを行うAPIに対し、リクエストを送ることが可能な場合の処理を記述します。
ここでは成功時にリフレッシュされたアクセストークンをCookieにセットし再度失敗したリクエストを行っています。
失敗時にはログイン画面に遷移させる場合などが多いのでしょうか、そのように実装しています。

axios.js
return axios
   .post('refresh')
       .then(res => {
           if (res.status === 200) {
               // set token to access_token in Cookie
               console.log('refresh success')
               console.log(res.data.access_token)
               Cookies.set('access_token', res.data.access_token, {
                   expires: res.data.expires_in / 1440
               })
               // 3) return originalRequest object with Axios.
               console.log(Cookies.get('access_token'))
                   return axios(originalRequest)
               }
       })
       .catch(() => {
           console.log('refresh error')
           router.push({ name: 'top' })
           return Promise.reject(error)
       })

全体

全体はこのようになります。

aixos.js
const axios = Axios.create({
   baseURL: 'http://api.localhost',
   headers: {
      'Content-Type': 'application/json',
       Authorization: 'Bearer' + token
   },
   responsetype: 'json'
})

let isRetry = false
axios.interceptors.response.use(
   response => {
      // 成功時はレスポンスを呼び出し元に返す
      return response
   },
   function(error) {
      // エラー時
      const originalRequest = error.config
      if (error.response.status === 401 && !isRetry) {
          isRetry = true
          return axios
              .post('refresh')
                  .then(res => {
                      if (res.status === 200) {
                          // set token to access_token in Cookie
                          console.log('refresh success')
                          console.log(res.data.access_token)
                          Cookies.set('access_token', res.data.access_token, {
                          expires: res.data.expires_in / 1440
                          })
                          // 3) return originalRequest object with Axios.
                          console.log(Cookies.get('access_token'))
                          return axios(originalRequest)
                      }
                  })
                  .catch(() => {
                      console.log('refresh error')
                      router.push({ name: 'top' })
                      return Promise.reject(error)
                  })
     } else {
          // 認証以外のエラーで失敗した場合など
          return Promise.reject(error)
     }
   } 
)

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