---
title: Laravelのメモ
tags: 定例発表
description: Laravelの基本
slideOptions:
theme: black
slideNumber: 'c/t'
center: false
transition: 'none'
keyboard: true
width: '93%'
height: '150%'
---
<style>
/* basic design */
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6,
.reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {
font-family: 'Meiryo UI', 'Source Sans Pro', Helvetica, sans-serif, 'Helvetica Neue', 'Helvetica', 'Arial', 'Hiragino Sans', 'ヒラギノ角ゴシック', YuGothic, 'Yu Gothic';
text-align: left;
line-height: 1.6;
letter-spacing: normal;
text-shadow: none;
word-wrap: break-word;
}
</style>
# Laravel
- PHPで書かれたWebアプリケーションフレームワーク(OSS)
- らくらくWebサイトが構築できる(MVC)

## Laravel とほか
https://trends.google.com/trends/explore?q=CakePHP,Laravel,Codeigniter,Phalcon,Symfony
- 圧倒的にLaravelが人気
- Symfony はLaravelのコアにも利用されている
## Laravel のバージョンポリシー
- LTSリリースでは、バグ修正が2年間提供され、セキュリティ修正が3年間提供されます
- 一般的なリリースの場合、バグ修正は6か月間、セキュリティ修正は1年間提供されます。
- 最近(3/3) にVersion 7 がリリースされた

## Laravelのよさげなところ
- ORM使いやすい(relation 定義、scope)
- Validatonが豊富
- 独自の例外処理が気軽にかける
- Kernel
- DIがよい
## ORM ってなに
- Object-Relational Mapping (オブジェクト関係マッピング)の略
- オブジェクト指向と関係データベースの考え方を上手く変換して繋いでくれるもの
- RDBにアクセスする際に、SQLを書かないため、見やすい
- Laravel のORM はEloquent ORM
```php
// sql
select * from users;
// orm(SQL を意識しないですむ)
User::all()
```
## Eloquent ORM のリレーション
1対多

```php
<?php
namespace App\Model\User;
use Jenssegers\Mongodb\Eloquent\Model;
class User extends Model
{
public function books()
{
return $this->hasMany(Book::class);
}
```
```php
<?php
namespace App\Model\Book;
use Jenssegers\Mongodb\Eloquent\Model;
class Book extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
```
```php
$user = User::find(1);
// user に紐づく複数book を取得
$books = $user->books
$book =Book::find(1);
// book に紐づくUserを取得
$user = $book->user;
```
## Eloquent ORM のリレーション(注意)
#### 1+N 問題
```php
$users = User::Limit(5)->get();
$books = $users->books
foreach($users as $user){
foreach($user->books as $book){
}
}
```
```
select * from books where books.user_id = 1
select * from books where books.user_id = 2
select * from books where books.user_id = 3
select * from books where books.user_id = 4
select * from books where books.user_id = 5
```
```php
$user = User::with('books')->find(1)->get();
```
```
select * from posts where posts.user_id in (1, 2, 3, 4, 5)
```
https://github.com/beyondcode/laravel-query-detector
https://www.yoheim.net/blog.php?q=20181104
## Eloquent ORM Scope
#### 条件をつけて検索したい
```php
class Book extends Model
{
public function scopeName($query,$name)
{
$query->where('name',$name);
// Query Builder 以外返すのはNG
// return $query->where('name',$name)->get();
}
public function scopeDate($query,$date)
{
$query->whereDate('created_at', '=', $date)
}
public function scopeHoge($query)
{
$query->where()
->where()
->with()
->date(''2018-08-03')
->name('速習 Laravel 6 速習シリーズ');
}
}
```
```php
$user = User::find(1);
$user->books()->name('速習 Laravel 6 速習シリーズ')->get()
$user->books()->name('速習 Laravel 6 速習シリーズ')->Date('2018-08-03')->get()
```
## Validation
#### 基本
Laravel の思想的には、Controllerで書くのが一般的
- Validationが通らなかったら、例外処理でSessionにエラー情報を格納
``` php
function store(Request $request)
{
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// The blog post is valid...
}
```
Controllerに書きたくない人はValidator Facade を利用する
```php
use Illuminate\Support\Facades\Validator;
$params = ['password' => 'hugahuga'];
$validator = Validator::make($params, [
'password' => [
'required'
]
]);
```
個人的には、要所要所書くのがおすすめ(コントローラー、モデル)
#### 拡張
独自のバリデーションを作成する方法は3,4個ある。(ありすぎ)
Rule オブジェクトをよく利用する
- 書くのが楽
- クラスごとに定義できるので、便利
- 独自のメッセージなどカスタム可が楽
- construct からDIできる
```php
<?php
namespace App\Rules\Auth;
use App\Model\Admin;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Hash;
class Password implements Rule
{
public function passes($attribute, $value)
{
// $value = password
// true or false で返す
return Hash::check($value, User::first()->password);
}
public function message()
{
return 'ログインに失敗しました';
}
}
```
```php
$validator = Validator::make($params, [
'password' => [
'required',
new Password()
]
]);
```
#### 注意
用意されているValidationは結構あやしい挙動があるので、実際に使うときは都度検証したほうがいいかも
- 型の指定はしておいた方が無難(動作が変わる) 例 max
- // md参照
## 例外処理
例外処理とはプログラムがなんらかのエラーを出したときに、処理を中断して別の処理を行うことだ。
- どういう時につかうの?
https://ja.wikipedia.org/wiki/%E4%BE%8B%E5%A4%96%E5%87%A6%E7%90%86#%E4%BE%8B%E5%A4%96%E3%81%A8%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AE%E9%81%95%E3%81%84
基本的に想定外のことを例外処理とするのが鉄則らしい
- 失敗した時に実行したい処理がある
- 複数テーブル更新時にエラー
## Laravel 例外処理
すでにフレームワークで例外処理をある程度やってくれているので、ほぼ不要
(error 時に404 にするなど)
## よく利用する方法
メイン処理部分で都度都度、結果をチェックしている
```php
$result = process1()
if($result = 'error'){
return 'errorだよ'
}
$result = process2()
if($result = 'error'){
return 'errorだよ'
}
$result = process3()
if($result = 'error'){
return 'errorだよ'
}
$result = process4()
if($result = 'error'){
return 'errorだよ'
}
```
こっちのほうがきれい
```php
process1($params)
process2($params)
process3($parama)
process4($params)
```
## 独自例外の作り方
#### Laravel のベース(共通処理)
- report でログ残しやSlack通知
- render でレスポンスを作成
render はLaravel独自のものがあるので手はあんまり加えない
```php
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
public function report(Exception $exception)
{
//$message = $exception->getMessage();
//$code = $exception->getCode();
//$file = $exception->getFile();
//$line = $exception->getLine();
//$trace = $exception->getTraceAsString();
//$params = $request->input();
parent::report($exception);
}
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}
}
```
#### 独自拡張
```php
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Log;
class ApiException extends Exception
{
public $errors;
public function __construct($errors)
{
$this->errors = $errros;
parent::__construct(Lang::get('exception.externalApi'), 503);
}
/**
* ここでログの記録
*
* @return void
*/
public function report()
{
// slack 通知やログの記録
}
/**
* Render the exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function render($request)
{
return response([
'message' => '外部システムの通信エラーが発生しています',
'status' => 'error',
'errors' => $this->errors
], 503);
}
}
```
```php
public function mail($shopNumber)
{
$url = Helper::config('api.mail');
$reponse = Helper::httpPost($url);
if ($reponse['status'] != 'success') {
throw new ApiException($reponse['message']);
}
return $reponse;
}
```
API呼び出しとValidation時にエラーが発生した場合は大抵共通のレスポンスなので、
例外処理を使ったほうが記述が簡単になるのではと考えています
## DI(Dependency injection)
- 日本語でいう外部注入
- 委譲や合成の考え方
例:お金を支払うクラスを考える
ほしい部品
お金、利用者、払い先、払い方法、、、
```php
class Money {
private $money;
public function __construct($money)
{
$this->money = $money;
}
}
class User {
private $name;
public function __construct($name)
{
$this->name = $name;
}
}
}
class Payment {
private $pay;
public function __construct($pay)
{
$this->pay = $pay;
}
}
```
```php
class PaymentMoney {
private user;
private money;
private payment;
public function __construct()
{
$money = new Money('1500')
$user = new User('saito')
$payment = new Payment('paypay')
}
}
// 上の方法だとMoenyやUserのコンストラクタが変わったら、PaymentMoenyでも変更が必要になる。
class PaymentMoney {
private user;
private money;
private payment;
public function __construct($user,$money,$payment)
{
$this->money = $money
$this->user = $user;
$this->payment = $payment;
}
}
```
```php
$payment = new PaymentMoney(new User('saito'),new Money('1500'),new Payment('paypay'))
```
確かに、柔軟に対応できるけど、毎回↑のようにコンストラクタを書くのはめんどくさい
Laravel では単純なクラス生成(引数なし)であれば、これを一行でかける(引数の順番も考えなくていい)
```php
$payment = app(Payment::class)
```
例
```php
class PaymentMoney {
private user;
private money;
private payment;
private validator;
public function __construct($user,$money,$payment,$validator)
{
$this->money = $money
$this->user = $user;
$this->payment = $payment;
$this->validator = $validator
}
public function pay ($params){
if(!$this->validator->validate($params)){
throw new PaymentException($this->validator->errors)
}
// 会計処理
$this->user->find()
$this->money->add()
$this->payment->pay()
}
}
```
最近、Goを勉強しているのですが、継承よりもInterfaceで定義すると柔軟にできるのかも。。。と考えています。
(中身のクラスがなんであれ、メソッドを実装していればよいため)
## Kernel
コントローラーでの処理前、後で共通の処理をしている
#### Laravel での処理
```php
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
protected $middleware = [
\App\Http\Middleware\TrustProxies::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\RequireJson::class, // 追加
],
];
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
// original
'CheckLogin' => \App\Http\Middleware\CheckLogin::class,
];
}
```
#### 独自のミドルウェア定義
```php
<?php
namespace App\Http\Middleware;
use Closure;
class CheckLogin
{
/**
* ログインチェック
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (!session('login')) {
return redirect()->route('login.create');
}
return $next($request);
}
}
```
#### ミドルウェアの利用
web.php,api.phpなどに記載
```php
<?php
// 認証必要
Route::group(["middleware" => 'CheckLogin'], function () {
Route::group(['namespace' => 'Hoge'], function () {
Route::get('hoge', ['uses' => 'HogeController@index', 'as' => 'hoge.index']);
});
});
// 認証不要
Route::group(['namespace' => 'Session'], function () {
Route::get('login', ['uses' => 'LoginController@create', 'as' => 'login.create']);
});
// どのルートにもマッチしない
Route::fallback(function () {
return response()->view('errors.404');
})->name('fallback.404');
```
## Laravel を使った感想
- 使い方、実装方法が定義されていないので、色々なやり方がある
- Rails などに比べると非常に柔軟(Railsは決まったやり方から拡張させるのは結構しんどい)
- 多くのシステムで利用されている(きっと)のでオンラインでのナレッジが豊富
- 簡素なシステムであれば、すぐに実装できる
- 最近だと、LaravelMix など、WebPackを簡易的にかけるシステムなどある
- Oauthや様々なサービスが簡単に実装できる