Laravelまめちしき

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

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

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


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

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

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

```php:php
$where = [
    'age'    => 20,
    'gender' => 1,
];
$user = \App\User::where($where)->get();
```
##複雑なwhere文を生成したい

```sql: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: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: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.id``と``users.staff_number``の２つをキーにしてリレーションができました。

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

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

```php:php
$user = \App\User::age(20)->gender(1)->get();
```

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

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

```php
\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モデルを複製したい


```php
$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に存在するか調べたい
コネクションを指定してバリデーションチェックを行うことができます。
なお、ルール内の文字列に[スペースを挟むとハマる](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E6%99%82%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC)ので注意。

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


##POSTで配列に対してバリデーションしたい
残念ながら*記法でまとめてバリデーションできるのは[Laravel5.2から](http://www.larajapan.com/2016/10/23/%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3-8-%E9%85%8D%E5%88%97%E3%82%92%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%99%E3%82%8B/)みたいです。
当然、[attributesメソッド](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#validate%E3%81%AE%E5%90%8D%E5%89%8D%E3%82%92%E5%A4%89%E3%81%88%E3%81%9F%E3%81%84)で要素名を日本語化する場合も、一個一個指定してあげる必要があります。

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

$post2 = [
    0 => ['checkbox' => 'on',],
    1 => ['checkbox' => 'on',],
    2 => ['checkbox' => 'on',],
];
```

```php: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',
];
```

```php:validate
$validator1 = \Validator::make($post1, $rule1);
$validator2 = \Validator::make($post2, $rule2);
```

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

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

    public function rules() {
        $input = \Request::only(['value']);
        // rulesに分岐した条件を詰め込んでいき、最後にreturnすればおｋ
        $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)

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

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

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

```php: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 は文字列で入力してくださいね',
        ];
    }

}
```

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

```







#Route関係
##ルーティングあれこれ

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

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

```php: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;
    }
```

```php: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);
    }
}
```

```php: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を指定する

```php: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に名前をつける

```php: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
});
```

```php:Controller
    return \Redirect::route('admin::show');
```

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

```php:Route
Route::group(['as' => 'admin::', 'prefix' => '/admin'], function() {
    Route::group(['as' => 'user::', 'prefix' => '/user'], function() {
        Route::get('/edit/{id}', ['as' => 'edit', 'uses' => 'AdminUserController@edit']);
    });
});
```

```php:View
<a href="{{route(admin::user::edit, ['id' => '$id'])}}">Edit User</a>
```

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

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

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

##routes.phpを複数ファイルに分ける
``app/Http/Providers/RouteServiceProvider.php``に追記していく。

```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以外のパラメータを引き継ぐ

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









#その他
##migration実行時に存在しないconnect名が表示される
コンソール上で次のコマンドを実行。

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

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

##新しくdatabaseを追加したらエラーが出る
>[InvalidArgumentException] Database [(connection_name)] not configured.  

```bash:console
$ php artisan config:clear
```

##Transaction処理を行いたい
例外がスローされた場合などに全ての処理を巻き戻したい場合に。
なお、Transactionを貼ってロールバックしたとしても[AutoIncrementsの値は巻き戻らない](http://cs.hatenablog.jp/entry/2014/03/14/103358)（データベースの整合性）点に留意。

###通常
```php: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:php
try {
    \DB::beginTransaction();
    $db = \DB::table('sample');
    $db->insert(['column' => 12345]);
    \DB::commit();
} catch($e) {
    \DB::rollback();
}
```

###connection指定

```php: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:php
// 特定の要素を取得する場合
$inputs = \Input::only(['id', 'name',]);

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





# 編集履歴
## 2018-03-02
+ 軽微な修正

## 2018-02-22
+ **ページネーションするときにpage以外のパラメータを引き継ぐ** を追加

## 2017-12-13
+ **複数キーでJOINしたい** を追加

## 2017-10-05
+ **[routes.phpを複数ファイルに分ける](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#routesphp%E3%82%92%E8%A4%87%E6%95%B0%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E5%88%86%E3%81%91%E3%82%8B)** を追加

## 2017-10-03
+ **[新しくdatabaseを追加したらエラーが出る](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#%E6%96%B0%E3%81%97%E3%81%8Fdatabase%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%97%E3%81%9F%E3%82%89%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E5%87%BA%E3%82%8B)** を追加

## 2017-09-28
+ **[Modelを変数として扱いたい](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#model%E3%82%92%E5%A4%89%E6%95%B0%E3%81%A8%E3%81%97%E3%81%A6%E6%89%B1%E3%81%84%E3%81%9F%E3%81%84)** を追記
+ **[クエリスコープを利用する](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#%E3%82%AF%E3%82%A8%E3%83%AA%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97%E3%82%92%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B)** を追記
+ **[Inputから特定の要素を取得したい・特定の要素を除外したい](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#input%E3%81%8B%E3%82%89%E7%89%B9%E5%AE%9A%E3%81%AE%E8%A6%81%E7%B4%A0%E3%82%92%E5%8F%96%E5%BE%97%E3%81%97%E3%81%9F%E3%81%84%E7%89%B9%E5%AE%9A%E3%81%AE%E8%A6%81%E7%B4%A0%E3%82%92%E9%99%A4%E5%A4%96%E3%81%97%E3%81%9F%E3%81%84)** を追加

## 2017-09-27
+ **[Eloquentで取得したモデルにデータがあるか判定したい](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#eloquent%E3%81%A7%E5%8F%96%E5%BE%97%E3%81%97%E3%81%9F%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8C%E3%81%82%E3%82%8B%E3%81%8B%E5%88%A4%E5%AE%9A%E3%81%97%E3%81%9F%E3%81%84)** を修正

## 2017-09-26
+ **[Transaction処理を行いたい](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#transaction%E5%87%A6%E7%90%86%E3%82%92%E8%A1%8C%E3%81%84%E3%81%9F%E3%81%84)** を修正（ご指摘いただいた方、ありがとうございました！）
+ **[SQL文のデバッグを行いたい！](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#sql%E6%96%87%E3%81%AE%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%82%92%E8%A1%8C%E3%81%84%E3%81%9F%E3%81%84)** を追加

## 2019-02-19
+ **[## 途中まで条件が同じEloquentモデルを複製したい](https://qiita.com/tosite0345/items/1acaa0cf892b72e3d30b#途中まで条件が同じeloquentモデルを複製したい)** を追加