15. • ディレクトリ
15
Model 層の比較
The Model layer represents the part of your application that implements the
business logic.
モデル層はビジネスロジックを実装するアプリケーションの部品を表しま
す。
.
├── src/
: :
│ ├── Model/
│ │ ├── Behavior/
│ │ ├── Entity/
│ │ └── Table/
: :
16. • CakePHP ORM
• 3つの概念に分かれる
• Table
• Entity
• Behavior
16
Model 層の比較
The CakePHP ORM borrows ideas and concepts from both ActiveRecord and
Datamapper patterns. It aims to create a hybrid implementation that
combines aspects of both patterns to create a fast, simple to use ORM.
CakePHP の ORM はアクティブレコードやデータマッパーパターンのアイ
デアやコンセプトを拝借しています。 その目的は、早く作成し、シンプ
ルに ORM を利用するという2つの利点を混成させるためです。
17. • Table
• Entity
17
Model 層の比較
They allow you to save new records, modify/delete existing ones, define
relations, and perform bulk operations.
これらを利用することで、新しいレコードを保存したり、 既存データの
編集/削除、リレーションの定義、そして一括処理ができます。
Entities represent individual records and allow you to define row/record level
behavior & functionality.
エンティティーは、個々のレコードを意味し、 行/レコードレベルの振る
舞いや機能の定義を可能にします。
18. • Behavior
• 【考察】
• 処理の種類ごとに記述する場所が規約で決められている
• どこに書くべきか、悩まずに記述できる
• 組み合わせやすい分類になっている
• 共通処理は Behavior
• テーブル単位の操作の Table
• レコード単位の操作は Entity
18
Model 層の比較
Behaviors provide a convenient way to package up behavior that is common
across many models.
ビヘイビアーは、多くのモデルで共通の振る舞いをまとめる便利な方法を
提供します。
19. • “Models” のようなディレクトリは存在しない
• 【私見】意訳すると、
「“models” という言葉が指す意味は人によって変わるので、
開発したいモノに合わせて各々が判断した場所に置けばよい」
19
Model 層の比較
When getting started with Laravel, many developers are confused by the lack
of a models directory. However, the lack of such a directory is intentional. We
find the word "models" ambiguous since it means many different things to
many different people.
For this reason, we choose to place Eloquent models in the app directory by
default, and allow the developer to place them somewhere else if they
choose.
20. • 明示的に Model と言っているのは Eloquent ORM のみ
• php artisan make:model の説明
「Create a new Eloquent model class」
• 【考察】
• ORM 即ち Model という意味ではない
• Eloquent model class にビジネスロジックを
記述してもよい
• Eloquent model class を単なる ORM とみなし
他の階層構造にビジネスロジックを記述してもよい
20
Model 層の比較
21. • Eloquent ORM
• 【私見】「Laravel 強み」としてよく挙げられる
• 【考察】比較的シンプルな記述ができるのが理由か
• 読み方がよく議論になる
• 【私見】では「エロクワント」と読む
21
Model 層の比較
The Eloquent ORM included with Laravel provides a beautiful, simple
ActiveRecord implementation for working with your database.
23. 23
Model 層の比較 - コード比較 case 1
class SafeStringBehavior extends Behavior
{
public function beforeSave($event, $entity, $options)
{
// h() -> htmlspecialchars()
$entity->body = h($entity->body);
return true;
}
}
class ClientContactsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
// SafeStringBehavior の読み込み
$this->addBehavior('SafeString');
}
}
// UserContractsTable も同様に記述
24. 24
Model 層の比較 - コード比較 case 1
class SafeStringBehavior extends Behavior
{
public function beforeSave($event, $entity, $options)
{
// h() -> htmlspecialchars()
$entity->body = h($entity->body);
return true;
}
}
class ClientContactsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
// SafeStringBehavior の読み込み
$this->addBehavior('SafeString');
}
}
// UserContractsTable も同様に記述する
• 共通の挙動は Behavior に記述する
• 保存前実行される処理は beforeSave
関数に記述する
25. 25
Model 層の比較 - コード比較 case 1
class SafeStringBehavior extends Behavior
{
public function beforeSave($event, $entity, $options)
{
// h() -> htmlspecialchars()
$entity->body = h($entity->body);
return true;
}
}
class ClientContactsTable extends Table
{
public function initialize(array $config)
{
parent::initialize($config);
// SafeStringBehavior の読み込み
$this->addBehavior('SafeString');
}
}
// UserContractsTable も同様に記述する
• Table に addBehavior() を記述する
26. パターン1
26
Model 層の比較 - コード比較 case 1
class ContactObserver
{
public function saving(Model $model)
{
// e() -> htmlspecialchars()
$model->body = e($model->body);
}
}
// UserContact も同様に記述
class ClientContact extends Model
{
}
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
ClientContact::observe(ContactObserver::class);
UserContact::observe(ContactObserver::class);
}
}
27. パターン1
27
Model 層の比較 - コード比較 case 1
class ContactObserver
{
public function saving(Model $model)
{
// e() -> htmlspecialchars()
$model->body = e($model->body);
}
}
// UserContact も同様に記述
class ClientContact extends Model
{
}
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
ClientContact::observe(ContactObserver::class);
UserContact::observe(ContactObserver::class);
}
}
• 保存処理の前に発火する
“saving” イベントを観測する
Observer を作成
28. パターン1
28
Model 層の比較 - コード比較 case 1
class ContactObserver
{
public function saving(Model $model)
{
// e() -> htmlspecialchars()
$model->body = e($model->body);
}
}
// UserContact も同様に記述
class ClientContact extends Model
{
}
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
ClientContact::observe(ContactObserver::class);
UserContact::observe(ContactObserver::class);
}
}
• ServiceProvider 内で対象 Model に
Observer を登録
29. パターン1
29
Model 層の比較 - コード比較 case 1
class ContactObserver
{
public function saving(Model $model)
{
// e() -> htmlspecialchars()
$model->body = e($model->body);
}
}
// UserContact も同様に記述
class ClientContact extends Model
{
}
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
ClientContact::observe(ContactObserver::class);
UserContact::observe(ContactObserver::class);
}
}
• 欠点:Modelの定義から
処理を辿れない
30. パターン2
30
Model 層の比較 - コード比較 case 1
// UserContact も同様に記述
class ClientContact extends Model
{
protected $events = [
'saving' => ClientContactSaving::class,
];
}
class Event
{
}
// UserContractSaving も同様に記述
class ClientContactSaving extends Event
{
public $target;
public function __construct(ClientContact $clientContact)
{
$this->target = $clientContact;
}
}
class ConvertToSafeString
{
public function handle(Event $event)
{
$event->target->body = e($event->target->body);
}
}
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
'ClientContactSaving' => [
ConvertToSafeString::class,
],
'UserContactSaving' => [
ConvertToSafeString::class,
],
];
}
31. パターン2
31
Model 層の比較 - コード比較 case 1
// UserContact も同様に記述
class ClientContact extends Model
{
protected $events = [
'saving' => ClientContactSaving::class,
];
}
class Event
{
}
// UserContractSaving も同様に記述
class ClientContactSaving extends Event
{
public $target;
public function __construct(ClientContact $clientContact)
{
$this->target = $clientContact;
}
}
class ConvertToSafeString
{
public function handle(Event $event)
{
$event->target->body = e($event->target->body);
}
}
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
'ClientContactSaving' => [
ConvertToSafeString::class,
],
'UserContactSaving' => [
ConvertToSafeString::class,
],
];
}
• Model に “saving” イベントを登録
32. パターン2
32
Model 層の比較 - コード比較 case 1
// UserContact も同様に記述
class ClientContact extends Model
{
protected $events = [
'saving' => ClientContactSaving::class,
];
}
class Event
{
}
// UserContractSaving も同様に記述
class ClientContactSaving extends Event
{
public $target;
public function __construct(ClientContact $clientContact)
{
$this->target = $clientContact;
}
}
class ConvertToSafeString
{
public function handle(Event $event)
{
$event->target->body = e($event->target->body);
}
}
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
'ClientContactSaving' => [
ConvertToSafeString::class,
],
'UserContactSaving' => [
ConvertToSafeString::class,
],
];
}
• Model に対応する
Event を定義
33. パターン2
33
Model 層の比較 - コード比較 case 1
// UserContact も同様に記述
class ClientContact extends Model
{
protected $events = [
'saving' => ClientContactSaving::class,
];
}
class Event
{
}
// UserContractSaving も同様に記述
class ClientContactSaving extends Event
{
public $target;
public function __construct(ClientContact $clientContact)
{
$this->target = $clientContact;
}
}
class ConvertToSafeString
{
public function handle(Event $event)
{
$event->target->body = e($event->target->body);
}
}
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
'ClientContactSaving' => [
ConvertToSafeString::class,
],
'UserContactSaving' => [
ConvertToSafeString::class,
],
];
}
• Event を捕捉した際に処理を行う
Listener を定義
34. パターン2
34
Model 層の比較 - コード比較 case 1
// UserContact も同様に記述
class ClientContact extends Model
{
protected $events = [
'saving' => ClientContactSaving::class,
];
}
class Event
{
}
// UserContractSaving も同様に記述
class ClientContactSaving extends Event
{
public $target;
public function __construct(ClientContact $clientContact)
{
$this->target = $clientContact;
}
}
class ConvertToSafeString
{
public function handle(Event $event)
{
$event->target->body = e($event->target->body);
}
}
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
'ClientContactSaving' => [
ConvertToSafeString::class,
],
'UserContactSaving' => [
ConvertToSafeString::class,
],
];
}
• ServiceProvider 内で
Event と Listener の対応を登録
35. パターン2
35
Model 層の比較 - コード比較 case 1
// UserContact も同様に記述
class ClientContact extends Model
{
protected $events = [
'saving' => ClientContactSaving::class,
];
}
class Event
{
}
// UserContractSaving も同様に記述
class ClientContactSaving extends Event
{
public $target;
public function __construct(ClientContact $clientContact)
{
$this->target = $clientContact;
}
}
class ConvertToSafeString
{
public function handle(Event $event)
{
$event->target->body = e($event->target->body);
}
}
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
'ClientContactSaving' => [
ConvertToSafeString::class,
],
'UserContactSaving' => [
ConvertToSafeString::class,
],
];
}
長所
• Model から処理を追跡できる
短所
• 記述量が多い
• 実現するために考えることが多い
• 突如として現れる
基底クラス Event など
36. 実現したいこと
• クライアントと担当コンサルタントの多対多関係
• 担当コンサルタントの一括更新
36
Model 層の比較 - コード比較 case 2
clients
- id
- name
consultants
- id
- name
client_consultant
- client_id
- consultant_id
client_id consultant_id
1 1
1 2
1 3
client_id consultant_id
1 1
1 4
1 5
37. 37
Model 層の比較 - コード比較 case 2
$Clients = TableRegistry::get('Clients');
$Clients->belongsToMany(
'Consultants',
['saveStrategy' => 'replace']
);
$clientEntity = $Clients->get(
1,
['contain' => 'Consultants']
);
$clientEntity->consultants = $Clients->Consultants
->find('all')
->where(['id IN' => [1, 4, 5]])
->all()
->toArray();
$clientEntity->dirty('consultants', true);
$Clients->save($clientEntity);
class Client extends Model
{
public function consultants()
{
return $this->belongsToMany('App¥Consultant');
}
}
class Consultant extends Model
{
}
$client = Client::find(1);
$client->consultants()->sync([1, 4, 5]);
38. 38
Model 層の比較 - コード比較 case 2
$Clients = TableRegistry::get('Clients');
$Clients->belongsToMany(
'Consultants',
['saveStrategy' => 'replace']
);
$clientEntity = $Clients->get(
1,
['contain' => 'Consultants']
);
$clientEntity->consultants = $Clients->Consultants
->find('all')
->where(['id IN' => [1, 4, 5]])
->all()
->toArray();
$clientEntity->dirty('consultants', true);
$Clients->save($clientEntity);
class Client extends Model
{
public function consultants()
{
return $this->belongsToMany('App¥Consultant');
}
}
class Consultant extends Model
{
}
$client = Client::find(1);
$client->consultants()->sync([1, 4, 5]);
データ取得・更新の処理
58. • 表示のためのロジックを切り離すもの
• ディレクトリ
• views 以下には blade template ファイルのみが置かれる
58
View 層の比較
Views contain the HTML served by your application and separate your
controller / application logic from your presentation logic.
.
:
├── resources
: :
│ └── views
:
59. • Blade
• シンプルかつ強力
• <?php ?> タグを使わずに記述
• ヘルパー等は別途実装するかパッケージインストールが必要
59
View 層の比較
Blade is the simple, yet powerful templating engine provided with Laravel.
Unlike other popular PHP templating engines, Blade does not restrict you
from using plain PHP code in your views.
条件分岐 繰り返し
@if ($isSucceeded)
{{ $message }}
@endif
@foreach ($items as $item)
{{ $item['name'] }}
@endforeach
65. • ディレクトリ
65
Controller 層の比較
.
:
├── src/
: :
│ ├── Controller/
│ │ └── Component/
: :
Your controller should handle interpreting the request data, making sure the
correct models are called, and the right response or view is rendered.
Controllers can be thought of as middle layer between the Model and View.
コントローラーはリクエストを解釈して、適切なモデルが 呼ばれるのを
確認して、正しいレスポンスまたはビューを書き出します。コントロー
ラーはモデルとビューの 中間層とみなすことができます。
67. • ドキュメント内に「Controller とは何か」という説明は
明示されていない
• 【考察】単にリクエストをハンドリングするロジックを
記述するクラスという位置づけ
• ディレクトリ
67
Controller 層の比較
Instead of defining all of your request handling logic as Closures in route
files, you may wish to organize this behavior using Controller classes.
.
├── app
: :
│ ├── Http/
│ │ ├── Controllers/
│ │ :
: :
68. • メソッドインジェクション
• メソッドの引数に型情報を与えることで
クラスの依存性解決を自動的に行う
• ¥Illuminate¥Http¥Request を引数に指定することで
リクエストパラメータを扱うことができる
68
Controller 層の比較
In addition to constructor injection, you may also type-hint dependencies on
your controller's methods.
class UserController extends Controller
{
/**
* Store a new user.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$name = $request->name;
//
}
}
76. • Service Container
• クラス依存性解決のための仕組み
• 複数の解決方法が提供されており、
場面に合わせた方法を選ぶことができる
• DI(依存性注入)がやりやすい
→テストの書きやすいコードに
76
その他特徴
The Laravel service container is a powerful tool for managing class
dependencies and performing dependency injection.
class UserController extends Controller
{
protected $users;
public function __construct(UserRepository $users)
{
$this->users = $users;
}
77. • Facade
• Service Container に格納されているインスタンスの
メソッドを static メソッドのように利用可能
• デフォルトで Log, DB, Session などといった
Facade を提供
• 自分で Facade 実装することもできる
77
その他特徴
Facades provide a "static" interface to classes that are available in the
application's service container. Laravel ships with many facades which
provide access to almost all of Laravel's features.
¥DB::transaction(function () {
// トランザクション開始
});
¥Log::debug('debug log');
¥Log::info('info log');
¥Log::error('error log');