LoginSignup
103
110

More than 5 years have passed since last update.

Laravelまめちしき

Last updated at Posted at 2017-09-26

はじめに

Laravel5.1やってて自分なりに思ったこと、気づいたことなんかをまとめてみました。
随時追加していきます。
5.5触ってみたいけどそんな余裕ない。

Model関係

\App\Model->get()で取得カラムを指定する

php
$columns = [
    'id',
    'name',
    'email',
];
\App\User::get($columns);

Modelを変数として扱いたい

変数に入れたモデル名をインスタンス化したいなんてときに。

php
$model = '\App\User';
if(!class_exists($model)) {
    throw new \Exception("モデル({$model})が見つかりませんでした。");
}
$user    = new $model;

whereをパラメーター化したい

php
$where = [
    'age'    => 20,
    'gender' => 1,
];
$user = \App\User::where($where)->get();

複雑なwhere文を生成したい

sql
/* Case 1 */
SELECT * FROM articles
    WHERE (`user_id` = 1 AND `votes` > 200)
    OR    (`user_id` = 2 AND `votes` > 200)
;

/* Case 2 */
SELECT * FROM articles
    WHERE (`votes` > 200)
    OR    (`user_id` = 1 OR `user_id` = 2)
;
php
// Case 1
$user_id = [1, 2];
$votes   = 200;
$articles = \App\Article::where(function($query) use ($user_id, $votes) {
    foreach($users as $user_id) {
        $query->where(['user_id' => $user_id, 'votes' => $votes]);
    }
});

// Case 2
$user_id = [1, 2];
$votes   = 200;
$articles = \App\Article::where('votes',$votes)
    ->where(function($query) use ($user_id, $votes) {
        foreach($users as $user_id) {
            $query->orWhere(['user_id' => $user_id]);
        }
    }
);

複合キーでJOINしたい

php
\App\User::join('employees', function (){
        $join->on('users.id', '=', 'employees.user_id')
        $join->on('users.staff_number', '=', 'employees.staff_number')
    })
    ->take(10)
    ->get()
;

モデルケースが適切かどうかは置いておいて。
これでusers.idusers.staff_numberの2つをキーにしてリレーションができました。

クエリスコープを利用する

クエリスコープを利用してデータの絞り込みが行えます。
内部的にはwhere条件書いてるだけですが、コードが簡素化できる・分かりやすくなるなどの利点があるみたいです。

App/User
public function scopeAge($query, $age) {
     return $query->where('age', '=', $age);
}
public function scopeGender($query, $gender) {
     return $query->where('gender', '=', $gender);
}
php
$user = \App\User::age(20)->gender(1)->get();

Eloquentで取得したモデルにデータがあるか判定したい

// $user = \App\User::where('gender', '=', 'male');
// あくまでもコレクションが存在しているかどうかに対しての判定
$user = \App\User::where('gender', '=', 'male')->get();
if ($user->isEmpty()) {
    // データが存在しない
} else {
    // データが存在する
}

SQL文のデバッグを行いたい!

\DB::enableQueryLog();
$sql = \App\User::join('sample_db.user_posts','users.id','=','user_posts.user_id')
    ->where('users.age', '>=', '20')
    ->orderBy('users.gender', 'asc')
    ->toSql()
;
dd($sql);
// dd(\DB::getQueryLog());

...基本はLaravel DebugBarから取れるのですが。
JobやらControllerやらの中でデバッグしたいときとかに。

途中まで条件が同じEloquentモデルを複製したい

$model1 = \App\User::where('age', '>', 20);
$model2 = clone $model1;

$res1 = $model1->where('gender', 'male')->toSql();
$res2 = $model2->where('name', 'like', '%tanaka%')->toSql();
/*
$res1:
  SELECT * FROM users WHERE age > 20 AND gender = 'male';
$res2:
  SELECT * FROM users WHERE age > 20 AND name LIKE '%tanaka%';  
*/

clone 使いましょう。

Validation関係

バリデーション時に別DBに存在するか調べたい

コネクションを指定してバリデーションチェックを行うことができます。
なお、ルール内の文字列にスペースを挟むとハマるので注意。

php
// ./config/database.php にmysql_sampleコネクションがあるものと仮定
$inputs   = \Input::all();
$rules    = ['id' => 'exists:mysql_sample.samples,id'];
// これはダメ['id' => 'exists:mysql_sample.samples, id'];
$validate = \Validation::make($inputs, $rules);

バリデーション正規表現で複数パターンを記述したい

php
$rules = [
    'id'     => 'required',
    'sample' => ['regex:/^\d\d:\d\d:\d\d|null/'], // パイプを使用する場合、配列で囲む
];

POSTで配列に対してバリデーションしたい

残念ながら*記法でまとめてバリデーションできるのはLaravel5.2からみたいです。
当然、attributesメソッドで要素名を日本語化する場合も、一個一個指定してあげる必要があります。

POST
$post1 = [
    'checkbox' => [
        0 => 'on',
        1 => 'on',
        2 => 'off',
    ],
];

$post2 = [
    0 => ['checkbox' => 'on',],
    1 => ['checkbox' => 'on',],
    2 => ['checkbox' => 'on',],
];
rules
$rule1 = [
    'checkbox.0' => 'required|boolean',
    'checkbox.1' => 'required|boolean',
    'checkbox.2' => 'required|boolean',
];
$rule2 = [
    '0.checkbox' => 'required|boolean',
    '1.checkbox' => 'required|boolean',
    '2.checkbox' => 'required|boolean',
];
validate
$validator1 = \Validator::make($post1, $rule1);
$validator2 = \Validator::make($post2, $rule2);

バリデーションルールを可変にしたい

例えばPOSTの値が0以外ならバリデーションしたい!とかそういう場合に。

Request
class RosterUser extends Request
{
    public function authorize() {
        return true;
    }

    public function rules() {
        $input = \Request::only(['value']);
        // rulesに分岐した条件を詰め込んでいき、最後にreturnすればおk
        $rules = [
            'id' => 'required|integer',
        ];
//        if (isset($input['value']))
//        {
//            $rules['value'] = 'required|integer';
//        }
        // これでもいけるっぽい
        $rules['value'] = (isset($input['value'])) ? 'required|integer' : '';
        return $rules;
    }
}

バリデーション時のエラー

SQLSTATE[42S22]: Column not found: 1054 Unknown column '(column_name)' in 'where clause' (SQL: select count(*) as aggregate from (table_name) where (column_name) = 1)

Request
$rules = [
    'value' => 'required|exists:connection_name.table_name, column_name',
    // table_nameとcolumn_nameの間にスペースを入れるとエラーになる
];

column_nameの前にスペースが入っていたことが原因。
分かればなんてことないけど2回くらいハマったので。

validationの名前を変えたい!ついでにメッセージも!

Request
class RosterUser extends Request
{
    public function authorize() {
        return true;
    }

    public function rules() {
        $rules = ['name' => 'required|string',];
        return $rules;
    }

    public function attributes() {
        // これで要素の英語が日本語に変換されるよ!やったね!
        return ['name' => 'ユーザー名'];
    }

    public function messages() {
        // これでメッセージも自由自在だよ!
        return [
            'name:required' => ':attribute が入力されていませんよ',
            'name:string'   => ':attribute は文字列で入力してくださいね',
        ];
    }

}

配列に対して

Request
public function attributes() {
    // Laravel 5.2からはname.*でイケるらしい
    return [
        'name.0' => 'ユーザー名',
        'name.1' => 'ユーザー名',
        'name.2' => 'ユーザー名',
    ];
}

Route関係

ルーティングあれこれ

middlewareを指定する

app/Http/routes.php
Route::group(['middleware' => 'test'], function() {
    Route::get('/test/show', function (){ ... });
    Route::get('/test/edit', function (){ ... });
    // グループ内のルートに対して全てtestミドルウェアが適用される
});

もしくは各コントローラーをインスタンス化する際にミドルウェアを通してもおk

Controller
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;

class TestController extends Controller
{
    public function __construct() {
        \App\Http\Middleware;
    }
app\Http\Middleware\TestMiddleware.php
namespace App\Http\Middleware;

use Closure;

class TestMiddleware
{
    public function handle($request, Closure $next)
    {
        echo "TEST-Middleware.";
        return $next($request);
    }
}
app\Http\Kernel.php
namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    protected $middleware = [
        ...
    ];

    protected $routeMiddleware = [
        'auth'       => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'guest'      => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'test'       => \App\Http\Middleware\TestMiddleware::class,
    ];
}

prefixを指定する

app/Http/routes.php
Route::group(['middleware' => 'test'], function() {
    Route::group(['prefix' => 'admin'], function() {
        Route::get('/test/show', function (){ ... }); // route = /admin/test/show
        Route::get('/test/edit', function (){ ... }); // route = /admin/test/edit
        // 先頭にprefixの文字が挿入される
    });
});

Routeに名前をつける

Route
Route::group(['middleware' => 'test', 'prefix' => 'admin', 'as' => 'admin::'], function() {
    Route::get('/test/show', ['as' => 'show', function() { ... }]);
    // route = /admin/test/show, name = admin::show
    Route::get('/test/edit', ['as' => 'edit', function() { ... }]);
    // route = /admin/test/edit, name = admin::edit
});
Controller
    return \Redirect::route('admin::show');

これで各コントローラーからはルート名を呼び出すだけで良くなるため、例えば仕様変更があってルートが変わったとしてもコントローラー側の修正が不要になります。
もちろん、ネストすることも可能。

Route
Route::group(['as' => 'admin::', 'prefix' => '/admin'], function() {
    Route::group(['as' => 'user::', 'prefix' => '/user'], function() {
        Route::get('/edit/{id}', ['as' => 'edit', 'uses' => 'AdminUserController@edit']);
    });
});
View
<a href="{{route(admin::user::edit, ['id' => '$id'])}}">Edit User</a>

Routeに対する必須パラメータ、任意パラメータ

app\Http\Routes.php
// パラメータを必須化したい場合
Route::get('/users/{id}', ...);

// パラメータを任意にしたい場合
Route::get('/users/{id?}', ...);

routes.phpを複数ファイルに分ける

app/Http/Providers/RouteServiceProvider.phpに追記していく。

app/Http/Providers/RouteServiceProvider.php
    /**
     * アプリケーションのルートを定義
     *
     * @param  \Illuminate\Routing\Router  $router
     * @return void
     */
    public function map(Router $router) {
        $router->group(['namespace' => $this->namespace], function ($router) {
            require app_path('Http/routes.php');
            require app_path('Http/new_routes.php');
        });
    }

ページネーションするときにpage以外のパラメータを引き継ぐ

resources/views/sample.blade.php
{!! $groups->appends(['group_id' => $group_id])->render() !!}

その他

migration実行時に存在しないconnect名が表示される

コンソール上で次のコマンドを実行。

console
$ composer dump-autoload
$ php artisan cache:clear

どうしてもダメっぽいなら、bootstrap/cache/config.phpを削除する。

新しくdatabaseを追加したらエラーが出る

[InvalidArgumentException] Database [(connection_name)] not configured.

console
$ php artisan config:clear

Transaction処理を行いたい

例外がスローされた場合などに全ての処理を巻き戻したい場合に。
なお、Transactionを貼ってロールバックしたとしてもAutoIncrementsの値は巻き戻らない(データベースの整合性)点に留意。

通常

php
\DB::transaction(function () {
    \DB::table('sample')->update(['column' => 1]);
});
// 多分変数使いたいときはuse使えばいける
$column = 1;
\DB::transaction(function () use ($column) {
    \DB::table('sample')->update(['column' => $column]);
});

手動

php
try {
    \DB::beginTransaction();
    $db = \DB::table('sample');
    $db->insert(['column' => 12345]);
    \DB::commit();
} catch($e) {
    \DB::rollback();
}

connection指定

php
try {
    \DB::connection('my_connect')->beginTransaction();

    $db = \DB::connection('my_connect')->table('sample');
    $db->insert(['column'=>12345]);

    \DB::connection('my_connect')->commit();
} catch($e) {
    \DB::connection('my_connect')->rollback();
}

Inputから特定の要素を取得したい・特定の要素を除外したい

php
// 特定の要素を取得する場合
$inputs = \Input::only(['id', 'name',]);

// 特定の要素を除外する場合
$inputs = \Input::except(['_token',]);

編集履歴

2018-03-02

  • 軽微な修正

2018-02-22

  • ページネーションするときにpage以外のパラメータを引き継ぐ を追加

2017-12-13

  • 複数キーでJOINしたい を追加

2017-10-05

2017-10-03

2017-09-28

2017-09-27

2017-09-26

2019-02-19

103
110
0

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