20190227のPHPに関する記事は17件です。

MVC 概要

そもそもMVCとは?

MVCはModelViewControllerの頭文字をとったもの。
大まかに言うとこれら3役がそれぞれ役割を担っていて、アプリケーションを作っていく上での考え方や概念といった基盤になる。
ここにデータベースやルーティングが絡んできて、アプリケーションの動きによってサイクルができる。

なぜMVCを考えなければいけない?

アプリケーションが簡単なうちはどこがどうなっているのかは把握できるかもしれないが、どんどん複雑になっていく上でその管理が難しくなってくる。
そこでMVCという考え方を持っていればそれぞれが役割を担っているためどこに何があるか把握ができ、状態の管理がしやすくなる。

Model

既存のデータベースをいじれるメソッドを色々持っている。ビジネスロジックと呼ばれるかも。
ここで書くコードとして、データベース上のどのカラムをいじれるかの設定や、データベースのテーブル同士でリレーションをつけたりする。

モデル名.php
//いじれるカラム名を指定する、$fillableではなく$guardedを使っていじれないカラムを指定する方法もある。

protected $fillable = [
    'column1',
    'column2',
];

//このモデルに付随するテーブルの1要素が、
//Exampleモデルに付随するテーブルの複数要素と関係性をもつことを表している。
//他にも$this->belongsToにしてこのテーブルは指定したテーブルの1要素と関係性をもつことを表すこともできる。

public function examples()
{
    return $this->hasMany('\App\Example');
}

Controller

ModelViewの間に立つものと考える。
ここではモデルをuseしてモデルのメソッドを使ってデータベースから取得したデータをViewに渡したり、Viewから入力された値に対してバリデーションをつけたりすることができる。
また、モデルのメソッドを使って新たにデータベースのテーブルに値を追加することもできる。

コントローラ名.php
use App\Example;

//Exampleモデルに付随するテーブルから、作られた時期の降順で取得し、
//view関数を使ってpages.indexファイルに取得したデータを連想配列で渡す。

public function index()
{
    $examples = Example::orderBy('created_at', 'desc')->get();

    return view('pages.index', ['examples' => $examples]);
}

//フォームからの入力を新たにデータベースに追加する関数

public function store(Request $request)
{

//$rulesでバリデーションを指定している。
//'required'は必須項目で、'email'はメール形式を指定してvalidate関数を使う。

    $rules = [
        'example1' => ['required'],
        'example2' => ['requierd', 'email'],
    ];

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

//モデルのcreateメソッドを使ってデータベースに追加する。

    Example::create([
        'example1' => $request['example1'],
        'example2' => $request['example2'],
    ]);

//トップページに遷移する

    return redirect('/');
}

View

Controllerから受け取ったデータを画面に表示する。

ビュー名.blade.php
//foreachを使ってコントローラで取得したデータのタイトルを表す。

<div>
    @foreach($examples as $example)
        <div>
            {{  $example->$title }}
        </div>
    @endforeach
</div>

まとめ

MVCについて簡単にまとめました。
Laravelをもっと深く理解したいです。。

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

macにPHPとapacheをbrewから入れてみた

大変すぎた。元々入ってるphpとapacheが邪魔しまくってきたのでログを取っておく
CakePHPで作ったプロジェクトが動くところはまでは行くかと思います。

何はともあれ、まずhomebrewの最新化

$ brew update

PHPインストール

PHPの検索

$ brew search php

php7.1(お好きなバージョン)をインストール

$ brew install php@7.1

インストールしたPHPを使うように設定変更

$ brew link php@7.1
上記で出てきた以下のコマンドを実行する
$ echo 'export PATH="/usr/local/opt/php@7.1/bin:$PATH"' >> ~/.bash_profile
$ echo 'export PATH="/usr/local/opt/php@7.1/sbin:$PATH"' >> ~/.bash_profile

設定変わったか確認

$ which php

/usr/local/opt/php@7.1/bin/php

apacheをインスール

元から入ってるapacheの自動起動を止める

$ sudo apacehctl stop
$ sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist

apacheを検索

$ brew search httpd

apacheをインストール

$ brew install httpd

設定変更

$ vi /usr/local/etc/httpd/httpd.conf

コメントを外す

#LoadModule userdir_module lib/httpd/modules/mod_userdir.so
#Include /usr/local/etc/httpd/extra/httpd-userdir.conf
#LoadModule rewrite_module modules/mod_rewrite.so

LoadModuleしてる辺りに追記
LoadModule php7_module /usr/local/opt/php@7.1/lib/httpd/modules/libphp7.so
<IfModule php7_module>
  AddType application/x-httpd-php .php
</IfModule>
書き換え

#ServerName www.example.com:8080

ServerName localhost

DirectoryIndex index.html

DirectoryIndex index.php index.html

DocumentRoot "/usr/local/var/www"
<Directory "/usr/local/var/www">

DocumentRoot "作成しているプロジェクトのディレクトリ"
<Directory "作成しているプロジェクトのディレクトリ">
上で書き換えたの中を変更

AllowOverride None

AllowOverride All

mysqlをインストール

$ brew search mysql
$ brew install mysql@5.6
$ brew link mysql@5.6
$ echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.bash_profile

mysql起動

$ source ~/.bash_profile
$ mysql.server start

mysql.sockの設定

$ mysqladmin version
UNIX socket っという項目の後に書いてあるmysqld.sockファイルの場所を記録
/usr/local/etc/php/7.1/php.iniに記述する
pdo_mysql.default_socket=[sockファイル]

$ apacehctl start
$ mysql.server start

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

WordPressで管理バー(wp_admin_bar)で項目別に非表示する方法

WordPressの管理画面の上部に表示される、wp_admin_barですが、管理者で表示されるの良いんですが、実際つかうエンドユーザー(投稿や編集者権限)に表示されるのは、なんとなく嫌なので、いつも非表示にしています。

で、いつも非表示にするときググるのでいい加減メモを残そうと思い、本投稿をします。

早速ですが、サンプルソースを。

サンプルソース

functions.phpに以下を記載する。

/**
 * wp管理バーの削除します。
 */
function my_remove_adminbar_menu( $wp_admin_bar ) {
    $wp_admin_bar->remove_menu( 'wp-logo' );      // WPロゴ
    $wp_admin_bar->remove_menu( 'site-name' );    // サイト名
    $wp_admin_bar->remove_menu( 'view-site' );    // サイト名 -> サイトを表示
    $wp_admin_bar->remove_menu( 'dashboard' );    // サイト名 -> ダッシュボード (公開側)
    $wp_admin_bar->remove_menu( 'themes' );       // サイト名 -> テーマ (公開側)
    $wp_admin_bar->remove_menu( 'customize' );    // サイト名 -> カスタマイズ (公開側)
    $wp_admin_bar->remove_menu( 'comments' );     // コメント
    $wp_admin_bar->remove_menu( 'updates' );      // 更新
    $wp_admin_bar->remove_menu( 'view' );         // 投稿を表示
    $wp_admin_bar->remove_menu( 'new-content' );  // 新規
    $wp_admin_bar->remove_menu( 'new-post' );     // 新規 -> 投稿
    $wp_admin_bar->remove_menu( 'new-media' );    // 新規 -> メディア
    $wp_admin_bar->remove_menu( 'new-link' );     // 新規 -> リンク
    $wp_admin_bar->remove_menu( 'new-page' );     // 新規 -> 固定ページ
    $wp_admin_bar->remove_menu( 'new-user' );     // 新規 -> ユーザー
    $wp_admin_bar->remove_menu( 'my-account' );   // マイアカウント
    $wp_admin_bar->remove_menu( 'user-info' );    // マイアカウント -> プロフィール
    $wp_admin_bar->remove_menu( 'edit-profile' ); // マイアカウント -> プロフィール編集
    $wp_admin_bar->remove_menu( 'logout' );       // マイアカウント -> ログアウト
    $wp_admin_bar->remove_menu( 'search' );       // 検索 (公開側)
}
add_action('admin_bar_menu', 'my_remove_adminbar_menu', 201);

サンプルソース(管理者権限を除く)

functions.phpに以下を記載する。

なんとなく、管理者権限(自分ですよね)は、すべて表示された方が気持ちいいです。

/**
 * wp管理バーの削除します。
 */
function my_remove_adminbar_menu( $wp_admin_bar ) {
    if ( current_user_can( 'administrator' ) ) {
        return; // リターンさせちゃいましょう
    }
  // 以下、省略
}
add_action('admin_bar_menu', 'my_remove_adminbar_menu', 201);

まとめ

これでコピペでいける。ググる時間短縮です!

参考URL
【WordPress】管理バー(admin bar)のメニュー項目を削除する方法 | よしあかつき
https://yosiakatsuki.net/blog/remove-menu-admin-bar/

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

【laravel5.7】 Multi Authログイン

laravelの機能、MultiAuthログインの実装メモ。

環境

MacOS 10.14.3
VisualStudio
laravel 5.7

やりたいこと

Admin、Userページそれぞれでログイン、ログアウトさせる

準備

1. プロジェクトの作成

terminal
composer create-project laravel/laravel multiauth_test

2. DBの作成
3. .envを編集

.env
DB_DATABASE=作ったDB名
DB_USERNAME=ユーザ名
DB_PASSWORD=パスワード

User認証

認証機能のインストール

terminal
php artisan make:auth

テーブル作成

terminal
php artisan migrate

usersはデフォルトでファイルが用意されているので、これだけでOK

auth.php

config/auth.php
'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],

+        'user' => [
+           'driver' => 'session',
+            'provider' => 'users',
+       ],
    ],

Handler.php

認証エラーだった時の処理をカスタマイズ。
オーバーライドで書き込みます。

app/Exceptions/Handler.php
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

+use Request;
+use Response;
+use Illuminate\Auth\AuthenticationException;

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 (in_array('user', $exception->guards())) {
+            return redirect()->guest('user/login');
+       }
+        return redirect()->guest(route('login'));
+    }
}

Controllerの作成

make:authで生成されたものをコピーして利用。
直下のAuthフォルダ+Controller.php+HomeController.phpをコピーします。

app/Http/Controllers/Userディレクトリを作りペースト。

app/Http/Controllers
Controllers
├── Auth
│   ├── ForgotPasswordController.php
│   ├── LoginController.php
│   ├── RegisterController.php
│   ├── ResetPasswordController.php
│   └── VerificationController.php
├── Controller.php
├── HomeController.php
└── User
    ├── Auth
    │   ├── ForgotPasswordController.php
    │   ├── LoginController.php
    │   ├── RegisterController.php
    │   ├── ResetPasswordController.php
    │   └── VerificationController.php
    ├── Controller.php
    └── HomeController.php

こんな階層になってればOK

Controller/Usersの編集

HomeController.php

app/Http/Controllers/User/HomeController.php
<?php

+namespace App\Http\Controllers\User;

use Illuminate\Http\Request;
+use App\User;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('auth:user');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Contracts\Support\Renderable
     */
    public function index()
    {
+        return view('user.home');
    }
}

Controller.php

app/Http/Controllers/User/Controller.php
<?php

+namespace App\Http\Controllers\User;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

LoginController.php

app/Http/Controllers/User/Auth/LoginController.php
<?php

+namespace App\Http\Controllers\User\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
+use Illuminate\Http\Request;

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 = '/user/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:user')->except('logout');
    }

+    public function showLoginForm()
+    {
+        return view('user.auth.login');
+    }

+    public function guard()
+    {
+        return \Auth::guard('user');
+    }

+    public function logout(Request $request)
+    {
+        $this->guard('user')->logout();
+        return redirect('/');
+    }
}

RegisterController.php

app/Http/Controllers/User/Auth/RegisterController.php
<?php

+namespace App\Http\Controllers\User\Auth;

use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
+use Illuminate\Http\Request;


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 = '/user/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:user');
    }
+    public function showRegisterForm()
+    {
+        return view('user.auth.register');
+    }

    /**
     * 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:8', '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']),
        ]);
    }
+    protected function guard()
+    {
+        return \Auth::guard('user');
+    }
}

Viewsの編集

viewsはmake:authをしてるので、下記のような改装になっていると思います。

├── auth
│   ├── login.blade.php
│   ├── passwords
│   │   ├── email.blade.php
│   │   └── reset.blade.php
│   ├── register.blade.php
│   └── verify.blade.php
├── home.blade.php
├── layouts
│   └── app.blade.php
└── welcome.blade.php

Controllerの時と同じように、user専用のviewsを作成します。
以下のディレクトリを作成。

recources/views/layouts/user/app.blade.php
 →layouts/app.blade.phpをコピペ
resources/views/user
 →views直下のauthフォルダ+home.blade.phpをコピペ


下記のような階層になるかと思います。
├── auth
│   ├── login.blade.php
│   ├── passwords
│   │   ├── email.blade.php
│   │   └── reset.blade.php
│   ├── register.blade.php
│   └── verify.blade.php
├── home.blade.php
├── layouts
│   ├── app.blade.php
│   └── user
│       └── app.blade.php
├── user
│   ├── auth
│   │   ├── login.blade.php
│   │   ├── passwords
│   │   │   ├── email.blade.php
│   │   │   └── reset.blade.php
│   │   ├── register.blade.php
│   │   └── verify.blade.php
│   └── home.blade.php
└── welcome.blade.php

app.blade.php

resources/views/layouts/user/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <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="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" 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 -->
                        @guest
                            <li class="nav-item">
+                                <a class="nav-link" href="{{ route('user.login') }}">{{ __('Login') }}</a>
                            </li>
                            @if (Route::has('register'))
                                <li class="nav-item">
+                                    <a class="nav-link" href="{{ route('user.register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <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>
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
+                                    <a class="dropdown-item" href="{{ route('user.logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

+                                    <form id="logout-form" action="{{ route('user.logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

login.blade.php

resources/views/layouts/user/login.blade.php
+@extends('layouts.user.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
+                    <form method="POST" action="{{ route('user.login') }}">
                        @csrf

                        <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 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>

                                @if (Route::has('password.request'))
+                                    <a class="btn btn-link" href="{{ route('user.password.request') }}">
                                        {{ __('Forgot Your Password?') }}
                                    </a>
                                @endif
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

ルーティングの設定

/user以下で画面が表示されるように設定します。
ひとまず、最低限のページルートのみ設定。
他のページも利用するときはその都度同じように設定すればOK。

routes/web.php
<?php


+use Illuminate\Http\Request;

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

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

+// User
+Route::group(['namespace' => 'User','prefix'=>'user'],function(){
+    Route::get('/',function(){
+        return redirect()->to('user/home');
+    })->name('user');
+    // home
+    Route::get('home','HomeController@index')->name('user.home');

+    // login lgoout
+    Route::get('login','Auth\LoginController@showLoginForm')->name('user.login');
+    Route::post('login','Auth\LoginController@login')->name('user.login');
+    Route::post('logout','Auth\LoginController@logout')->name('user.logout');

+    // register
+    Route::get('register','Auth\RegisterController@showRegisterForm')->name('user.register');
+    Route::post('register','Auth\RegisterController@register')->name('user.register');

Admin認証

adminはuserの手順とほぼ同じです。

テーブル・モデルの作成

terminal
php artisan make:model Admin -m

下記のテーブルとモデルができたかと思います。
app/Admin.php
database/201x_xx_xx_xxxxxx_create_admins_table.php

テーブル編集

userテーブルと同じ内容にします。

create_admins_table.php
public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

Admin.php

これもUser.phpとほぼ同じで、Authenticatableを継承

Admin.php
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable 
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password','api_token'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

auth.php

config/auth.php
<?php

return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
        'user' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
+       'admin' => [
+           'driver' => 'session',
+           'provider' => 'admins'
+       ]
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
+       'admins' => [
+           'driver' => 'eloquent',
+           'model' => App\Admin::class,
+       ],
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
+       'admins' => [
+           'provider' => 'admins',
+           'table' => 'password_resets',
+           'expire' => 60,
+       ],
    ],
];

Handler.php

app/Exceptions/Handler.php
protected function unauthenticated($request, AuthenticationException $exception)
{
    if($request->expectsJson()){
        return response()->json(['error' => 'Unauthenticated.'],401);
    }

    if(in_array('user',$exception->guards())){
        return redirect()->guest('user/login');
    }

+   if(in_array('admin',$exception->guards())){
+       return redirect()->guest('admin/login');
+   }

    return redirect()->back()->withInput(Input::all());

}

Controller

さっき作った”app/Http/Controller/User”フォルダを同じ階層にコピペしてAdminにリネームします。

Controller・Adminの編集

HomeController.php

app/Http/Controllers/Admin/HomeController.php
<?php

+namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;
use App\User;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('auth:admin');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Contracts\Support\Renderable
     */
    public function index()
    {
+        return view('admin.home');
    }
}

Controller.php

app/Http/Controllers/Admin/Controller.php
<?php

+namespace App\Http\Controllers\Admin;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

LoginController.php

app/Http/Controllers/Admin/Auth/LoginController.php
<?php

+namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
+use Illuminate\Http\Request;

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 = '/admin/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:admin')->except('logout');
    }

    public function showLoginForm()
    {
+        return view('admin.auth.login');
    }

    public function guard()
    {
+        return \Auth::guard('admin');
    }

    public function logout(Request $request)
    {
+        $this->guard('admin')->logout();
        return redirect('/');
    }
}

RegisterController.php

app/Http/Controller/Admin/Auth/RegisterController.php
<?php

+namespace App\Http\Controllers\Admin\Auth;

use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
+use Illuminate\Http\Request;

+use App\Admin;

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 = '/admin/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:admin');
    }
    public function showRegisterForm()
    {
+        return view('admin.auth.register');
    }

    /**
     * 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:admins'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\Admin
     */
    protected function create(array $data)
    {
+        return Admin::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
    protected function guard()
    {
+        return \Auth::guard('admin');
    }
}

Views

Controllerと同様、userで作成したファイルをコピペして、adminにリネームします。

app.blade.php

layouts/admin/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <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=Nunito" 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 -->
                        @guest
                            <li class="nav-item">
+                               <a class="nav-link" href="{{ route('admin.login') }}">{{ __('Login') }}</a>
                            </li>
                            <li class="nav-item">
                                @if (Route::has('register'))
+                                   <a class="nav-link" href="{{ route('admin.register') }}">{{ __('Register') }}</a>
                                @endif
                            </li>
                        @else
                            <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>
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
+                                   <a class="dropdown-item" href="{{ route('admin.logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

+                                   <form id="logout-form" action="{{ route('admin.logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

login.blade.php

views/admin/login.blade.php
+@extends('layouts.admin.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
+                    <form method="POST" action="{{ route('admin.login') }}">
                        @csrf

                        <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 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>

                                @if (Route::has('password.request'))
+                                    <a class="btn btn-link" href="{{ route('admin.password.request') }}">
                                        {{ __('Forgot Your Password?') }}
                                    </a>
                                @endif
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

ルーティングの設定

userと同様。
userのルーティングの下に追加します。

php/routes/web.php
// Admin
Route::group(['namespace' => 'Admin','prefix'=>'admin'],function(){
    Route::get('/',function(){
        return redirect()->to('admin/home');
    })->name('admin');
    // home
    Route::get('home','HomeController@index')->name('admin.home');

    // login logout
    Route::get('login','Auth\LoginController@showLoginForm')->name('admin.login');
    Route::post('login','Auth\LoginController@login')->name('admin.login');
    Route::post('logout','Auth\LoginController@logout')->name('admin.logout');

    // register
    Route::get('register','Auth\RegisterController@showRegisterForm')->name('admin.register');
    Route::post('register','Auth\RegisterController@register')->name('admin.register');

動作確認

php artisan serve

User
http://127.0.0.1:8000/user/login
Admin
http://127.0.0.1:8000/admin/login

それぞれで、ログイン・ログアウトできるか確認します。
別々に動作していれば成功です。

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

laravel5.7 Multi Authログイン

laravelの新機能、MultiAuthログインの実装メモ。

環境

MacOS 10.14.3
VisualStudio
laravel 5.7

やりたいこと

Admin、Userページそれぞれでログイン、ログアウトさせる

準備

1. プロジェクトの作成

terminal
composer create-project laravel/laravel multiauth_test

2. DBの作成
3. .envを編集

.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=作ったDB名
DB_USERNAME=ユーザ名
DB_PASSWORD=パスワード

User認証

認証機能のインストール

terminal
php artisan make:auth

テーブル作成

terminal
php artisan migrate

usersはデフォルトでファイルが用意されているので、これだけでOK

auth.php

config/auth.php
'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],

+        'user' => [
+           'driver' => 'session',
+            'provider' => 'users',
+       ],
    ],

Handler.php

認証エラーだった時の処理をカスタマイズ。
オーバーライドで書き込みます。

app/Exceptions/Handler.php
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

+use Request;
+use Response;
+use Illuminate\Auth\AuthenticationException;

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 (in_array('user', $exception->guards())) {
+            return redirect()->guest('user/login');
+       }
+        return redirect()->guest(route('login'));
+    }
}

Controllerの作成

make:authで生成されたものをコピーして利用。
直下のAuthフォルダ+Controller.php+HomeController.phpをコピーします。

app/Http/Controllers/Userディレクトリを作りペースト。

app/Http/Controllers
Controllers
├── Auth
│   ├── ForgotPasswordController.php
│   ├── LoginController.php
│   ├── RegisterController.php
│   ├── ResetPasswordController.php
│   └── VerificationController.php
├── Controller.php
├── HomeController.php
└── User
    ├── Auth
    │   ├── ForgotPasswordController.php
    │   ├── LoginController.php
    │   ├── RegisterController.php
    │   ├── ResetPasswordController.php
    │   └── VerificationController.php
    ├── Controller.php
    └── HomeController.php

こんな階層になってればOK

Controller/Usersの編集

HomeController.php

app/Http/Controllers/User/HomeController.php
<?php

+namespace App\Http\Controllers\User;

use Illuminate\Http\Request;
+use App\User;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('auth:user');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Contracts\Support\Renderable
     */
    public function index()
    {
+        return view('user.home');
    }
}

Controller.php

app/Http/Controllers/User/Controller.php
<?php

+namespace App\Http\Controllers\User;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

LoginController.php

app/Http/Controllers/User/Auth/LoginController.php
<?php

+namespace App\Http\Controllers\User\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
+use Illuminate\Http\Request;

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 = '/user/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:user')->except('logout');
    }

+    public function showLoginForm()
+    {
+        return view('user.auth.login');
+    }

+    public function guard()
+    {
+        return \Auth::guard('user');
+    }

+    public function logout(Request $request)
+    {
+        $this->guard('user')->logout();
+        return redirect('/');
+    }
}

RegisterController.php

app/Http/Controllers/User/Auth/RegisterController.php
<?php

+namespace App\Http\Controllers\User\Auth;

use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
+use Illuminate\Http\Request;


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 = '/user/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:user');
    }
+    public function showRegisterForm()
+    {
+        return view('user.auth.register');
+    }

    /**
     * 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:8', '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']),
        ]);
    }
+    protected function guard()
+    {
+        return \Auth::guard('user');
+    }
}

Viewsの編集

viewsはmake:authをしてるので、下記のような改装になっていると思います。

├── auth
│   ├── login.blade.php
│   ├── passwords
│   │   ├── email.blade.php
│   │   └── reset.blade.php
│   ├── register.blade.php
│   └── verify.blade.php
├── home.blade.php
├── layouts
│   └── app.blade.php
└── welcome.blade.php

Controllerの時と同じように、user専用のviewsを作成します。
以下のディレクトリを作成。

recources/views/layouts/user/app.blade.php
 →layouts/app.blade.phpをコピペ
resources/views/user
 →views直下のauthフォルダ+home.blade.phpをコピペ


下記のような階層になるかと思います。
├── auth
│   ├── login.blade.php
│   ├── passwords
│   │   ├── email.blade.php
│   │   └── reset.blade.php
│   ├── register.blade.php
│   └── verify.blade.php
├── home.blade.php
├── layouts
│   ├── app.blade.php
│   └── user
│       └── app.blade.php
├── user
│   ├── auth
│   │   ├── login.blade.php
│   │   ├── passwords
│   │   │   ├── email.blade.php
│   │   │   └── reset.blade.php
│   │   ├── register.blade.php
│   │   └── verify.blade.php
│   └── home.blade.php
└── welcome.blade.php

app.blade.php

resources/views/layouts/user/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <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="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" 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 -->
                        @guest
                            <li class="nav-item">
+                                <a class="nav-link" href="{{ route('user.login') }}">{{ __('Login') }}</a>
                            </li>
                            @if (Route::has('register'))
                                <li class="nav-item">
+                                    <a class="nav-link" href="{{ route('user.register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <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>
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
+                                    <a class="dropdown-item" href="{{ route('user.logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

+                                    <form id="logout-form" action="{{ route('user.logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

login.blade.php

resources/views/layouts/user/login.blade.php
+@extends('layouts.user.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
+                    <form method="POST" action="{{ route('user.login') }}">
                        @csrf

                        <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 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>

                                @if (Route::has('password.request'))
+                                    <a class="btn btn-link" href="{{ route('user.password.request') }}">
                                        {{ __('Forgot Your Password?') }}
                                    </a>
                                @endif
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

ルーティングの設定

/user以下で画面が表示されるように設定します。

routes/web.php
<?php


+use Illuminate\Http\Request;

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

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

+// User
+Route::group(['namespace' => 'User','prefix'=>'user'],function(){
+    Route::get('/',function(){
+        return redirect()->to('user/home');
+    })->name('user');
+    // home
+    Route::get('home','HomeController@index')->name('user.home');

+    // login lgoout
+    Route::get('login','Auth\LoginController@showLoginForm')->name('user.login');
+    Route::post('login','Auth\LoginController@login')->name('user.login');
+    Route::post('logout','Auth\LoginController@logout')->name('user.logout');

+    // register
+    Route::get('register','Auth\RegisterController@showRegisterForm')->name('user.register');
+    Route::post('register','Auth\RegisterController@register')->name('user.register');

Admin認証

adminはuserの手順とほぼ同じです。

テーブル・モデルの作成

terminal
php artisan make:model Admin -m

下記のテーブルとモデルができたかと思います。
app/Admin.php
database/201x_xx_xx_xxxxxx_create_admins_table.php

テーブル編集

userテーブルと同じ内容にします。

create_admins_table.php
public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

Admin.php

これもUser.phpとほぼ同じで、Authenticatableを継承

Admin.php
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable 
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password','api_token'
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}

auth.php

config/auth.php
<?php

return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
        'user' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
+       'admin' => [
+           'driver' => 'session',
+           'provider' => 'admins'
+       ]
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
+       'admins' => [
+           'driver' => 'eloquent',
+           'model' => App\Admin::class,
+       ],
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
+       'admins' => [
+           'provider' => 'admins',
+           'table' => 'password_resets',
+           'expire' => 60,
+       ],
    ],
];

Handler.php

app/Exceptions/Handler.php
protected function unauthenticated($request, AuthenticationException $exception)
{
    if($request->expectsJson()){
        return response()->json(['error' => 'Unauthenticated.'],401);
    }

    if(in_array('user',$exception->guards())){
        return redirect()->guest('user/login');
    }

+   if(in_array('admin',$exception->guards())){
+       return redirect()->guest('admin/login');
+   }

    return redirect()->back()->withInput(Input::all());

}

Controller

さっき作った”app/Http/Controller/User”フォルダを同じ階層にコピペしてAdminにリネームします。

Controller・Adminの編集

HomeController.php

app/Http/Controllers/Admin/HomeController.php
<?php

+namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;
use App\User;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('auth:admin');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Contracts\Support\Renderable
     */
    public function index()
    {
+        return view('admin.home');
    }
}

Controller.php

app/Http/Controllers/Admin/Controller.php
<?php

+namespace App\Http\Controllers\Admin;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

LoginController.php

app/Http/Controllers/Admin/Auth/LoginController.php
<?php

+namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
+use Illuminate\Http\Request;

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 = '/admin/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:admin')->except('logout');
    }

    public function showLoginForm()
    {
+        return view('admin.auth.login');
    }

    public function guard()
    {
+        return \Auth::guard('admin');
    }

    public function logout(Request $request)
    {
+        $this->guard('admin')->logout();
        return redirect('/');
    }
}

RegisterController.php

app/Http/Controller/Admin/Auth/RegisterController.php
<?php

+namespace App\Http\Controllers\Admin\Auth;

use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
+use Illuminate\Http\Request;

+use App\Admin;

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 = '/admin/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
+        $this->middleware('guest:admin');
    }
    public function showRegisterForm()
    {
+        return view('admin.auth.register');
    }

    /**
     * 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:admins'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\Admin
     */
    protected function create(array $data)
    {
+        return Admin::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }
    protected function guard()
    {
+        return \Auth::guard('admin');
    }
}

Views

Controllerと同様、userで作成したファイルをコピペして、adminにリネームします。

app.blade.php

layouts/admin/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <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=Nunito" 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 -->
                        @guest
                            <li class="nav-item">
+                               <a class="nav-link" href="{{ route('admin.login') }}">{{ __('Login') }}</a>
                            </li>
                            <li class="nav-item">
                                @if (Route::has('register'))
+                                   <a class="nav-link" href="{{ route('admin.register') }}">{{ __('Register') }}</a>
                                @endif
                            </li>
                        @else
                            <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>
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>

                                <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
+                                   <a class="dropdown-item" href="{{ route('admin.logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

+                                   <form id="logout-form" action="{{ route('admin.logout') }}" method="POST" style="display: none;">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

login.blade.php

views/admin/login.blade.php
+@extends('layouts.admin.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
+                    <form method="POST" action="{{ route('admin.login') }}">
                        @csrf

                        <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 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>

                                @if (Route::has('password.request'))
+                                    <a class="btn btn-link" href="{{ route('admin.password.request') }}">
                                        {{ __('Forgot Your Password?') }}
                                    </a>
                                @endif
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

ルーティングの設定

userと同様。
userのルーティングの下に追加します。

php/routes/web.php
// Admin
Route::group(['namespace' => 'Admin','prefix'=>'admin'],function(){
    Route::get('/',function(){
        return redirect()->to('admin/home');
    })->name('admin');
    // home
    Route::get('home','HomeController@index')->name('admin.home');

    // login logout
    Route::get('login','Auth\LoginController@showLoginForm')->name('admin.login');
    Route::post('login','Auth\LoginController@login')->name('admin.login');
    Route::post('logout','Auth\LoginController@logout')->name('admin.logout');

    // register
    Route::get('register','Auth\RegisterController@showRegisterForm')->name('admin.register');
    Route::post('register','Auth\RegisterController@register')->name('admin.register');

動作確認

php artisan serve

User
http://127.0.0.1:8000/user/login
Admin
http://127.0.0.1:8000/admin/login

それぞれで、ログイン・ログアウトできるか確認します。
別々に動作していれば成功です。

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

Laravel インストール

まずは、Composerのインストール
1.windowsの場合
https://getcomposer.org/download/
image.png

2.Macの場合

次に、Laravelのインストール
コマンドプロンプトで下記を実行
※インストールまで少し時間がかかります。

composer global require "laravel/installer=~1.1"

最後に、環境変数passの設定
1.コントロールパネルを開き、『詳細設定』タブの「環境変数」ボタンを押す
image.png

2.『システム環境変数』のリスト中から「Path」を探し編集
image.png

以下を先頭に追加
image.png
※binの後に「;」を加える
※編集ウインドウがリストになっている場合は『新規』ボタンを押し、上記を追加する。

以上でインストールは終了です。
誤りなどあればコメントお願いします。

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

【PHP】count() メモ

PHPの型まわりのメモ

count()

型まわりの注意点まとめ。

$array1 = false;
$array2 = 'str';
$array3 = [
    'a',
    'b',
];
$array4 = [
    ['a'],
    ['b'],
];
$array5 = null;

echo count($array1); // 1
echo count($array2); // 1
echo count($array3); // 2
echo count($array4); // 2
echo count($array5); // 0
echo count((array)$array1); // 1
echo count((array)$array2); // 1

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

CakePHP3で一つのクエリ結果をネストループさせるときに注意すること

CakePHP3で実装する際に、find()した結果をforeachでネストループさせる時に、
想定外の挙動をしてハマりましたので、tipsとして記録します。

背景

Usersテーブルに入っているユーザそれぞれに対して、
そのユーザとその他のユーザの類似度を計算したかった。

そのため、Usersテーブルの中身を2重ループするような実装をした。

$users = $this->Users->find()->all();

echo "*START\n";
foreach ($users as $outerUser) {
    echo "OUTER: {$outerUser->id}\n";

    foreach ($users as $innerUser) {
        echo "INNER: {$innerUser->id}\n";
    }
}
echo "*END\n";

問題となる結果

なぜか外側のループが最後まで回らず1回目で終了した。

*START
OUTER: 1
INNER: 1
INNER: 2
INNER: 3
INNER: 4
INNER: 5
*END

解決策

toArray()をつかって配列にすることで問題は起きなくなった。
が、ポピュラーで割とやりがちな実装だと思うので要注意だと思いました。

$users = $this->Users->find()->toArray();

ソース

issueを上げて確認したところ、そういった仕様という認識で良い模様。
https://github.com/cakephp/cakephp/issues/13021

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

Laravelのマイグレーションではtimestamp型のchangeが出来ない

エラー内容

Laravelのマイグレーションでtimestamp型のカラムに変更を加えようとすると以下のようなエラーが出る。

Unknown column type "timestamp" requested. Any Doctrine type that you use has to be registered with \  
  Doctrine\DBAL\Types\Type::addType(). You can get a list of all the known types with \Doctrine\DBAL\Ty  
  pes\Type::getTypesMap(). If this error occurs during database introspection then you might have forgo  
  tten to register all database types for a Doctrine Type. Use AbstractPlatform#registerDoctrineTypeMap  
  ping() or have your custom types implement Type#getMappedDatabaseTypes(). If the type name is empty y  
  ou might have a problem with the cache or forgot some mapping information.

理由

Laravel内で利用しているDoctrineというライブラリが対応していないから。ちなみに対応の予定もなさそう。
https://github.com/doctrine/dbal/issues/2558

理由としてはtimestamp型はMySQL固有の型だから特定のDBサービス用の型はサポートしないよということ。

ちなみにLaravelのchangeで変更できるカラムの種類は以下に限られている

bigInteger
binary
boolean
date
dateTime
dateTimeTz
decimal
integer
json
longText
mediumText
smallInteger
string
text
time
unsignedBigInteger
unsignedInteger
unsignedSmallInteger

解決策

DBファサードを使って生のSQLを発行する
MySQLのバージョンは5.7.25です。

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        DB::statement('ALTER TABLE `テーブル名` MODIFY COLUMN `カラム名` TIMESTAMP NULL;');
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        DB::statement('ALTER TABLE `テーブル名` MODIFY COLUMN `カラム名` TIMESTAMP NOT NULL;');
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】ModelのFillableの値を取得する

getFillable()を使って取得できる

user.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    protected $fillable = [
        'lastName',
        'firstName',
        'email',
    ];
}
<?php

$user = new User();
dd($user->getFillable());

結果

array:4 [
  0 => "lastName"
  1 => "firstName"
  2 => "email"
]

参考

https://laravel.com/api/5.6/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.html#method_getFillable

https://github.com/laravel/framework/blob/5.6/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php#L35

GuardはgetGuarded()で取得できる

https://laravel.com/api/5.6/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.html#method_getGuarded

https://github.com/laravel/framework/blob/5.6/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php#L58

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

Laravel 5.7 パスワードリセットの日本語化

こちらはブログでも掲載しています。

前回、メールアドレスの確認と日本語化したのですが、パスワードリセットでも英文のメールが送られていたので、こちらも日本語化をしたいと思います。

Laravel 5.7 メールアドレスの確認を日本語化も含めて実装する

ユーザー認証は以下のコマンドで実装されているものとします。

php artisan make:auth

ビューの日本語化

まずは、パスワードリセットの画面を日本語化します。

./resources/views/auth
├── passwords
│   ├── email.blade.php
│   └── reset.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">{{ __('message.password_reset.title') }}</div>

                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif

                    <form method="POST" action="{{ route('password.email') }}">
                        @csrf

                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('message.user.field.email') }}</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 mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('message.button.send') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

翻訳ファイルも変更します。(./resources/lang/ja/message.php)

<?php
return [
    // ボタン
    "button" => [
        "send" => ' 送信 ',
    ],
    // ユーザ情報
    "user" => [
        "field" => [
            "email" => "メールアドレス",
        ]
    ],
    // パスワードリセット
    "password_reset" => [
        'title' => 'パスワードリセット'
    ],
];

送信メールの日本語化

まずは、パスワードリセットをしたときにどこでメールを送っているのかを確認します。

UserモデルがAuthenticatable(Illuminate\Foundation\Auth\User)を継承しています。

その中で、CanResetPasswordというtraitを呼び出しています。

class User extends Model implements
    AuthenticatableContract,
    AuthorizableContract,
    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail;
}

CanResetPasswordの中身を見ると、sendPasswordResetNotificationでメールを送っているので、こちらをカスタマイズしていきます。

trait CanResetPassword
{
    /**
     * Get the e-mail address where password reset links are sent.
     *
     * @return string
     */
    public function getEmailForPasswordReset()
    {
        return $this->email;
    }

    /**
     * Send the password reset notification.
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new ResetPasswordNotification($token));
    }
}

カスタムのNotificationを作成する

artisanを利用して、カスタムのNotificationを作成します。

php artisan make:notification RestPasswordCustom

./app/Notifications/にファイルが作成されるので、もともとパスワードリセットで使用されているRestPasswordNotification(Illuminate\Auth\Notifications\ResetPassword)を参考に編集をいたします。

変更箇所はtoMailの中で、英文をセットしている箇所を日本語に置き換えるだけです。

namespace App\Notifications;

use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Lang;

class RestPasswordCustom extends Notification
{
    /**
     * The password reset token.
     *
     * @var string
     */
    public $token;

    /**
     * The callback that should be used to build the mail message.
     *
     * @var \Closure|null
     */
    public static $toMailCallback;

    /**
     * Create a notification instance.
     *
     * @param  string  $token
     * @return void
     */
    public function __construct($token)
    {
        $this->token = $token;
    }

    /**
     * Get the notification's channels.
     *
     * @param  mixed  $notifiable
     * @return array|string
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Build the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        if (static::$toMailCallback) {
            return call_user_func(static::$toMailCallback, $notifiable, $this->token);
        }

        return (new MailMessage)
            ->subject(Lang::getFromJson('message.mail.password_reset.title'))
            ->line(Lang::getFromJson('message.mail.password_reset.line'))
            ->action(Lang::getFromJson('message.mail.password_reset.action'), url(config('app.url').route('password.reset', $this->token, false)))
            ->line(Lang::getFromJson('message.mail.password_reset.out_line1', ['count' => config('auth.passwords.users.expire')]))
            ->line(Lang::getFromJson('message.mail.password_reset.out_line2'));
    }

    /**
     * Set a callback that should be used when building the notification mail message.
     *
     * @param  \Closure  $callback
     * @return void
     */
    public static function toMailUsing($callback)
    {
        static::$toMailCallback = $callback;
    }
}

翻訳ファイルは

<?php

return [
    // メール
    "mail" => [
        // パスワードリセット
        "password_reset" => [
            "title" => "パスワードリセットのお知らせ",
            "line" => "パスワードリセットの受け付けました。",
            "action" => "パスワードリセット",
            "out_line1" => "こちらのパスワードリセットの有効期限は :count 分です。",
            "out_line2" => "こちらのメールに身に覚えがない場合は、無視をしてください。"
        ]
    ],
];

以上、パスワードリセットの日本語化は終わりです。

Laravelはtraitを多様していますので、何かあればその中身を調査するようにしています(ーー;

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

【Laravel】blade Laravel Collective で css クラスを動的に付加する

{{ Form::text('e_mail', null, ['class' => 'mail' . ($errors->has('e_mail') ? ' error':'')]) }}

上記のように書くことで、$errors->has('e_mail') が trueの時のみ cssのclass error を付加することができますね:relaxed:

Laravel Collective

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

PHP: 関数合成する関数を作る方法【愚直にforeach編】

前回はPHP: 関数合成する関数を作る方法 - Qiitaという投稿をした。そこで紹介した関数合成する関数の実装は次のコードだった。

$composeAll = function (callable $function, callable ...$functions): callable {
    return array_reduce(
        $functions,
        function (callable $f, callable $g): callable {
            return function (...$arguments) use ($f, $g) {
                return $g($f(...$arguments));
            };
        },
        $function
    );
};

この関数は、関数の配列をreduceしていってひとつの関数を作る設計になっているが、ものすごく大量の関数を合成することは想定されていない。

試してみると分かるが、2つの関数を合成するたびにメモリが増えていき、memory_limitに達するとFatal Errorが発生する。

$increment = function (int $value): int {
    return $value + 1;
};

// 1,048,576個の関数を合成する
$increments1048576 = array_fill(0, 1024 * 1024, $increment);

$add1048576 = $composeAll(...$increments1048576); // Fatal Error

実行結果:

Terminated due to signal: SEGMENTATION FAULT (11)

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in /.../Untitled 3.php on line 11

これは、関数合成1回ごとにつなぎの関数のためのメモリが確保されていくから仕方がないところ。イメージとしては、こんなかんじで変数を大量に定義するのと同じ:

$increments1_2 = function (...) { return $increment2($increment1(...)); };
$increments1_2_3 = function (...) { return $increment3($increments1_2(...)); };
$increments1_2_3_4 = function (...) { return $increment4($increments1_2_3(...)); };
$increments1_2_3_4_5 = function (...) { return $increment5($increments1_2_3_4(...)); };
$increments1_2_3_4_5_6 = function (...) { return $increment6($increments1_2_3_4_5(...)); };
...1048570個くらい略...
$increments1_2_3_4_5_6_snip_1048576 = function (...) { return $increment1048576($increments1_2_3_4_5_snip_1048575(...)); };

100万個もの関数を合成することはそうそうないので、実際は直面する可能性が低い軽微な問題ではある。

それでも性能上の限界があることは記憶に留めておきたい。そして、限界はメモリだったりと実行時の状況次第で限界点のしきい値が変わるということも。

プログラムの完成度として、できるだけ限界がないようにしておきたいときは、愚直にforeachする方法もある:

$increment = function (int $value): int {
    return $value + 1;
};

$composeAll = function (callable ...$functions): callable {
    return function ($argument) use ($functions) {
        foreach ($functions as $function) {
            $argument = $function($argument);
        }
        return $argument;
    };
};

$increments1048576 = array_fill(0, 1024 * 1024, $increment);

$add1048576 = $composeAll(...$increments1048576);

echo $add1048576(0); //=> 1048576
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでMySQLに接続できない場合の対応

MySQLに接続しようとしたらPDOExceptionが発生した。

PHP Fatal error:  Uncaught PDOException: could not find driver in hoge.php:1

ドライバが見つからないと言っているので、この場合はphp-mysqlを入れます。

sudo apt install php-mysql

おわり

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

PHPでDOMDocumentがないと言われた場合の対応

下記のようなエラーが出たら

PHP Fatal error:  Uncaught Error: Class 'DOMDocument' not found in /hoge.php:8

php-xmlをインストールする

sudo apt install php-xml

or

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

php-master-changes 2019-02-26

今日は以前に行ったテスト修正のマージし直し、zend_vm_gen.php のリファクタリングがあった!

2019-02-26

nikic: Fix tests bareword fallback with error suppression

beberlei: Make regex replacements in zend_vm_gen.php more obvious

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

nginxでlaravelを動かす際にいろいろ詰んだ話

環境

  • macOS 10.12.6
  • MacBook pro 2016
  • VirtualBox 5.2.18
  • CentOS 7.6-1810
  • PHP 7.0.33
  • Laravel Framework 5.5.45
  • nginx 1.14.2

前提

nginx, PHP, PHP-FPM(設定済), composer をインストール済み

今回の手順

1.laravelプロジェクトの新規作成
2.nginxの設定ファイルを新規作成
3.nginxの再起動 ←ちょっと詰まった
4.ブラウザ確認 ←結構詰まった

laravelプロジェクトの新規作成

プロジェクトを作りたい場所で、コマンド一発叩きます。
自分は、/home/username/www/の配下に作りました。

# cd ~/www/
# composer create-project --prefer-dist laravel/laravel projectname

nginxの設定ファイルを新規作成

nginxの設定ファイルは/etc/nginx/conf.d/配下に作ります。
デフォルトでは、default.confがいるので、こいつをコピーして作りましょう。

# cd /etc/nginx/conf.d/
# cp default.conf laravel.conf //設定ファイルの名前は好きなように(プロジェクト名にするのが無難かも)

設定ファイルは、デフォルトのままでは動きませんので、書き換えます。
自分の場合は、以下のような感じです。

laravel.conf
server {
    listen       5000; //ポート番号はお好みで
    server_name  localhost;

    root   /home/username/www/projectname/public/; //先ほどlaravelプロジェクトを作成した場所に設定します。publicに関しては以下で説明
    index  index.php index.html index.htm;

    #charset koi8-r;
    access_log  /var/log/nginx/projectname/access.log  main; //アクセスログの出力先を指定
    error_log   /var/log/nginx/projectname/error.log  warn;//エラーログの出力先を指定

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

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root/index.php; //ここの$document_rootは、ファイルの上の方で設定したrootが代入されています。

        fastcgi_max_temp_file_size 0;
        fastcgi_buffer_size 4K;
        fastcgi_buffers 64 4k;

        include        fastcgi_params;
    }

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

コメントにも記載したが、rootで指定したものが$document_rootに代入される。
デフォルトでは、location ~ \.php$ {}内にrootが宣言されているが、ここでrootを宣言すると、上のrootを上書きしてしまうみたいなので気を付けましょう。

このrootを、/home/username/www/projectname/public/としているが、laravelで設定したルーティングを使用するためには、projectname/public/index.phpを最初に見に行かせればいいので、このような設定になっている。

nginx用のログディレクトリ作成

今回は、ログの出力場所を/var/log/nginx/projectname/配下にしたので、ディレクトリをしっかり作成しておく。

# cd /var/log/nginx/
# mkdir projectname

これでログの置き場所はできた。

ポート開放

あとは、ファイアーウォールの壁に変更したポートの穴を開けてあげなければ外部からのアクセスができないので、ポート開放をしてあげます。

# firewall-cmd --add-port=開けたいポート番号/tcp --zone=public --permanent
# firewall-cmd --reload //反映

nginxの再起動

まずは、普通に再起動。

# systemctl restart nginx
Job for nginx.service failed because the control process exited with error code. See "systemctl status nginx.service" and "journalctl -xe" for details.

エラーや…と思い、エラーログを確認。

error.log
2019/02/24 13:24:50 [error] 4690#4690: *1 "/home/username/www/projectname/public/index.php" is forbidden (13: Permission denied), client: ***.***.***.***, server: localhost, request: "GET / HTTP/1.1", host: "host-name:5000"

パーミッション!?と思いsudoで実行してもダメ。
詰んだ…と思ったけど、以下の手順で解決できました!

SELinuxというクセもの

SELinuxという標準の制御機能があるらしく、そいつの制御に引っかかって弾かれていたみたいです…(そんなの今回のこのエラーだけじゃわからんよ…)
SELinuxの状態は以下で確認できます。

# getenforce
enforcing

・enforcing SELinux有効でありアクセス制御が有効となる
・permissive アクセス制御は無効だが警告メッセージを表示する
・disabled SELinux無効

こちらより

なので、今回はSELinuxをpermissiveにしなくてはなりませんね。
変更のやり方はこうです

# setenforce 0 //permissiveに変更
# setenforce 1 //enforcingに変更

これで、無事にnginxの再起動ができました!(めでたしめでたし。。。ではなかった)

ブラウザ確認

あとは、ブラウザ確認だ!ということで、先ほど設定して開放もしたポートにアクセス。
すると、laravelにアクセスできてはいるものの、laravelからエラーが返ってきました。
こんな感じ↓
スクリーンショット 2019-02-27 0.00.20.png
エラー内容は以下です。

The stream or file "/home/username/www/projectname/storage/logs/laravel.log" could not be opened: failed to open stream: Permission denied

こいつは、projectname/storage/logs/を開けないため起きているみたいです。
laravelに最初にアクセスするとき、このprojectname/storage/logs/配下にlaravel.logというアプリケーションログファイルを新規で作成しようとするのですが、書き込み権限がないためにエラーが出ているみたいですね。
※ちなみに、最初のアクセス時だけこのエラーが出るので、一回このエラーを解消してしまえば、後にパーミッションを元に戻してもエラーは起こりません。
チャチャっとパーミッションの変更。

# cd storage/
# chmod 777 logs //元は775でした

これで解決だ!!
と思いきやまた別のエラー。。。

こんな感じ↓
スクリーンショット 2019-02-27 0.07.16.png
エラー内容は以下

file_put_contents(/home/username/www/projectname/storage/framework/views/ddedc3f39a1683fbdb7b78fe94e93c581d280345.php): failed to open stream: Permission denied

同じく、projectname/storage/framework/views/配下がいじれないぞと言われていますね。
同じようにパーミッション変更!

# cd storage/framework/
# chmod 777 views //元は775でした

これで再度アクセス!
スクリーンショット 2019-02-27 0.13.16.png

やったぞ!!!アクセス成功!!!
お疲れました…。

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