はじめに
Laravel5.1やってて自分なりに思ったこと、気づいたことなんかをまとめてみました。
随時追加していきます。
5.5触ってみたいけどそんな余裕ない。
Model関係
\App\Model->get()で取得カラムを指定する
$columns = [
'id',
'name',
'email',
];
\App\User::get($columns);
Modelを変数として扱いたい
変数に入れたモデル名をインスタンス化したいなんてときに。
$model = '\App\User';
if(!class_exists($model)) {
throw new \Exception("モデル({$model})が見つかりませんでした。");
}
$user = new $model;
whereをパラメーター化したい
$where = [
'age' => 20,
'gender' => 1,
];
$user = \App\User::where($where)->get();
複雑なwhere文を生成したい
/* 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)
;
// 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したい
\App\User::join('employees', function (){
$join->on('users.id', '=', 'employees.user_id')
$join->on('users.staff_number', '=', 'employees.staff_number')
})
->take(10)
->get()
;
モデルケースが適切かどうかは置いておいて。
これでusers.id
とusers.staff_number
の2つをキーにしてリレーションができました。
クエリスコープを利用する
クエリスコープを利用してデータの絞り込みが行えます。
内部的にはwhere条件書いてるだけですが、コードが簡素化できる・分かりやすくなるなどの利点があるみたいです。
public function scopeAge($query, $age) {
return $query->where('age', '=', $age);
}
public function scopeGender($query, $gender) {
return $query->where('gender', '=', $gender);
}
$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に存在するか調べたい
コネクションを指定してバリデーションチェックを行うことができます。
なお、ルール内の文字列にスペースを挟むとハマるので注意。
// ./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);
バリデーション正規表現で複数パターンを記述したい
$rules = [
'id' => 'required',
'sample' => ['regex:/^\d\d:\d\d:\d\d|null/'], // パイプを使用する場合、配列で囲む
];
POSTで配列に対してバリデーションしたい
残念ながら*記法でまとめてバリデーションできるのはLaravel5.2からみたいです。
当然、attributesメソッドで要素名を日本語化する場合も、一個一個指定してあげる必要があります。
$post1 = [
'checkbox' => [
0 => 'on',
1 => 'on',
2 => 'off',
],
];
$post2 = [
0 => ['checkbox' => 'on',],
1 => ['checkbox' => 'on',],
2 => ['checkbox' => 'on',],
];
$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',
];
$validator1 = \Validator::make($post1, $rule1);
$validator2 = \Validator::make($post2, $rule2);
バリデーションルールを可変にしたい
例えばPOSTの値が0以外ならバリデーションしたい!とかそういう場合に。
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)
$rules = [
'value' => 'required|exists:connection_name.table_name, column_name',
// table_nameとcolumn_nameの間にスペースを入れるとエラーになる
];
column_name
の前にスペースが入っていたことが原因。
分かればなんてことないけど2回くらいハマったので。
validationの名前を変えたい!ついでにメッセージも!
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 は文字列で入力してくださいね',
];
}
}
配列に対して
public function attributes() {
// Laravel 5.2からはname.*でイケるらしい
return [
'name.0' => 'ユーザー名',
'name.1' => 'ユーザー名',
'name.2' => 'ユーザー名',
];
}
Route関係
ルーティングあれこれ
middlewareを指定する
Route::group(['middleware' => 'test'], function() {
Route::get('/test/show', function (){ ... });
Route::get('/test/edit', function (){ ... });
// グループ内のルートに対して全てtestミドルウェアが適用される
});
もしくは各コントローラーをインスタンス化する際にミドルウェアを通してもおk
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;
}
namespace App\Http\Middleware;
use Closure;
class TestMiddleware
{
public function handle($request, Closure $next)
{
echo "TEST-Middleware.";
return $next($request);
}
}
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を指定する
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::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
});
return \Redirect::route('admin::show');
これで各コントローラーからはルート名を呼び出すだけで良くなるため、例えば仕様変更があってルートが変わったとしてもコントローラー側の修正が不要になります。
もちろん、ネストすることも可能。
Route::group(['as' => 'admin::', 'prefix' => '/admin'], function() {
Route::group(['as' => 'user::', 'prefix' => '/user'], function() {
Route::get('/edit/{id}', ['as' => 'edit', 'uses' => 'AdminUserController@edit']);
});
});
<a href="{{route(admin::user::edit, ['id' => '$id'])}}">Edit User</a>
Routeに対する必須パラメータ、任意パラメータ
// パラメータを必須化したい場合
Route::get('/users/{id}', ...);
// パラメータを任意にしたい場合
Route::get('/users/{id?}', ...);
routes.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以外のパラメータを引き継ぐ
{!! $groups->appends(['group_id' => $group_id])->render() !!}
その他
migration実行時に存在しないconnect名が表示される
コンソール上で次のコマンドを実行。
$ composer dump-autoload
$ php artisan cache:clear
どうしてもダメっぽいなら、bootstrap/cache/config.php
を削除する。
新しくdatabaseを追加したらエラーが出る
[InvalidArgumentException] Database [(connection_name)] not configured.
$ php artisan config:clear
Transaction処理を行いたい
例外がスローされた場合などに全ての処理を巻き戻したい場合に。
なお、Transactionを貼ってロールバックしたとしてもAutoIncrementsの値は巻き戻らない(データベースの整合性)点に留意。
通常
\DB::transaction(function () {
\DB::table('sample')->update(['column' => 1]);
});
// 多分変数使いたいときはuse使えばいける
$column = 1;
\DB::transaction(function () use ($column) {
\DB::table('sample')->update(['column' => $column]);
});
手動
try {
\DB::beginTransaction();
$db = \DB::table('sample');
$db->insert(['column' => 12345]);
\DB::commit();
} catch($e) {
\DB::rollback();
}
connection指定
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から特定の要素を取得したい・特定の要素を除外したい
// 特定の要素を取得する場合
$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
- Transaction処理を行いたい を修正(ご指摘いただいた方、ありがとうございました!)
- SQL文のデバッグを行いたい! を追加