20200906のlaravelに関する記事は13件です。

Laravel で array 型のリクエストパラメータのバリデーション

PHP-7.2 Laravel-6.x

Laravel で array 型として扱われるようなリクエストパラメータのバリデーションの記述に関するメモです。

実装

下記の例の Request Parameter にはリクエストパラメータを $request->all() で取得したときに得られる array を記述します。
Validation にはとバリデーションルールを表す array を記述します。

それぞれの変数 $data$rules に対して Validator::validate($data, $rules) を Tinker 等で実行すればバリデーションが効いているか試すことができます。

例1. key を省略した array

  • Request Parameter
$data = [
    'article_ids' => [1, 23, 456],
]
  • Validation
$rules = [
    'article_ids' => 'required|array',
    'article_ids.*' => 'int',
];

例2. key と value の組で構成された array

  • Request Parameter
$data = [
    'ingredients' => [
        'egg' => true,
        'peanuts' => false,
        'others' => 'water'
    ],
];
  • Validation
$rules = [
    'ingredients' => 'required|array',
    'ingredients.egg' => 'required|boolean',
    'ingredients.peanuts' => 'required|boolean',
    'ingredients.others' => 'string',
];

例3. ネストした array

  • Request Parameter
$data = [
    'person' => [
        ['first_name' => 'Taro', 'last_name' => 'Tanaka'],
        ['first_name' => 'Hanako', 'last_name' => 'Yamada'],
    ],
];
  • Validation
$rules = [
    'person' => 'required|array',
    'person.*.first_name' => 'required_with:person.*.last_name|string',
    'person.*.last_name' => 'required_with:person.*.first_name|string',
];

参考

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

【Laravel】ファイルをアップロードしたい

アップロードフォームの作成

index.balde.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <form method="POST" action="/upload" enctype="multipart/form-data">
        {{ csrf_field() }}
    <input type="file" id="file" name="file" class="form-control">
    <button type="submit">アップロード</button>
    </form>
</body>
</html>

引用:Laravelでファイルをアップロードする方法を詳細解説

ポイント

ファイルアップロード時はenctype=”multipart/form-data”を必ず指定する

ルート情報

通常表示のために適当にアドレスを書く

Route::get('/','コントローラ名@index');

アップロードファイルの受け取り

    function store(Request $request)
    {
        print_r($request->all());
    }

引用:Laravelでファイルをアップロードする方法を詳細解説

また以下のように書けばファイルのみを見ることができる

print_r($request->file('file'));

fileメソッドの引数としてfileが入っているのはフォームのinput要素のnameプロパティに設定している値。

ルート情報

このメソッドを書いたらpost送信されるときに呼ばれるようにルート情報を書く

Route::post('/','コントローラ名@store');

アップロードされたファイルの保存

public function store(Request $request){

   $request->file('file')->store('');

}

引用:Laravelでファイルをアップロードする方法を詳細解説

名前をつけて保存

パスとファイル名、ディスク名を指定して好きな名前をつけられる

$request->file('file')->storeAs('','image.jgp');

引用:リクエストの取得

複数ファイルのアップロード

input要素を以下に修正

index.blade.php
<input type="file" id="file" name="file[]" class="form-control" multiple>

変更点は以下の2つ

  • nameを"file"から"file[]"に変更
  • multipleを追加

受け取り

前と同じで$request->file('file')で受け取れ、配列となっている。

参考

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

【laravel+mysql】新規でアプリケーションを立上げ、初めてのphp artisan migrateで詰まった件

どんな問題?

初めてPHP言語を使用して、Webアプリケーションを作成するためにlaravelとmysqlで開発環境を整え、新規作成したテーブルをmigrateした際にエラーが発生し、正常にテーブルが作成できない。

エラーの内容

ターミナルで $ php artisan migrateコマンドを入力したところ、以下のようなエラーが発生しました。
6行目の通り、1050 Table 'users' already exists...とあるように「既にuserテーブルは存在あるよ」ということです。

ターミナル
$ php artisan migrate
Migrating: 2014_10_12_000000_create_users_table

   Illuminate\Database\QueryException 

  SQLSTATE[42S01]: Base table or view already exists: 1050 Table 'users' already exists (SQL: create table `users` (`id` bigint unsigned not null auto_increment primary key, `name` varchar(255) not null, `email` varchar(255) not null, `email_verified_at` timestamp null, `password` varchar(255) not null, `remember_token` varchar(100) null, `created_at` timestamp null, `updated_at` timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci')

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

      +9 vendor frames 
  10  database/migrations/2014_10_12_000000_create_users_table.php:24
      Illuminate\Support\Facades\Facade::__callStatic("create")

      +22 vendor frames 
  33  artisan:37
      Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))

前提

macOS ver10.15.4
laravel ver4.0.0導入済み
mysql ver5.6.47導入済み
sequelproに接続済み

エラー発生までの流れ

ターミナル上で

ターミナル
$ laravel new [プロジェクト名]

コマンドで新規プロジェクト作成した際、userテーブルも自動生成されます。(password_resetsテーブルも自動生成されます。)

今回、追加でitemテーブルを作ろうと思い、

ターミナル
$ php artisan make:migration create_items_table

でitemテーブルのマイグレーションファイルを作成し、

ターミナル
$ php artisan migrate

を実行したところ、上記の通り1050 Table 'users' already exists...というエラーが出て、itemテーブルが作成できませんでした。

対処方法

userマイグレーションファイルを以下のように変更しました。

変更前

userマイグレーションファイル
上段省略
class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }
以下省略

変更後

userマイグレーションファイル
上段省略
class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        if (Schema::hasTable('users')) {
            // usersテーブルが存在していればリターンする
            return;
        }
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }
以下省略

userのマイグレーションファイルのpublic function up() の中身に

userマイグレーションファイル
if (Schema::hasTable('users')) {
            // usersテーブルが存在していればリターンする
            return;

の記述を入れて存在しているテーブルをリターンさせたところ、migrateが通りました。

なお、userを解決した後、password_resetsテーブルについても同様の現象が発生したため、同じ要領で対応しました。

最後に

phpに関しては初学者のため、まだまだ解っていないことが多いので、他に最善な解決方法等がありましたらご指摘いただけますと幸いです。

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

laravel/jetstreamが気になりすぎたのでLaravel8をフライングゲットしてみた

はじめに

Laravel8が2020/09/09にリリースされます。開発するときはLTSを選択する保守的な自分にとっては、7.x以降のバージョンにはあまり興味がなかったのですが、今回はlaravel/jetstreamというフロントエンドスタックが導入されたことで気になり始め、リリース日を待たずにインストールして使ってみることにしました。

開発版のLaravel8をインストールする

composer create-project --prefer-dist laravel/laravel laravel8 dev-develop

起動してみる

php artisan serve

ドーン
スクリーンショット 2020-09-06 15.24.27.png

ちゃんと画面右下にBuild v8.x-devて出てますね。

modelの格納先を確認する

Laravel8では、modelはapp直下ではなくapp/Models に格納されます。単純なMVCモデルで開発するとき、いちいちapp/Modelsになるよう設定を変更していた身としてはありがたいです。
スクリーンショット 2020-09-06 15.28.40.png

route:cache使ってみる

laravel8では、クロージャーで書かれていてもroute:cacheが通るようになったので、試してみます。

web.php
<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

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

こういう書き方は、7.xまではcacheできなくて、route:cacheするとエラーになっていました。しかし、laravel8.xからは。

php artisan route:cache
Route cache cleared!
Routes cached successfully!

このように、エラーになることなく実行できます。

さらに改善されたメンテナンスモード

Laravelでは artisan down を実行することでメンテナンスモードに切り替えることができました。ただ、メンテンスモード中も、開発者はテスト等の為にアクセスする必要があり、laravel6.x系リリース時にIPアドレスによるホワイトリスト形式が採用されました。しかしそれも煩雑だということで、8.x系からはCookie方式が採用されたようです。

php artisan down --secret=HOGE

普通にアクセスすると503エラー画面に。
スクリーンショット 2020-09-06 15.54.24.png

--secretで指定したディレクトリにアクセスするとトップページにリダイレクトされ、かつlaravel_maintenanceという名前のCookieが発行されます。例えば、--secret=HOGE なら http://127.0.0.1:8000/HOGE にアクセスします。

これにより、メンテナンス中でも、メンテナンスモードを回避してサイトにアクセスできます。IPアドレスのホワイトリスト形式より格段に便利ですね。
スクリーンショット 2020-09-06 16.05.39.png

さらに、artisan down 時に、--renderと、--statusというオプションが指定できるようになりました。何も指定していないと以下のように503エラー画面がレンダリングされ、statusは503で返りますが……。
スクリーンショット 2020-09-06 16.20.16.png

例えば、php artisan down --render="errors::404" --status=200というように指定することで、404画面をレンダリング、statusは200を返すというようなことが簡単にできるようです。
スクリーンショット 2020-09-06 16.19.40.png

もちろん --renderには、レンダリング可能なbladeテンプレートを自由に指定できるので、例えば、welcome.blade.phpを読み込むようにphp artisan down --render="welcome"と指定すると……。welcome画面をレンダリングしつつ、statusは503を返すみたいなことができるわけです。
スクリーンショット 2020-09-06 16.23.34.png

laravel/jetstream!

さて、本命のjetstreamを使ってみます。実は調べてみて気づいたのですが、デフォルトではjetstreamは同梱されないようです(下記のcomposer.json参照)。laravel/installer を使った場合のみ laravel new project-name --jet というように --jetオプションを指定することで同梱されるようです。

composer.json
    "license": "MIT",
    "require": {
        "php": "^7.3",
        "fideloper/proxy": "^4.2",
        "fruitcake/laravel-cors": "^2.0",
        "guzzlehttp/guzzle": "^7.0.1",
        "laravel/framework": "^8.0",
        "laravel/tinker": "^2.0"
    },
    "require-dev": {
        "facade/ignition": "^2.3.6",
        "fzaninotto/faker": "^1.9.1",
        "mockery/mockery": "^1.3.1",
        "nunomaduro/collision": "^5.0",
        "phpunit/phpunit": "^9.3"
    },
    "config": {
        "optimize-autoloader": true,
        "preferred-install": "dist",
        "sort-packages": true
    },

なので、仕方なくcomposerでインストールしました。ぐぬぬ……。

composer require laravel/jetstream

installした後はartisanコマンドでセットアップを行います。

php artisan jetstream:install [stack]

stackには、Livewireinertiaを指定します。この辺りはプロダクトの要件次第です。SEO必須ならLivewire、特にそういった制約はなく、かつvueやreactなどのメジャーなjsフレームワークに馴染みがあるならinertiaが良いでしょう。ちなみにイナーシャと読みます。今回は、inertiaを選択します。

php artisan jetstream:install inertia
npm install && npm run dev

これでセットアップは完了しました。しかし、このままだとDBのセットアップができていないので、http://127.0.0.1:8000/ にアクセスしてもエラーになります。

SQLSTATE[HY000] [2002] Connection refused (SQL: select * from `sessions` where `id` = x7v2PAHVtjL9uktGtbuedgx1bP3glsWwgbqFI3ZS limit 1)

なので、vesselをサクッと入れてみます。

composer require shipping-docker/vessel --dev
php artisan vendor:publish --provider="Vessel\VesselServiceProvider"
bash vessel init
./vessel start

その上でmigrateを実行します。

./vessel art migrate

準備ができたので、/dashboard にアクセスすると。
スクリーンショット 2020-09-06 16.58.15.png

うまくセットアップできたように見えます。早速 /register に移動してユーザー登録してみます。
スクリーンショット 2020-09-06 17.00.47.png

登録が完了すると、ログイン状態になり/dashboardにリダイレクトされます。
スクリーンショット 2020-09-06 17.02.16.png

ここまでは、従来のログイン/ユーザー登録のスカフォールドと同じ機能群となります。しかし今回からは /profileが追加され、非常にモダンなプロフィール画面が追加されていることがわかります。
profile.png

なんと2要素認証の設定が。ここをENABLEにすると2要素認証が利用可能になります。
スクリーンショット 2020-09-06 17.09.45.png

上記の画面のQRコードをGoogle Authenticatorなどで読み込ませておき、ログインします。ちゃんとコード入力を求められます。
スクリーンショット 2020-09-06 17.10.44.png

Google Authenticatorで表示される値を入力すると、無事dashboard画面が表示されました。
スクリーンショット 2020-09-06 17.02.16.png

加えて、APIトークンを使った認証も最初から提供されているようです。
スクリーンショット 2020-09-06 17.17.07.png

以上。jetstreamのスカフォールドに注目してみました。他に--teams オプションのようなものもあるようですが一旦今までと同じ使い方さえできたら良かったので検証はこれで十分。

また、今回からフロントエンドの技術スタックに TALL-stackとして注目されていたLivewireとtailwindCSSの組み合わせが簡単に導入できるようになったのも大きいです。この辺りは、バックエンド開発にしか馴染みのない人間にとっては特に恩恵が大きいと思いますので、この機会にぜひ色々チャレンジしてみてください。

さいごに

Laravel8では、プロジェクトを作った直後に自分で行なっていたmodelやrouteの設定変更が不要になったこと、またスカフォールドで提供される機能がよりモダンになり、登録制のサイト開発が簡単になったことが印象的です。他に、ジョブ実行でエクスポネンシャルバックオフを簡単に実装できたり、Schema Dumpにより開発環境での初期migrateを簡単にできるようになったとも聞いています。このため開発を実践する上での課題に対して、多くのソリューションを提供してくれているのがlaravel8なのかなと。今後新規開発では、LTSの6を使うか8を使うか実に悩ましいところです。

参考

Release Notes

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

LaravelでFormクラスのインストールメモ

・Class ‘Form’ not found

Laravelで開発したときにFormクラスでデータ送信をしようとしたら、ありませんとのことだったのでインストールしたときのメモ。Laravelのverは7.27.0

composer.jsonのrequireのセクションに以下を追加。

composer.json

"require": {
        "php": "^7.2.5",
        "doctrine/dbal": "^2.10",
        "fideloper/proxy": "^4.2",
        "fruitcake/laravel-cors": "^2.0",
        "guzzlehttp/guzzle": "^6.3",
        "laravel/framework": "^7.24",
        "laravel/tinker": "^2.0",
        "laravelcollective/html": "^6.1.0" //ここを追加
    },

追加後、下記でcomposerを更新

composer update

これで無事Formクラスが使えるようになった。

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

laravelのメール本文のテスト

Mailtrapを使った方法と
メールの受け取り先を共通にする方法の2つを紹介します

Mailtrapを使った方法

https://mailtrap.io/

.envファイルの修正

Mialtrapの設定を見ながらsmtpサーバの設定をenvファイルに反映する
mailtrap.png

MAIL_DRIVER=smtp
MAIL_USERNAME=xxxxxxxxxxxxxx
MAIL_PASSWORD=xxxxxxxxxxxxx
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525

phpunit.xml

一時的にphpunitを使ったメール送信をMailtrapでキャッチしたい場合はMAIL_DRIVERの値が上書きされてしまうので注意
MAIL_DRIVERの値をsmtpにする
ただ戻しておかないとCircleCIとかでテストを回すたびにMailtrapにメールが飛んでMailtrapの制限にいっちゃうので注意

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="vendor/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>

        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
        </whitelist>
    </filter>
    <php>
        <server name="APP_ENV" value="local"/>
        <server name="BCRYPT_ROUNDS" value="4"/>
        <server name="CACHE_DRIVER" value="redis"/>
        <server name="MAIL_DRIVER" value="smtp"/>
        <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
    </php>
</phpunit>

宛先のメールアドレスを変えて自分のメールボックスでメールを受けて確認する方法

config/mail.phpにtoを設定すると全てのメールが指定されたアドレスに送信されます

'to' => [
    'address' => 'example@example.com',
    'name' => 'Example'
],

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

How to make delete button in laravel (File Upload Site #3)

  1. ルートを作成
web.php
Route::delete('files/{id}', 'FilesController@destroy')->name('deletefile');
  1. View ファイルにフォームを追加する
home.blade.php
<th>
<form action="{{ route('deletefile', $file->id) }}" method="POST">
@csrf @method('DELETE')
<button type="submit" class="btn btn-outline-danger btn-delete"><i class="fa fa-trash"></i>Delete</button>
</form>
</th>

@csrf はクロスサイトリクエストフォージェリからの防蟻です。

3.コントローラーにfunction追加

filescontroller.php
public function destroy($id)
    {
        $del = File::find($id);
        Storage::delete($del->path);
        $del->delete();
        return redirect('/home');
    }

Screen Shot 2020-09-06 at 15.21.01.png
こんな感じです。

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

Laravelでフォームのデータを処理する(2/2)DBのデータをトップページに表示する

はじめに

この記事はLaravelでフォームのデータを処理する(1/2)フォームで送信されたデータを取得しDBに挿入するの続きです。

今、掲示板のようなWebサービスを作成していて、Laravelで下記のような機能を実装したいです。
(1)フォームで送信されたデータを取得し、DBにデータとして挿入する。
(2)DBのデータをトップページに並べて表示する。

こんな初歩的な機能ですが、頭を抱えまくったのでメモしておきます。Laravelでこれを実現するには、色々な方法があるみたいですが、初心者の時速40kmの走行方法(教習所内)をメモしておきたいと思います。今回は(2)を実装していきます。

環境:Laravel 7、MAMP 5.7
MAMP以外をご利用の方はそこだけ貴方の環境に合わせて変換していただければと思います。

1)DBにデータが入っていることを確認する

言わずもがなですが、DBが空だと何も表示できないので、ダミーでいいのでデータを入れます。

2)トップページを用意し、プログラムを埋め込む

今回はBladeテンプレートを使用し、/resources/views直下に置いています。。(非常に簡略化しています)
例はライブの感想を投稿する掲示板なので、ライブの日付(live_date)や、ミュージシャン(musician)、会場(venue)、感想(text)などを表示したいです。

index.blade.php
    <div>
     @foreach ($items as $item)
       <p>{{$item->live_date}}</p>
       <p>{{$item->musician}}</p>
       <p>{{$item->venue}}</p>
       <p>{{$item->text}}</p>
     @endforeach
    </div>

ポイント↑:

  • いきなり{{$item->live_date}}みたいなプログラムを埋め込んでしまっていますが、最初は普通にHTMLだけで静的なサイトを準備して、ダミーでデータも書き込んでおいて、あとでその部分をプログラムで書き換えるのがいいと思います。
  • @はディレクティブと呼ばれ、Blade内でここに構文(ifなど)的なプログラムを書くよーという合図です。
  • foreachで$itemsという配列変数を受け取り、それを$itemとして一つずつ抜き出していきます。
  • その$itemは{{$item->live_date}}で使用されます。
  • が、ここに$itemsという変数を渡して上げないといけません。それが次のコントローラの役割です。

3)コントローラ

2で作ったページに渡す変数を定義し、そのページ自体を返すという設定をします。第1話で作ったControllerで、/app/Http/Controllersフォルダに配置しています。

PostController
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use Illuminate\Support\Facades\DB; //DB接続のクラス

class PostController extends Controller
{
    //こちらを追加
    public function index(Request $request)
    {
        $items = DB::select('select * from reviews');
        return view('index', ['items' => $items]);
    }

ポイント↑:

  • DB::selectでDBの全てのデータ*を呼び出し、index.blade.phpに渡す変数$itemsに代入します。
  • Viewsフォルダ直下にあるindex(.blade.php)を呼び出し、第二パラメータで$itemsを渡しています。(正直、その前の'items' =>の意味がわかってません...)
  • あとはこのコントローラとアクションメソッドを呼び出す設定をしてあげればよくて、それをRoutingで行います。

4)ルーティング

web.php
<?php

use Illuminate\Support\Facades\Route; //デフォルトで書いてあるクラス

Route::get('/', 'PostController@index'); //こちらを追加

ポイント↑:

  • 第1パラメータは'/'、つまり、トップページ(=Document Rootを設定しているアドレス)にアクセスがあったらという条件設定
  • 第2パラメータ、PostControllerを呼び出し、そこにあるindexメソッドアクションを、、
  • 呼び起こしなさい(=Route::get)という命令文

5)成功

この状態でドキュメントルートに設定しているページにアクセスすると、DBの情報が存在しているだけ全てトップページに表示されるはずです。

うまく行かない場合のヒント

  • 私は当初、ルーティングのRoute::getをRoute::postにするという、ブレーキとアクセスを踏み間違えるようなミスで、エラーを起こしていました。
  • $items$itemなどの複数形のsのあるなし、$の付け忘れ、その他スペルミスはないか。

最後に

もし何かご指摘などありましたら、コメント欄などで頂戴できますと幸いです。

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

Docker × Laravel コードを自動整形するコンテナを構築する

PHP CS Fixer とは

PHP CS Fixer (PHP Coding Standards Fixer) とは、その名の通りPHPのコードをコーディング規約に沿うよう修正してくれるツールです。

前提

当記事は上記の記事の補足になる記事です。

docker-compose.ymlの編集

docker-compose.yml
services:
  cs:
    image: herloct/php-cs-fixer
    volumes:
      - ./backend:/project

services.cs を追記します。

コマンド

# 自動整形しない(差分表示のみ)
$ docker-compose run cs fix --dry-run -v --diff --diff-format udiff .

# 自動整形する
$ docker-compose run cs fix -v --diff --diff-format udiff .

Makefile

コマンドが長いのでMakefileを用意しておくと良いです。

dry-cs:
    docker-compose run cs fix --dry-run -v --diff --diff-format udiff .
fix-cs:
    docker-compose run cs fix -v --diff --diff-format udiff .

下記のコマンドで実行できるようになります。

$ make dry-cs
$ make fix-cs

参考

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

Laravel で CRUD をコマンド一つで出力するライブラリ

とにかく、マスター管理機能や、CRUDを書くのがめんどくさいと感じていたので、artisan コマンドを拡張して一発で必要なファイルを出力するライブラリを書きました。

使い方などは readme にも書いているのですが、github では英語で書いているので、日本語原文をここに公開しておきます。

laravel-crud-command

前もって作成されたデータベースから情報を取得し、artisan コマンドで CRUD に必要なファイルを一括で出力します。

特徴

  1. MySQLのテーブルコメント、およびカラムコメントから翻訳用のファイルを生成します。
    • resourcees/lang/{locale}/tables.php
    • resourcees/lang/{locale}/columns.php
  2. MySQLのテーブル定義からバリデーションルール作成します。
    • rules/{model}.php
  3. モデル作成
    • テーブルのカラムからプロパティを自動生成します。
    • 外部キー制約から belongsTo と hasMany、belongsToMany メソッド を出力します。
  4. コントローラー作成
    • CRUDに必要なメソッドを全て出力します。
  5. グローバルスコープ作成
    • 各モデルに対して一つのグローバルスコープクラスを作成します。
  6. フォームリクエスト・クラス作成
    • テーブル定義から自動的にルールを出力します。
  7. ビュー・コンポーザー作成
    • 外部キーの定義からフォーム部品をビューに渡すロジックを自動的に生成します。
  8. ビュー作成
    • 一覧、詳細、新規作成、更新 を自動で生成します。
  9. パンくずリスト作成
    • CRUDで生成したファイルには自動的にパンくずリストを出力します。
  10. テンプレートのカスタマイズ
    • プロジェクトによっては、出力するテンプレートをカスタマイズする必要があると思います。その場合は、stub をお好みの形で編集しておくことが可能です。

インストール

composer require shibuyakosuke/laravel-crud-command

セットアップ

1. 何よりも先に、マイグレーションファイルを作成することから始めます。例示のように、コメントと外部キーの設定を必ず行ってください。
  • モデルを生成するテーブルには必ず、テーブルコメントをつけてください。
  • 多対多の中間テーブルにはコメントをつけてはいけません。

テーブルコメント機能については、diplodocker/comments-loader を利用しています。

use Illuminate\Database\Schema\Blueprint;

Schema::create('users', function (Blueprint $table) {
    $table->id()->comment('ID');
    $table->unsignedBigInteger('role_id')->nullable()->comment('ロールID');
    $table->unsignedBigInteger('company_id')->nullable()->comment('会社ID');
    $table->string('name')->comment('氏名');
    $table->string('email')->unique()->comment('メールアドレス');
    $table->timestamp('email_verified_at')->nullable()->comment('メール認証日時');
    $table->string('password')->comment('パスワード');
    $table->rememberToken()->comment('リメンバートークン');
    $table->timestamp('created_at')->nullable()->comment('作成日時');
    $table->timestamp('updated_at')->nullable()->comment('更新日時');
    $table->softDeletes()->comment('削除日時');

    $table->tableComment('ユーザー'); // Table comment helps you to make language files.

    // Foreign key helps you to make belongsTo methods, hasMany methods and views .
    $table->foreign('role_id')->references('id')->on('roles');
    $table->foreign('company_id')->references('id')->on('companies');
});
2. マイグレーションを実行する
php artisan migrate
3. config/app.php を編集して言語を設定する
'locale' => 'ja',
4. リソースを出力します
php artisan crud:setup
5. CRUDファイルを全て出力します
php artisan make:crud users
オプション
  • --force
    ファイルが存在しても、上書きして出力します。
  • --api
    通常のコントローラを出力せず、REST用のコントローラのみを出力します。
  • --with-api
    通常のコントローラとREST用のコントローラを出力します。--apiと同時に指定はできません。
  • --sortable
    テーブルのソート機能を合わせて出力します。
  • --with-export
    テーブルのエクスポート機能を合わせて出力します。
  • --with-filter
    テーブルのフィルタ機能を合わせて出力します。
  • --with-trashed
    テーブルのエクスポート機能を合わせて出力します。

その他コマンド

出力するファイルをカスタマイズする場合、以下のコマンドを実行すると、/stubs ディレクトリに .stub を拡張子に物ファイルが複数出力されます。
出力したファイルをカスタマイズしてください。

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

laravel Class ‘Form’ not found の対処法

調べてみると、composer.jsonでlaravelcollective/htmlを追記してcomposer updateするって書いてあるけど、やってみるとエラーでうまくインストールできなかったので以下をやってみたらすんなりFormファサードを読み込めた

・composer.jsonは編集せずそのまま
・ターミナルから composer require laravelcollective/html ←version指定しない
...これだけ。
すると、composerが自動でlaravelのバージョンに合わせたlaravelcollectiveをインストールしてくれる

$ composer require laravelcollective/html #version指定しない
Using version ^6.1 for laravelcollective/html
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing laravelcollective/html (v6.1.2): Downloading (100%)
Package jakub-onderka/php-console-color is abandoned, you should avoid using it. Use php-parallel-lint/php-console-color instead.
Package jakub-onderka/php-console-highlighter is abandoned, you should avoid using it. Use php-parallel-lint/php-console-highlighter instead.
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Writing lock file
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: laravelcollective/html
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSで割とモダンな技術使う大学生日記①

前置き

terraformで構成しようとしたが、まずはAWSを理解するためコンソールから構築
本記事は私自身が少し悩んだ、ネットワークの構成やトラブルシューティングについて重点的に記載されております。ハンズオン形式の記事はQiita等にも多くございますのでそちらを参照しつつ、こちらの記事でより理解を深めていただければな、と思います。

使用技術
Laravel6.x
AWS ECS(Fargate), RDS, ElasticCache
Docker, docker-compose

ネットワーク構成

VPCは10.1.0.0/16を利用。
サブネットの利用感は下記みたいな感じ

サブネット 用途
10.1.0.0/24, 10.1.1.0/24 Publicサブネット。LBに使うサブネット
10.1.2.0/24, 10.1.3.0/24 Privateサブネット。LBからECSに行くサブネット
10.1.4.0/24, 10.1.5.0/24 ECS~RDS間のサブネット。ECSからDBに接続するサブネット
10.1.6.0/24 EC2~RDS間のサブネット。EC2からDBに接続するサブネット

migrationはDockerfileには置かないのでDB接続用のEC2インスタンスを用意する。
(userのデータとかも取得できるので)

ルートテーブルでInternetGatewayと接続するのはLBとDB接続用のEC2インスタンスのみ。
appサーバーとDBサーバー(RDS)はローカルでしか接続できないように設定する。


※ネットワークの軽い知識があれば上記のサブネットや、CIDRを理解できます。
30分とかで理解できると思うのでYouTubeとかでおさらいしておいてください。

Fargateタイプにおいてのコンテナ間通信について

webコンテナはNginx(読み方はエンジンエックスっていうらしい、最近までエヌジンクスって読んでた)を利用していてappコンテナはPHPでフレームワークはLaravel
そのためwebコンテナからappコンテナへの通信が必要。
ローカル内では下記のように.confファイルを記述する。
ポートはwebはHTTP通信のため80、appは9000
(docker-compose.ymlで名前をそれぞれwebappで指定しておく)

default.conf
01:server {
02:    listen 80;
03:    root /work/public;
04:    index index.php;
05:    charset utf-8;
06:
07:    location / {
08:        root /work/public;
09:        try_files $uri $uri/ /index.php$is_args$args;
10:    }
11:
12:    location ~ \.php$ {
13:        fastcgi_split_path_info ^(.+\.php)(/.+)$;
14:        fastcgi_pass app:9000;
15:        fastcgi_index index.php;
16:        include fastcgi_params;
17:        fastcgi_param SCRIPT_FILENAME /work/public/index.php;
18:        fastcgi_param PATH_INFO $fastcgi_path_info;
19:    }
20:}

上記のような設定だとAWS ECSのFargate起動タイプだとヘルスチェックで
Taskが実行できません。
というエラーが出ます。そこで14行目を下記のように変更します。

default.conf
14:        fastcgi_pass localhost:9000;

上記のようにする理由としては、Fargate起動タイプは同じローカルネットワーク内でtaskが実行されるためlocalhostは共有している。そのためコンテナ間通信はlocalhost:ポート番号で指定する。

トラブルシューティング

ヘルスチェックが通らない。

ECSを起動する際に、ヘルスチェックが通らず、taskが実行できなかったとき。
Dockerfileへの知見が甘かったため、
待つポート番号やコピーするディレクトリを間違えていたりした。
Dockerfileの最後の行にEXPOSE <ポート番号>を指定すること。
また、ローカルでコンテナを起動し期待するディレクトリにソースが入っているかを確認するといい。

ヘルスチェックは通るがLBのDNSに接続してもログインページが表示されない

ECS起動後にヘルスチェックが通るがログインページは表示されない場合。

ヘルスチェックは通っている
          ↓
ローカル内での通信はできている
          ↓
ネットワークの構成を確認し、落ちているところを確認

主に原因はネットワーク。
SGのインバウンドルールは適切か。ルートテーブルは適切か。再度確認する。
この過程が割と大事でCloudFomationとかで構成すると割と曖昧になりがちなネットワーク構成を理解できる。私の場合は、LBのターゲットグループを複数作成しており、利用したいターゲットグループの優先順位が一番ではなかったため、ログインページが表示されていなかった。

セッションの共有

可用性のために、複数のタスクを実行し、AutoScallingをオンにしていると思うので
セッションの共有をしておかなければいけない。
Laravelのセッションはデフォルトでfileを選択しておりProject/storage/sessionsに保存されている。実行されるタスクが複数でセッションの保存先がfileの場合、それぞれのappコンテナの中に保存されてしまうので、セッションの共有をしなければいけない。
今回は、AWSのマネージドサービスであるElasticCacheを利用する。


※実装の工数上、現状(2020/09/06)では未構築のため後日追記する。
その際には上記のネットワーク構成にも修正を加える予定。

Dockerfileとdocker-compose.yml

いや、GitHub載せろよ。って思うよね。
僕的に、Qiita見ながらコピペできるのが理想なのでDockerfiledocker-compose.ymlはここに書きます。default.confは上記に記載済み。Fargate起動タイプの時はlocalhost指定してください。
ディレクトリ構成も載せておきます。

|
├── docker
|   |
│   ├── nginx
|   |   ├── Dockerfile
│   │   └── default.conf
│   └── php
│       ├── Dockerfile
│       └── php.ini
├── docker-compose.yml
|
├── .env
|
├── .gitignore
|
└── live
docker-compose.yml
version: "3"
services:
  app:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
      args:
        - TZ=${TZ}
    ports:
      - ${APP_PORT}:9000
    volumes:
      - ./live:/work
      - ./logs:/var/log/php
      - ./docker/php/php.ini:/usr/local/etc/php/php.ini
    working_dir: /work
    environment:
      # ここは要設定
      - DB_CONNECTION={DB_CONNECTION}
      - DB_HOST=${DB_HOST}
      - DB_DATABASE=${DB_DATABASE}
      - DB_USERNAME=${DB_USERNAME}
      - DB_PASSWORD=${DB_PASSWORD}
      - TZ=${TZ:-Asia/Tokyo}

  web:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    depends_on:
      - app
    ports:
      - ${WEB_PORT:-80}:80
    volumes:
      - ./live:/work
      - ./logs:/var/log/nginx
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    environment:
      - TZ=${TZ:-Asia/Tokyo}

volumes:
  db-store:

docker/nginx/Dockerfile
FROM nginx:1.19-alpine

COPY ./docker/nginx/default.conf /etc/nginx/conf.d/default.conf

COPY ./live /work

EXPOSE 80
dokcer/php/Dockerfile
FROM php:7.4-fpm-alpine

ARG PSYSH_DIR=/usr/local/share/psysh
ARG PHP_MANUAL_URL=http://psysh.org/manual/ja/php_manual.sqlite

ARG TZ

ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer

RUN set -eux && \
  apk update && \
  apk add --update --no-cache --virtual=.build-dependencies \
    autoconf \
    gcc \
    g++ \
    make \
    tzdata && \
  apk add --update --no-cache \
    icu-dev \
    oniguruma-dev \
    libzip-dev && \
  cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
  echo ${TZ} > /etc/timezone && \
  pecl install xdebug && \
  apk del .build-dependencies && \
  docker-php-ext-install intl pdo_mysql mbstring zip bcmath && \
  docker-php-ext-enable xdebug && \
  mkdir $PSYSH_DIR && wget $PHP_MANUAL_URL -P $PSYSH_DIR && \
  curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer && \
  composer config -g repos.packagist composer https://packagist.jp && \
  composer global require hirak/prestissimo

RUN apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev && \
  docker-php-ext-configure gd \
    --with-jpeg=/usr/include/ \
    --with-freetype=/usr/include/ && \
  NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) && \
  docker-php-ext-install -j${NPROC} gd && \
  apk del --no-cache freetype-dev libpng-dev libjpeg-turbo-dev

ADD ./live /work

WORKDIR /work

RUN chmod -R 777 /work/storage \
        /work/bootstrap/cache

RUN composer install
RUN php artisan key:generate
RUN php artisan config:clear
RUN php artisan config:cache
# RUN php artisan migrate
# RUN php artisan db:seed

EXPOSE 9000

php.iniは自分で検索してください。笑
.envファイルがないと起動できないので各々の用途に合わせて記述してください。
一応下記に書いておきます。

TZ=Asia/Tokyo
APP_PORT=9000
DB_CONNECTION=mysql
DB_HOST=<RDSのエンドポイント>
DB_PORT=3306
DB_DATABASE=<DB名>
DB_USERNAME=<username, rootでもいい>
DB_PASSWORD=<セキュアなやつにしてね>

以上です。
まだ、CI/CDとかできていないので別記事にでも書こうと思っております。
AWS第一弾でした。
docker/php/Dockerfileは割と書きすぎてる気がするのでこれいらんよ、とかあったら教えて欲しいです。未熟者の記事ですが読んでいただいてありがとうございます。

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

Laravel6 ユーザと管理者の認証を分けて管理者の認証情報のみ名前とパスワードだけにする

目的

  • ユーザと管理者とで認証を分ける方法をまとめる。

実施環境

  • ハードウェア環境
項目 情報
OS macOS Catalina(10.15.5)
PC 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.3 Homwbrewを用いて導入
Laravel バージョン 6.18.35 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う
MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする

前提条件

  • 先の実施環境に近い環境が整っていること。
  • Laravelは最新バージョン用に構築しているがアプリ作成コマンド実行時に5.6を指定して作成する。

前提情報

  • DockerやAWSなどは使用せずにMacのローカル環境に直接Laravelアプリを作成する。
  • 若干難易度が高い作業であるためなるべくわかりやすい様に丁寧に記載する。
  • 本記事の内容を最初から実施していけば誰でもユーザと管理者別々での認証を行えることを目指す。
  • 管理者認証情報は管理者名、メールアドレス、パスワードとする。
  • 作業複雑化を避けるため管理者用のパスワードはアプリ側から変更できる様にしない。
  • 既存のAuth認証コントローラを追記編集して管理者の認証機能を作成する。
  • 下記のドキュメントを参考に必要名部分のみを抜粋して実装する。また、説明をより分かりやすくするために若干実装の順番を変更する。

読後感

  • Laravel5.6のでユーザ認証と管理者認証機能のついたアプリを作成することができる。

概要

  1. データベースの作成
  2. Laravelアプリの作成と初期設定
  3. ユーザ認証機能作成
  4. 管理者情報用テーブルの準備
  5. ガードとプロバイダの追加
  6. コントローラの修正
  7. 認証用ページの作成
  8. 認証後遷移ページの作成
  9. ルーティングの記載と認証後遷移ページの設定と例外時の処理の記載
  10. 確認

詳細

  1. データベースの作成

    1. 下記コマンドを実行してMySQLにターミナルからログインする。(MySQLのrootユーザのパスワードを忘れてしまった方はこちら→Mac ローカル環境の MySQL 8.x のrootパスワードを忘れた時のリセット方法)

      $ mysql -u root -p
      
    2. 下記SQLを実行して「multi_auth_laravel_6」データベースを作成する。

      create database multi_auth_laravel_6;
      
    3. 下記SQLを実行してデータベース一覧を出力して「multi_auth_laravel_6」が含まれていることを確認する。確認後、MySQLをログアウトする。

      show databases;
      
  2. Laravelアプリの作成と初期設定

    1. Laravelアプリを作成する任意のディレクトリに移動する。
    2. 下記コマンドを実行して「multi_auth_laravel_6」というLaravel6のアプリを作成する。(完了まで時間がかかる可能性があるので少し待機する。)

      $ composer create-project laravel/laravel multi_auth_laravel_6 "6.*"
      
    3. 下記コマンドを実行して作成されたアプリ名ディレクトリに移動する。以後のコマンドは特に記載がない場合、このmulti_authディレクトリ内部で実行する物とする。

      $ cd multi_auth
      
    4. 下記コマンドを実行して.envファイルを開く。

      $ vi .env
      
    5. 下記の様に.envファイルのデータベースの記述を修正する。

      multi_auth_laravel_6/.env
      DB_DATABASE=multi_auth_laravel_6
      DB_USERNAME=root
      DB_PASSWORD=mysql -u root -pコマンドを実行した際に入力したパスワード
      
    6. 修正後の.envファイルの全体の内容を記載する。

      multi_auth_laravel_6/.env
      APP_NAME=Laravel
      APP_ENV=local
      APP_KEY=アプリキーの記載は各個人で異なります。
      APP_DEBUG=true
      APP_URL=http://localhost
      
      LOG_CHANNEL=stack
      
      DB_CONNECTION=mysql
      DB_HOST=127.0.0.1
      DB_PORT=3306
      DB_DATABASE=multi_auth_laravel_6
      DB_USERNAME=root
      DB_PASSWORD=皆さんの環境のMySQLのrootユーザのパスワード
      
      BROADCAST_DRIVER=log
      CACHE_DRIVER=file
      QUEUE_CONNECTION=sync
      SESSION_DRIVER=file
      SESSION_LIFETIME=120
      
      REDIS_HOST=127.0.0.1
      REDIS_PASSWORD=null
      REDIS_PORT=6379
      
      MAIL_MAILER=smtp
      MAIL_HOST=smtp.mailtrap.io
      MAIL_PORT=2525
      MAIL_USERNAME=null
      MAIL_PASSWORD=null
      MAIL_ENCRYPTION=null
      MAIL_FROM_ADDRESS=null
      MAIL_FROM_NAME="${APP_NAME}"
      
      AWS_ACCESS_KEY_ID=
      AWS_SECRET_ACCESS_KEY=
      AWS_DEFAULT_REGION=us-east-1
      AWS_BUCKET=
      
      PUSHER_APP_ID=
      PUSHER_APP_KEY=
      PUSHER_APP_SECRET=
      PUSHER_APP_CLUSTER=mt1
      
      MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
      MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
      
    7. 下記コマンドを実行して初期マイグレーションファイルをマイグレートする。

      $ php artisan migrate
      
    8. 下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    9. 下記にアクセスしてLaravelの初期画面が表示されることを確認する。

  3. ユーザ認証機能作成

    1. 下記コマンドを実行してユーザ認証機能を作成する。

      $ composer require laravel/ui "^1.0" --dev
      $ php artisan ui vue --auth
      $ npm install && npm run dev
      
    2. 下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    3. 下記にアクセスしてLaravelの初期画面を開く。

    4. 右上の「REGISTER」をクリックする。

      Laravel.png

    5. 各種情報を入力して「Register」をクリックする。

      Laravel-2.png

    6. 「Register」をクリック後角の画面に遷移することを確認する。

      Laravel-3.pngLaravel.png

  4. 管理者情報用テーブルの準備

    1. 下記コマンドを実行してAdminモデルファイルとadminsテーブル作成用マイグレーションファイルを作成する。

      $ php artisan make:model Admin -m
      
    2. 下記コマンドを実行して先に作成したマイグレーションファイルを開く。(YYYY_MM_DD_XXXXXXの部分はマイグレーションファイル作成日により異なる。)

      $ vi database/migrations/YYYY_MM_DD_XXXXXX_create_admins_table.php
      
    3. 開いたマイグレーションファイルを下記の様に追記する。

      multi_auth_laravel_6/database/migrations/YYYY_MM_DD_XXXXXX_create_admins_table.php
      <?php
      
      use Illuminate\Support\Facades\Schema;
      use Illuminate\Database\Schema\Blueprint;
      use Illuminate\Database\Migrations\Migration;
      
      class CreateAdminsTable extends Migration
      {
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('admins', function (Blueprint $table) {
                  $table->increments('id');
                  //下記から追加
                  $table->string('name');
                  $table->string('email')->unique();
                  $table->string('password');
                  $table->boolean('is_super')->default(false);
                  $table->rememberToken();
                  //上記までを追加
                  $table->timestamps();
              });
          }
      
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::dropIfExists('admins');
          }
      }
      
      
    4. 下記コマンドを実行して今記載したマイグレーションファイルをマイグレートする。

      $ php artisan migrate
      
    5. 下記コマンドを実行して先に作成したモデルファイルを開く。

      $ vi app/Admin.php
      
    6. 開いたモデルファイルの内容を全て削除し、下記の内容をコピーアンドペーストで記載する

      multi_auth_laravel_6/app/Admin.php
      <?php
      
      namespace App;
      
      use Illuminate\Notifications\Notifiable;
      use Illuminate\Foundation\Auth\User as Authenticatable;
      
      class Admin extends Authenticatable
      {
          use Notifiable;
      
          protected $guard = 'admin';
      
          protected $fillable = [
              'name', 'email', 'password',
          ];
      
          protected $hidden = [
              'password', 'remember_token',
          ];
      }
      
  5. ガードとプロバイダの追加

    1. 下記コマンドを実行しガードとプロバイダを定義しているファイルを開く。

      $ vi  config/auth.php
      
    2. ガードの記載を下記の様に追記する。

      multi_auth_laravel_6/config/auth.php
      /*
      |--------------------------------------------------------------------------
      | Authentication Guards
      |--------------------------------------------------------------------------
      |
      | Next, you may define every authentication guard for your application.
      | Of course, a great default configuration has been defined for you
      | here which uses session storage and the Eloquent user provider.
      |
      | All authentication drivers have a user provider. This defines how the
      | users are actually retrieved out of your database or other storage
      | mechanisms used by this application to persist your user's data.
      |
      | Supported: "session", "token"
      |
      */
      
      'guards' => [
          'web' => [
              'driver' => 'session',
              'provider' => 'users',
          ],
      
          'api' => [
              'driver' => 'token',
              'provider' => 'users',
          ],
          //下記を追記
          'admin' => [
              'driver' => 'session',
              'provider' => 'admins',
          ],
          //上記までを追記
      ],
      
    3. 同ファイル内のプロバイダの記載も追記を行う。

      multi_auth_laravel_6/config/auth.php
      /*
      |--------------------------------------------------------------------------
      | User Providers
      |--------------------------------------------------------------------------
      |
      | All authentication drivers have a user provider. This defines how the
      | users are actually retrieved out of your database or other storage
      | mechanisms used by this application to persist your user's data.
      |
      | If you have multiple user tables or models you may configure multiple
      | sources which represent each model / table. These sources may then
      | be assigned to any extra authentication guards you have defined.
      |
      | Supported: "database", "eloquent"
      |
      */
      
      'providers' => [
          'users' => [
              'driver' => 'eloquent',
              'model' => App\User::class,
          ],
          //下記を追記する
          'admins' => [
              'driver' => 'eloquent',
              'model' => App\Admin::class,
          ],
          //上記までを追記する
      
          // 'users' => [
          //     'driver' => 'database',
          //     'table' => 'users',
          // ],
      ],
      
    4. 追記後のmulti_auth_laravel_6/config/auth.phpのファイルの全体を下記に記載する。

      multi_auth_laravel_6/config/auth.php
      <?php
      
      return [
      
          /*
          |--------------------------------------------------------------------------
          | Authentication Defaults
          |--------------------------------------------------------------------------
          |
          | This option controls the default authentication "guard" and password
          | reset options for your application. You may change these defaults
          | as required, but they're a perfect start for most applications.
          |
          */
      
          'defaults' => [
              'guard' => 'web',
              'passwords' => 'users',
          ],
      
          /*
          |--------------------------------------------------------------------------
          | Authentication Guards
          |--------------------------------------------------------------------------
          |
          | Next, you may define every authentication guard for your application.
          | Of course, a great default configuration has been defined for you
          | here which uses session storage and the Eloquent user provider.
          |
          | All authentication drivers have a user provider. This defines how the
          | users are actually retrieved out of your database or other storage
          | mechanisms used by this application to persist your user's data.
          |
          | Supported: "session", "token"
          |
          */
      
          'guards' => [
              'web' => [
                  'driver' => 'session',
                  'provider' => 'users',
              ],
      
              'api' => [
                  'driver' => 'token',
                  'provider' => 'users',
              ],
              //下記を追記する
              'admin' => [
                  'driver' => 'session',
                  'provider' => 'admins',
              ],
              //上記までを追記する
          ],
      
          /*
          |--------------------------------------------------------------------------
          | User Providers
          |--------------------------------------------------------------------------
          |
          | All authentication drivers have a user provider. This defines how the
          | users are actually retrieved out of your database or other storage
          | mechanisms used by this application to persist your user's data.
          |
          | If you have multiple user tables or models you may configure multiple
          | sources which represent each model / table. These sources may then
          | be assigned to any extra authentication guards you have defined.
          |
          | Supported: "database", "eloquent"
          |
          */
      
          'providers' => [
              'users' => [
                  'driver' => 'eloquent',
                  'model' => App\User::class,
              ],
              //下記を追記する
              'admins' => [
                  'driver' => 'eloquent',
                  'model' => App\Admin::class,
              ],
              //上記までを追記する
              // 'users' => [
              //     'driver' => 'database',
              //     'table' => 'users',
              // ],
          ],
      
          /*
          |--------------------------------------------------------------------------
          | Resetting Passwords
          |--------------------------------------------------------------------------
          |
          | You may specify multiple password reset configurations if you have more
          | than one user table or model in the application and you want to have
          | separate password reset settings based on the specific user types.
          |
          | The expire time is the number of minutes that the reset token should be
          | considered valid. This security feature keeps tokens short-lived so
          | they have less time to be guessed. You may change this as needed.
          |
          */
      
          'passwords' => [
              'users' => [
                  'provider' => 'users',
                  'table' => 'password_resets',
                  'expire' => 60,
              ],
          ],
      
      ];
      
  6. コントローラの修正

    1. 下記の二つのコントローラを修正する。
      • multi_auth_laravel_6/app/Http/Controllers/Auth/LoginController.php
      • multi_auth_laravel_6/app/Http/Controllers/Auth/RegisterController.php
    2. 下記コマンドを実行してログインを司るコントローラファイルを開く。

      $ vi app/Http/Controllers/Auth/LoginController.php
      
    3. 開いたコントローラファイルを下記の様に修正する。

      multi_auth_laravel_6/app/Http/Controllers/Auth/LoginController.php
      <?php
      
      namespace App\Http\Controllers\Auth;
      
      use App\Http\Controllers\Controller;
      use Illuminate\Foundation\Auth\AuthenticatesUsers;
      
      // 下記を追記する
      use Illuminate\Http\Request;
      use Auth;
      // 上記までを追記する
      
      class LoginController extends Controller
      {
          /*
          |--------------------------------------------------------------------------
          | Login Controller
          |--------------------------------------------------------------------------
          |
          | This controller handles authenticating users for the application and
          | redirecting them to your home screen. The controller uses a trait
          | to conveniently provide its functionality to your applications.
          |
          */
      
          use AuthenticatesUsers;
      
          /**
           * Where to redirect users after login.
           *
           * @var string
           */
          protected $redirectTo = '/home';
      
          /**
           * Create a new controller instance.
           *
           * @return void
           */
          public function __construct()
          {
              $this->middleware('guest')->except('logout');
              //下記を追記する
              $this->middleware('guest:admin')->except('logout');
          }
      
          // 下記を追記する
          public function showAdminLoginForm()
          {
              return view('auth.login', ['url' => 'admin']);
          }
      
          public function adminLogin(Request $request)
          {
              $this->validate($request, [
                  'email'   => 'required|email',
                  'password' => 'required|min:6'
              ]);
      
              if (Auth::guard('admin')->attempt(['email' => $request->email, 'password' => $request->password], $request->get('remember'))) {
      
                  return redirect()->intended('/admin');
              }
              return back()->withInput($request->only('email', 'remember'));
          }
          // 上記までを追記
      }
      
    4. 下記コマンドを実行してログインを司るコントローラファイルを開く。

      $ vi app/Http/Controllers/Auth/RegisterController.php
      
    5. 開いたコントローラファイルを下記の様に修正する。

      multi_auth_laravel_6/app/Http/Controllers/Auth/RegisterController.php
      <?php
      
      namespace App\Http\Controllers\Auth;
      
      //下記を追記する
      use App\Admin;
      use Illuminate\Http\Request;
      //上記までを追記する
      use App\User;
      use App\Http\Controllers\Controller;
      use Illuminate\Support\Facades\Hash;
      use Illuminate\Support\Facades\Validator;
      use Illuminate\Foundation\Auth\RegistersUsers;
      
      class RegisterController extends Controller
      {
          /*
          |--------------------------------------------------------------------------
          | Register Controller
          |--------------------------------------------------------------------------
          |
          | This controller handles the registration of new users as well as their
          | validation and creation. By default this controller uses a trait to
          | provide this functionality without requiring any additional code.
          |
          */
      
          use RegistersUsers;
      
          /**
           * Where to redirect users after registration.
           *
           * @var string
           */
          protected $redirectTo = '/home';
      
          /**
           * Create a new controller instance.
           *
           * @return void
           */
          public function __construct()
          {
              $this->middleware('guest');
              //下記を追記する
              $this->middleware('guest:admin');
          }
      
          /**
           * Get a validator for an incoming registration request.
           *
           * @param  array  $data
           * @return \Illuminate\Contracts\Validation\Validator
           */
          protected function validator(array $data)
          {
              return Validator::make($data, [
                  'name' => 'required|string|max:255',
                  'email' => 'required|string|email|max:255|unique:users',
                  'password' => 'required|string|min:6|confirmed',
              ]);
          }
      
          /**
           * Create a new user instance after a valid registration.
           *
           * @param  array  $data
           * @return \App\User
           */
          protected function create(array $data)
          {
              return User::create([
                  'name' => $data['name'],
                  'email' => $data['email'],
                  'password' => Hash::make($data['password']),
              ]);
          }
          //下記を追記する
          public function showAdminRegisterForm()
          {
              return view('auth.register', ['url' => 'admin']);
          }
      
          protected function createAdmin(Request $request)
          {
              $this->validator($request->all())->validate();
              $admin = Admin::create([
                  'name' => $request['name'],
                  'email' => $request['email'],
                  'password' => Hash::make($request['password']),
              ]);
              return redirect()->intended('login/admin');
          }
          //上記までを追記する
      }
      
  7. 管理者ログインページのビューファイルの修正

    1. 下記コマンドを実行してログインページのビューファイルを開く。

      $ vi resources/views/auth/login.blade.php
      
    2. 開いたビューファイルを下記の様に修正する。

      multi_auth_laravel_6/resources/views/auth/login.blade.php
      @extends('layouts.app')
      
      @section('content')
      <div class="container">
          <div class="row justify-content-center">
              <div class="col-md-8">
                  <div class="card">
                      <!-- 下記を修正する -->
                      <div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Login') }}</div>
      
                      <div class="card-body">
                          @isset($url)
                          <form method="POST" action='{{ url("login/$url") }}' aria-label="{{ __('Login') }}">
                          @else
                          <form method="POST" action="{{ route('login') }}" aria-label="{{ __('Login') }}">
                          @endisset
                              @csrf
                              <!-- 上記までを修正する -->
      
                              <div class="form-group row">
                                  <label for="email" class="col-sm-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
      
                                  <div class="col-md-6">
                                      <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required autofocus>
      
                                      @if ($errors->has('email'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('email') }}</strong>
                                          </span>
                                      @endif
                                  </div>
                              </div>
      
                              <div class="form-group row">
                                  <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
      
                                  <div class="col-md-6">
                                      <input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>
      
                                      @if ($errors->has('password'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('password') }}</strong>
                                          </span>
                                      @endif
                                  </div>
                              </div>
      
                              <div class="form-group row">
                                  <div class="col-md-6 offset-md-4">
                                      <div class="form-check">
                                          <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
      
                                          <label class="form-check-label" for="remember">
                                              {{ __('Remember Me') }}
                                          </label>
                                      </div>
                                  </div>
                              </div>
      
                              <div class="form-group row mb-0">
                                  <div class="col-md-8 offset-md-4">
                                      <button type="submit" class="btn btn-primary">
                                          {{ __('Login') }}
                                      </button>
      
                                      <a class="btn btn-link" href="{{ route('password.request') }}">
                                          {{ __('Forgot Your Password?') }}
                                      </a>
                                  </div>
                              </div>
                          </form>
                      </div>
                  </div>
              </div>
          </div>
      </div>
      @endsection
      
  8. 管理者登録ページのビューファイルの修正

    1. 下記コマンドを実行してログインページのビューファイルを開く。

      $ vi resources/views/auth/register.blade.php
      
    2. 開いたビューファイルを下記の様に修正する。

      multi_auth_laravel_6/resources/views/auth/register.blade.php
      @extends('layouts.app')
      
      @section('content')
      <div class="container">
          <div class="row justify-content-center">
              <div class="col-md-8">
                  <div class="card">
                      <!-- 下記を修正する -->
                      <div class="card-header"> {{ isset($url) ? ucwords($url) : ""}} {{ __('Register') }}</div>
      
                      <div class="card-body">
                          @isset($url)
                          <form method="POST" action='{{ url("register/$url") }}' aria-label="{{ __('Register') }}">
                          @else
                          <form method="POST" action="{{ route('register') }}" aria-label="{{ __('Register') }}">
                          @endisset
                              @csrf
                              <!-- 上記までを修正する -->
      
                              <div class="form-group row">
                                  <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
      
                                  <div class="col-md-6">
                                      <input id="name" type="text" class="form-control{{ $errors->has('name') ? ' is-invalid' : '' }}" name="name" value="{{ old('name') }}" required autofocus>
      
                                      @if ($errors->has('name'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('name') }}</strong>
                                          </span>
                                      @endif
                                  </div>
                              </div>
      
                              <div class="form-group row">
                                  <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>
      
                                  <div class="col-md-6">
                                      <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" required>
      
                                      @if ($errors->has('email'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('email') }}</strong>
                                          </span>
                                      @endif
                                  </div>
                              </div>
      
                              <div class="form-group row">
                                  <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
      
                                  <div class="col-md-6">
                                      <input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>
      
                                      @if ($errors->has('password'))
                                          <span class="invalid-feedback" role="alert">
                                              <strong>{{ $errors->first('password') }}</strong>
                                          </span>
                                      @endif
                                  </div>
                              </div>
      
                              <div class="form-group row">
                                  <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>
      
                                  <div class="col-md-6">
                                      <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
                                  </div>
                              </div>
      
                              <div class="form-group row mb-0">
                                  <div class="col-md-6 offset-md-4">
                                      <button type="submit" class="btn btn-primary">
                                          {{ __('Register') }}
                                      </button>
                                  </div>
                              </div>
                          </form>
                      </div>
                  </div>
              </div>
          </div>
      </div>
      @endsection
      
  9. 認証後遷移ページの作成

    1. 下記コマンドを実行してビューファイルを作成する。

      $ touch resources/views/layouts/auth.blade.php
      $ touch resources/views/admin.blade.php
      
    2. 下記コマンドを実行して先に作成したビューファイルを開く。

      $ vi resources/views/layouts/auth.blade.php
      
    3. 下記の内容をコピーアンドペーストで貼り付ける。

      multi_auth_laravel_6/resources/views/layouts/auth.blade.php
      <!DOCTYPE html>
      <html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
      <head>
          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1">
      
          <!-- CSRF Token -->
          <meta name="csrf-token" content="{{ csrf_token() }}">
      
          <title>{{ config('app.name', 'Laravel') }}</title>
      
          <!-- Scripts -->
          <script src="{{ asset('js/app.js') }}" defer></script>
      
          <!-- Fonts -->
          <link rel="dns-prefetch" href="https://fonts.gstatic.com">
          <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css">
      
          <!-- Styles -->
          <link href="{{ asset('css/app.css') }}" rel="stylesheet">
      </head>
      <body>
          <div id="app">
              <nav class="navbar navbar-expand-md navbar-light navbar-laravel">
                  <div class="container">
                      <a class="navbar-brand" href="{{ url('/') }}">
                          {{ config('app.name', 'Laravel') }}
                      </a>
                      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                          <span class="navbar-toggler-icon"></span>
                      </button>
      
                      <div class="collapse navbar-collapse" id="navbarSupportedContent">
                          <!-- Left Side Of Navbar -->
                          <ul class="navbar-nav mr-auto">
      
                          </ul>
      
                          <!-- Right Side Of Navbar -->
                          <ul class="navbar-nav ml-auto">
                              <!-- Authentication Links -->
                             <li class="nav-item dropdown">
                                  <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                      Hi There <span class="caret"></span>
                                  </a>
      
                                  <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                      <a class="dropdown-item" href="{{ route('logout') }}"
                                         onclick="event.preventDefault();
                                                       document.getElementById('logout-form').submit();">
                                          {{ __('Logout') }}
                                      </a>
      
                                      <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                          @csrf
                                      </form>
                                  </div>
                              </li>
                          </ul>
                      </div>
                  </div>
              </nav>
      
              <main class="py-4">
                  @yield('content')
              </main>
          </div>
      </body>
      </html>
      
    4. 下記コマンドを実行して先に作成したビューファイルを開く。

      $ vi resources/views/admin.blade.php
      
    5. 下記の内容をコピーアンドペーストで貼り付ける。

      multi_auth_laravel_6/resources/views/admin.blade.php
      @extends('layouts.auth')
      
      @section('content')
      <div class="container">
          <div class="row justify-content-center">
              <div class="col-md-8">
                  <div class="card">
                      <div class="card-header">Dashboard</div>
      
                      <div class="card-body">
                          Hi boss!
                      </div>
                  </div>
              </div>
          </div>
      </div>
      @endsection
      
    6. 下記コマンドを実行して先に作成したビューファイルを開く。

      $ vi resources/views/layouts/home.blade.php
      
    7. すでに記載されている内容を削除し下記の内容をコピーアンドペーストで貼り付ける。

      multi_auth_laravel_6/resources/views/layouts/home.blade.php
      @extends('layouts.auth')
      
      @section('content')
      <div class="container">
          <div class="row justify-content-center">
              <div class="col-md-8">
                  <div class="card">
                      <div class="card-header">Dashboard</div>
      
                      <div class="card-body">
                           Hi there, regular user
                      </div>
                  </div>
              </div>
          </div>
      </div>
      @endsection
      
  10. ルーティングの記載と認証後遷移ページの設定と例外時の処理の記載

    1. 下記コマンドを実行してルーティングファイルを開く。

      $ vi routes/web.php
      
    2. 下記の様に追記する。

      multi_auth_laravel_6/routes/web.php
      <?php
      
      /*
      |--------------------------------------------------------------------------
      | Web Routes
      |--------------------------------------------------------------------------
      |
      | Here is where you can register web routes for your application. These
      | routes are loaded by the RouteServiceProvider within a group which
      | contains the "web" middleware group. Now create something great!
      |
      */
      
      Route::get('/', function () {
          return view('welcome');
      });
      
      Auth::routes();
      
      Route::get('/home', 'HomeController@index')->name('home');
      
      //下記を追記する
      Route::get('/login/admin', 'Auth\LoginController@showAdminLoginForm');
      Route::get('/register/admin', 'Auth\RegisterController@showAdminRegisterForm');
      
      Route::post('/login/admin', 'Auth\LoginController@adminLogin');
      Route::post('/register/admin', 'Auth\RegisterController@createAdmin');
      
      Route::view('/home', 'home')->middleware('auth');
      Route::view('/admin', 'admin');
      //上記までを追記する
      
  11. リダイレクトの設定

    1. 下記コマンドを実行してリダイレクトを司るミドルウェアファイルを開く

      $ vi app/Http/Controllers/Middleware/RedirectIfAuthenticated.php
      
    2. 下記の様に修正する。

      multi_auth_laravel_6/app/Http/Controllers/Middleware/RedirectIfAuthenticated.php
      <?php
      
      namespace App\Http\Middleware;
      
      use Closure;
      use Illuminate\Support\Facades\Auth;
      
      class RedirectIfAuthenticated
      {
          public function handle($request, Closure $next, $guard = null)
          {
              // 下記を追記する
              if ($guard == "admin" && Auth::guard($guard)->check()) {
                  return redirect('/admin');
              }
              // 上記までを追記する
              if (Auth::guard($guard)->check()) {
                  return redirect('/home');
              }
      
              return $next($request);
          }
      }
      
  12. 例外時の設定

    1. 下記コマンドを実行してハンドラーファイルを開く。

      $ vi app/Exceptions/Handler.php
      
    2. 下記のようにハンドラーファイルを修正する。

      multi_auth_laravel_6/app/Exceptions/Handler.php
      <?php
      
      namespace App\Exceptions;
      
      use Exception;
      use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
      
      //下記を追記する
      use Illuminate\Auth\AuthenticationException;
      use Auth; 
      //上記までを追記する
      
      class Handler extends ExceptionHandler
      {
          /**
           * A list of the exception types that are not reported.
           *
           * @var array
           */
          protected $dontReport = [
              //
          ];
      
          /**
           * A list of the inputs that are never flashed for validation exceptions.
           *
           * @var array
           */
          protected $dontFlash = [
              'password',
              'password_confirmation',
          ];
      
          /**
           * Report or log an exception.
           *
           * @param  \Exception  $exception
           * @return void
           */
          public function report(Exception $exception)
          {
              parent::report($exception);
          }
      
          /**
           * Render an exception into an HTTP response.
           *
           * @param  \Illuminate\Http\Request  $request
           * @param  \Exception  $exception
           * @return \Illuminate\Http\Response
           */
          public function render($request, Exception $exception)
          {
              return parent::render($request, $exception);
          }
          //下記を追記する
          protected function unauthenticated($request, AuthenticationException $exception)
          {
              if ($request->expectsJson()) {
                  return response()->json(['error' => 'Unauthenticated.'], 401);
              }
              if ($request->is('admin') || $request->is('admin/*')) {
                  return redirect()->guest('/login/admin');
              }
              return redirect()->guest(route('login'));
          }
          //上記までを追記する
      
      }
      
  13. 確認

    1. 下記コマンドを実行してローカルサーバを起動する。

      $ php artisan serve
      
    2. 下記にアクセスしてLaravelの初期画面が表示されることを確認する。

    3. 下記にアクセスし必要情報を入力後「Registar」をクリックする。

    4. 下記の画面にリダイレクトすることを確認する。一つ前の手順で入力した管理者登録時の情報を入力し「Login」をクリックする。

      Laravel-10.png

    5. 下記のページ「Hi boss!」が表示されれば管理者としてのログインは完了である。

      Laravel.png

参考文献

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