87
97

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravelメモ

Last updated at Posted at 2018-01-15

随時更新

インストール後にやっておいた方が良い事

  • clone時にautoCRLFを無効にしていてもCRLFでcloneされてしまうので、以下の行を削除しておく
.gitattributes
* text=auto
  • 各自判断になりますが、入力値をtrimするMiddlewareと入力値の空文字をnullにするMiddlewareのコメントアウト
app/Http/Kernel.php
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,

忘れた頃にやってくる罠(namespaceの更新)

修正等で新たなクラスを追加してnamespaceを追加した場合はnamespaceの更新が必要(思わぬところでClass not foundが出ます。既にあるネームスペース配下にクラスを追加した場合は特に問題なく見つかるので忘れがちになります。)

composer dump-autoload

リダイレクト

return redirect('/home');

名前付きルート

return redirect()->route('home');

入力値を維持して$errors['test']を設定して元の画面に戻す。

return redirect()->back()->withErrors(['test' => 'test error'])->withInput();

入力値を戻す際にはbladeに以下のような設定が必要です。

oldは第二引数にデフォルト値を設定出来るそうです。(コメントより)

name="email" value="{{ old('email') }}"

name="email" value="{{ old('email', $user_object->email) }}"

name="email" value="{{ old('email', $email ?? '') }}"

フラッシュデータをリダイレクト先に渡す

return redirect()->route('update', ['id' => $id])->with('success', true);

画面に表示するにはbladeに以下のような設定します。

@if (session('success'))
    更新に成功しました。
@endif

コントローラ内から他のPOSTメソッドを呼び出す

$request= new Request();
$request->replace(['test' => 'test']);
return $this->methodName($request);

oldとerrorsをコントローラ内で設定する方法

レスポンス

JSONを返す

return response()->json(['message' => 'OK'], 200);

ダウンロードさせる

return response()->download($pathToFile);

ファイルを開いて表示

return response()->file($pathToFile);

エラーページに飛ばす

abortヘルパを使う

CSRF除外

app/Http/Middleware/VerifyCsrfToken
protected $except = [
 'api/*'
];

パラメータ取得(GET POST)

use Illuminate\Http\Request as HttpRequest;
use Request;

?query=test

Request::input('query');// test

// コントローラの引数を(HttpRequest $request)とする
$request->query;// test

?query=

Request::input('query');// null

// コントローラの引数を(HttpRequest $request)とする
$request->query;// null

<input type="text" name="test">

Request::input('test');

// コントローラの引数を(HttpRequest $request)とする
$request->test;

リクエストボディのjsonのプロパティも同じ用に取得できます。

メソッド取得

$request->method();// GET,POST...

セッション

use Session;

Session::get('key');
Session::put('key', $value);
Session::regenerate();
Session::forget('key');
Session::flush();

コントローラ内のメソッドでは使用できたが、コンストラクタ内ではSession::get('key')で何故か取得できなかった。

コントローラのコンストラクタ内でSessionを使うなら、Kernel.phpのmiddlewareに以下を追加てmiddlewareGroupsのwebに\Illuminate\Session\Middleware\StartSession::class,が設定されている場合は削除しろとのこと

middlewareGroupsのwebに\Illuminate\Session\Middleware\StartSession::class,が設定されている場合はコメントアウトする

これが設定されている場合は二重に呼び出してしまいエラーで戻した場合のerrorやinputが空になってしまった。
二重に呼び出しするとグローバルにセットしたセッションは保持されているがローカルなセッションは消えてしまう様子?

\Illuminate\Session\Middleware\StartSession::class,

コントローラのコンストラクタ

Auth::user()のようなコントローラのコンストラクタで直では取得できないものや上記のSessionのようにうまく使えないものもあるのでmiddlewareを使用すると良い

namespace App\Http\Controllers;

use Illuminate\Support\Facades\View;
// AuthやSessionを使う場合
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
public function __construct()
{
    $this->middleware(function ($request, $next) {

        $data = [];

        // viewに共通データを渡す
        View::share($data);

        return $next($request);
    });
}

バリデーション

routesでバリデーションを行う

Route::get('/test/{id}', 'TestController@index')->where(['id' => '[0-9]+'])->name('test');

(条件に合わない場合は404)

Validatorクラスを使う

クエリーストリングとセグメントをコントローラー内でチェックする

use Request;

~~~~~~~~~~~~

public function index(string $id)
    {
        $data = [];
 
        // クエリーストリングチェック
        $validator = Validator::make(Request::all(), [
            'query' => 'required'
        ]);
        $validator->after(function($validator) use ($id) {
            // セグメントチェック
            if ($id !== '1') {
                $validator->errors()->add('id', 'idが不正です。');
            }
        });

        // errorsに入れるとこのページがフォームだった場合にフォームの実行結果から戻した場合のエラーに上書きされるので分ける
        $data['direct_errors'] = $validator->errors();

        // バリデーション実行
        if ($validator->fails()) {
            return view('sample_form', $data);
        }
        return view('sample_form', $data);
    }

クエリーストリングを取得するにはRequest::query('page')(?page=の場合)
未設定の場合はnullを返します

フォームデータをコントローラー内でチェックする

use Illuminate\Http\Request;

~~~~~~~~~~~~

public function execute(Request $request)
    {
        $data = [];

        $validator = Validator::make($request->all(), [
            'name' => 'required',
            'tel' => 'required',
            'zip' => 'required',
            'textarea' => 'nullable|string|max:200',// textareaは何も入力しないとnullなのでnullも通す maxは文字数
            'password' => 'required|regex:/^(?=.*?[A-Za-z])(?=.*?[0-9])(?=.*?[ -\/:-@\[-`\{-\~])/',
        ],
        [
            'password.regex' => '半角英数記号を含めてください。',
        ]);

        // 特定の値を変換して登録する場合用に変換後の値を保持しておくオブジェクト
        $std_object = new \stdClass();
        $std_object->tel = $request->tel;
        $std_object->zip = $request->zip;

        $validator->after(function($validator) use ($std_object) {
            $tel = str_replace(['-', '‐'], '', $std_object->tel);// ハイフンを取り除く
            $std_object->tel = mb_convert_kana($tel, 'n');// 全角数字を半角に変換
            if (preg_match('/^0[1-9][0-9]{8,9}$/', $std_object->tel) !== 1)
            {
                $validator->errors()->add('tel', '電話番号の形式が正しくありません。');
            }
            $zip = str_replace(['-', '‐'], '', $std_object->zip);// ハイフンを取り除く
            $std_object->zip = mb_convert_kana($zip, 'n');// 全角数字を半角に変換
            if (preg_match('/^[0-9]{7}$/', $std_object->zip) !== 1)
            {
                $validator->errors()->add('zip', '郵便番号の形式が正しくありません。');
            }
        });

        // バリデーション実行
        if ($validator->fails()) {
            // 入力値を維持して$errorsを設定して元の画面に戻す。
            return redirect()->back()->withErrors($validator->errors())->withInput();
        }

        return view('sample_result', $data);
    }
blade
@if ($errors->any())
    @foreach ($errors->all() as $error)
        {{ $error }}
    @endforeach
@endif

Validatorをエラー表示だけに使う

Illuminate\Contracts\Validation\Factory
make(array $data, array $rules, array $messages = [], array $customAttributes = [])

$validator = Validator::make([], []);
$validator->errors()->add('error', 'XXに失敗しました。');// フォームのバリデーションに使っていないのでフィールド名は適当で良い
return redirect()->back()->withErrors($validator->errors())->withInput();

バリデーションのエラー結果にそのまま表示されるname属性を変更する

例)
name属性がgenderのものを性別と表示する。
name属性がareaのものを地域と表示する。

resources\lang\ja\validation.php
'attributes' => [
    'gender' => '性別',
    'area' => '地域',
],

配列に含まれる値のみを許容する設定

'status' => 'required|integer|in:' . implode(',', $statusList),

配列のバリデーション

例)
name属性がarea[]とdetail_area[]のチェックボックスがあるとする。
どちらか必須入力。
要素は値が入っていて255文字までとする。

<label><input type="checkbox" name="area[]" value="東北">東北</label>
<label><input type="checkbox" name="area[]" value="関東">関東</label>

<label><input type="checkbox" name="detail_area[]" value="東京">東京</label>
<label><input type="checkbox" name="detail_area[]" value="千葉">千葉</label>
$validator = Validator::make($request->all(), [
    'area' => 'required_without:detail_area|array',// detail_areaが設定されていない場合は必須 配列
    'area.*' => 'required|max:255',// 配列に値が入った場合に次の条件を満たす 空のvalueは許可しない 255文字まで
    'detail_area' => 'required_without:area|array',// areaが設定されていない場合は必須 配列
    'area.*' => 'required|max:255',// 配列に値が入った場合に次の条件を満たす 空のvalueは許可しない 255文字まで
]);

エラー結果表示設定

resources\lang\ja\validation.php
'attributes' => [
    'area' => '地域',
    'area.*' => '地域',
    'detail_area' => '地域',
    'detail_area.*' => '地域',
]

bladeではそれぞれのバリデーションエラーには

@if ($errors->has('area'))
    {{ $errors->first('area') }}
@endif

@if ($errors->has('area.*'))
    {{ $errors->first('area.*') }}
@endif

のようにアクセス可能

また以下のようにもアクセスできます

@if ($errors->has('area.0'))
    {{ $errors->first('area.0') }}
@endif

入力値を保持する場合(エラー時にページを戻した場合)

<label><input type="checkbox" name="area[]" value="東北"{{ old('area') ? (in_array('東北', old('area')) ? ' checked' : '') : '' }}>東北</label>
<label><input type="checkbox" name="area[]" value="関東"{{ old('area') ? (in_array('関東', old('area')) ? ' checked' : '') : '' }}>関東</label>

<label><input type="checkbox" name="detail_area[]" value="東京"{{ old('detail_area') ? (in_array('東京', old('detail_area')) ? ' checked' : '') : '' }}>東京</label>
<label><input type="checkbox" name="detail_area[]" value="千葉"{{ old('detail_area') ? (in_array('千葉', old('detail_area')) ? ' checked' : '') : '' }}>千葉</label>
多次元配列の場合

多次元配列のバリデーション

セグメントの取得

$request->segment($number);// string|null numberは1から

Illuminate/Http/Request
segment

フォームリクエストバリデーション(バリデーションをコントローラに書かない)

設定例

App\Http\Requests\コントローラ名\メソッド名Request
public function authorize()
{
    // 認証系の判定(falseを返すと401特に判定を行わない場合はtrueで良い)
    // Middlewareで判定した方がすっきりすることも(validation→middleware)
    return true;
}

public function rules()
{
    return [
        'date' => ['nullable', 'date_format:Y-m-d', new myCustomRule()],
    ];
}

// resoureces/langのvalidation.phpの設定を使わない、または上書きする場合
public function attributes()
{
    return [
        'date' => '日付',
    ];
}

// resoureces/langのvalidation.phpの設定を使わない、または上書きする場合
public function messages()
{
    return [
        'date.nullable' => 'エラー',
    ];
}

カスタムバリデーションルール

App\Rules\myCustomRule
public function passes($attribute, $value)
{
    // バリデーションの判定
    return true;
}

public function message()
{
    return ':attributeが失敗したときのメッセージ';
}

使い方

以下でvalidationが通ったものだけがこのコントローラにたどり着ける

コントローラ

use App\Http\Requests\コントローラ名\メソッド名Request

public function メソッド名(メソッド名Request $request)
{
}

アップロード

storeAs

$validator = Validator::make($request->all(), [
    'file' => 'required|file|max:10|mimes:jpeg|mimetypes:image/jpeg|dimensions:min_width=80,min_height=80,max_width=360,max_height=360',// maxはKBになる
]);

if ($validator->fails())
{
    // エラー
}

$filename = 'icon.jpeg';
// storage/app/imageに保存 絶対パスにしたところでstorage/app配下に保存される ディレクトリは勝手に作成してくれた
$path = $request->file('file')->storeAs('image', $filename);// string|false

if ($path === false)
{
    // エラー
}

バリデーションのmimetypesを設定する際の確認方法
$request->file_name->getMimeType();
または普通に
mime_content_type($_FILES['file_name']['tmp_name']);

※バリデーションのmimesはそのままの拡張子ではなく、mimetypesに対応する拡張子になる。

公開ディレクトリに置く

// storage/app/public/imageに保存
$request->file('file')->storeAs('public/image', $filename);

のようにして以下でシンボリックリンクを作成

ln -s storage/app/public public/storage

セグメントが変数で定義されたルートに変数を渡してアクセスする

routes\web.php
Route::get('/test/{id}', 'TestController@index')->name('test');
resources\views\XXXX.blade.php
{{ route('test', $id) }}
{{ route('test', ['id' => $id]) }}

引数が複数ある場合
{{ route('test', [$id, $id2]) }}
Keyは無くても可

(第二引数を設定しないとエラーになります。)

ログイン後にユーザー登録をさせたい

AuthのRegisterControllerのコンストラクター等に記載されている

app\Http\Controllers\Auth\RegisterController
$this->middleware('guest');

app\Http\Kernel.php
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,

RedirectIfAuthenticatedにはログインしていればリダイレクトするという処理が記載されている。

RegisterControllerのメソッドはログインしていればリダイレクトされてしまうのでログイン済みの場合は実行できない。

RegisterControllerにメソッドを追加したりした際にリダイレクトさせたくない場合(ログイン後(管理者等で)に一般ユーザーを登録させたい場合等)は例外を設定すれば良い。

app\Http\Controllers\Auth\RegisterController
$this->middleware('guest', ['except' => [
    'showGeneralRegistrationForm',
    'generalRegister',
]]);

ログイン済みの場合のログイン画面からのリダイレクト先を変更する

RedirectIfAuthenticatedhandleメソッド内で処理を変更する

app\Http\Middleware\RedirectIfAuthenticated
if (Auth::guard($guard)->check())
{
    if ($guard === 'web')
    {
        return redirect('/sample');
    }
}

ログインしていない場合のリダイレクト先の設定

app\Exceptions\Handler.php
// 認証されていない場合のリダイレクト先設定の上書き

/**
 * Convert an authentication exception into a response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Illuminate\Auth\AuthenticationException  $exception
 * @return \Illuminate\Http\Response
 */
protected function unauthenticated($request, AuthenticationException $exception)
{
    if ($request->expectsJson()) {
        return response()->json(['error' => 'Unauthenticated.'], 401);
    }

    if (in_array('backend_web', $exception->guards())) {
        return redirect()->guest(route('backend.login'));
    }

    return redirect()->guest(route('login'));
}

middlewareで複数のゲートを設定したい場合

adminとgeneralが通れるallゲートを作成する

app\Providers\AuthServiceProvider.php
Gate::define('all', function ($user) {
    return $user->role === \App\Model\Role::ADMIN || $user->role === \App\Model\Role::GENERAL;
});

// 管理者判定
Gate::define('admin', function ($user) {
    return $user->role === \App\Model\Role::ADMIN;
});

// 一般ユーザー判定
Gate::define('general', function ($user) {
    return $user->role === \App\Model\Role::GENERAL;
});

allでゲートを設定する

routes\web.php
Route::group(['middleware' => ['auth:web', 'can:all']], function () {
    Route::get('/home', 'HomeController@index')->name('home');
});

以下の二通りの方法で出し分けしたりする

  • コントローラでviewを出し分ける
use Illuminate\Support\Facades\Auth;

if (Auth::user()->can('admin') === true)
{
    return view('home');
}
if (Auth::user()->can('general') === true)
{
    return view('general_home');
}
  • 画面でadminとgeneralを出し分ける
resources\views\home.blade.php
@can('admin')
    ADMIN
@endcan

@can('general')
    GENERAL
@endcan

強制ログイン

use App\User;

$user = User::find(1);
$this->guard()->login($user);

自前でパスワードチェック

use Illuminate\Support\Facades\Hash

Hash::check($request->password, 'DBにパスワードとして登録されているハッシュ文字列');// return bool

ハッシュ値を作るのは

Hash::make($password)

Guard webでログイン判定

PHP

if (Auth::guard('web')->check())
{
    // ログイン済み
}

blade

@guest('web')
// 未認証
@endguest

@auth('web')
// 認証済
@endauth

bladeの中で変数を定義する

@php
    $variable = 0;
@endphp
<?php $variable = 0; ?>

現在のURLを取得

url()->current();

キャッシュ

use Illuminate\Support\Facades\Cache;

基本

Cache::get('key' . Auth::user()->id);// 存在しない場合はnull 第二引数にデフォルト値を設定可能
Cache::put('key' . Auth::user()->id, $value, $expireMinutes);// $expireMinutesは期限切れ時刻(DateTime)でも可
Cache::forget('key');

キー存在チェック

Cache::has('key');// bool

存在する場合はそのまま取得し、存在しない場合はクロージャの戻り値を登録しその値を取得する

Cache::remember('key' . Auth::user()->id, $expireMinutes, function () {
    return 'test';
});

取得時して削除

Cache::pull('key');

DB(クエリービルダ)

Builder

use Illuminate\Support\Facades\DB;

INSERT

try
{
    $result_flag = DB::table($table_name)
        ->insert($data);
}
catch (\Throwable $error)
{
    return false;
}

return $result_flag;

または

// 失敗時は$insert_idはnull
$insert_id = DB::table($table_name)->insertGetId($data);

バルクインサート

$result_flag = DB::table($table_name)->insert([$data, $data, $data]);
  • 失敗時は何も登録されません。
  • insertGetIdはバルクインサートに使えません。
  • バルクインサートで入れたIDが欲しい場合は実行前の数値(for updateして取得)と登録件数から計算する必要があります。

WHERE系

whereNull / whereNotNull
whereIn / whereNotIn
whereBetween / whereNotBetween
など

LIKE

ワイルドカードの%(0回以上の任意の文字)と_(任意の1文字)はエスケープされないので入力値の%_という文字列は検索できないのでエスケープする必要があります

$input = str_replace(['%', '_'], ['\%', '\_'], $input);
->where('name', 'like', '%' . $input . '%');

UPDATE

try
{
    $count = DB::table($table_name)
        ->where('id', $id)
        ->update($data);
}
catch (\Exception $exception)
{
    return false;
}

return $count === 1;

1レコード取得

\stdClassまたはnullを返す。

$object = DB::table($table_name)
        ->where('id', $id)
        ->first();

レコードからpluckで取得

値の入った配列を返す。
(pluckはIlluminate\Support\Collectionを返す)

$name_list = DB::table($table_name)->pluck('name')->toArray();

リストで取得

\stdClassの配列を返す。

$object_list = DB::table($table_name)
        ->where('delete_flag', 0)
        ->orderBy('id', 'desc')
        ->offset($offset)
        ->limit($limit)
        ->get()->all();

A and (B or C)

$builder = DB::table($table_name)
    ->where('delete_flag', 0);// A

if ( ! empty($search))
{
    $search = str_replace(['%', '_'], ['\%', '\_'], $search);
    $builder->where(function($query) use ($search){
        $query->where('name', 'like', '%' . $search . '%')// B
            ->orWhere('email', 'like', '%' . $search . '%');// C
    });
}

SQL実行

$preparedSql = 'INSERT INTO `table` (`name`, `value`) VALUES (?, ?), (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)';
$valueList = ['名前1', 1, '名前2', 2];

$resultFlag = DB::statement(
    $preparedSql,
    $valueList
);

トランザクション

DB::beginTransaction();

DB::commit();

DB::rollback();

FOR UPDATE

$object_list = DB::table($table_name)
        ->where('id', 1)
        ->lockForUpdate()
        ->get()
        ->all();

ログ

ログファイルをファイル名を指定して作る

Log::useFiles(storage_path().'/logs/test_'.$test_id.'.log');

useFiles

暗号化

暗号化

その他

ログファイルの解放

87
97
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
87
97

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?