Model
アプリケーションが育つと、さまざまな場所やさまざまなプレゼンターで似たようなデータベースの操作をする必要があるとすぐ気づきます。たとえば最新の公開された記事を取り出すことなどです。アプリケーションを良くしようとして、たとえば記事が下書きかどうかを示す印を足したら、記事をデータベースから取り出しているすべての場所を見直して、下書きでない記事だけを選ぶ
where の条件を足さなければなりません。
こうなると、データベースを直接扱うだけでは足りなくなり、公開された記事を返す新しいメソッドを使うほうが賢いやり方になります。そしてあとで別の条件(たとえば未来の日付の記事は表示しないなど)を足すときも、コードを 1 か所直すだけで済みます。
そのメソッドは PostFacade クラスに置き、getPublicArticles() と名付けます。
記事の面倒を見るモデルのクラス PostFacade を app/Model/
のディレクトリに作ります。
<?php
namespace App\Model;
use Nette;
final class PostFacade
{
public function __construct(
private Nette\Database\Explorer $database,
) {
}
public function getPublicArticles()
{
return $this->database
->table('posts')
->where('created_at < ', new \DateTime)
->order('created_at DESC');
}
}
このクラスでは、データベースの Explorerをコンストラクタで求めています。これは DI コンテナの力を使っています。
では HomePresenter に移り、Nette\Database\Explorer
への依存を取り除いて、新しいクラスへの依存に置き換えます。
<?php
namespace App\Presentation\Home;
use App\Model\PostFacade;
use Nette;
final class HomePresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private PostFacade $facade,
) {
}
public function renderDefault(): void
{
$this->template->posts = $this->facade
->getPublicArticles()
->limit(5);
}
}
use の区画に App\Model\PostFacade があるので、PHP のコードでは書き方を
PostFacade と短くできます。このオブジェクトをコンストラクタで求め、$facade
のプロパティに入れ、renderDefault メソッドで使います。
最後の一歩は、このオブジェクトを作るよう DI コンテナに教えることです。ふつうは
config/services.neon ファイルの services
の区画に項目を足し、完全なクラス名とコンストラクタのパラメータを指定します。これで登録され、そのオブジェクトは
サービス と呼ばれます。オートワイヤリングの魔法のおかげで、たいていコンストラクタのパラメータを指定する必要はありません。DI
がそれを見分けて自動的に渡すからです。ですからクラス名を書くだけで足ります。
...
services:
- App\Model\PostFacade
とはいえ、この行も足す必要はありません。services.neon の search
の区画で、-Facade や -Factory で終わるすべてのクラスを DI
が自動的に見つけると定められていて、PostFacade もそれに当てはまるからです。
まとめ
PostFacade クラスはコンストラクタで Nette\Database\Explorer
を求め、このクラスは DI
コンテナに登録されているので、コンテナがそのインスタンスを作って渡します。こうして DI は
PostFacade のインスタンスを私たちの代わりに作り、それを求めた HomePresenter
クラスのコンストラクタへ渡します。まるでマトリョーシカ人形です :)
みんな欲しいものを言うだけで、それがどこでどう作られるかは気にしません。作るのは DI
コンテナが受け持ちます。
dependency injectionと設定については、こちらで詳しく読めます。