ブログのトップページ
最近の記事を並べるトップページを作りましょう。
始める前に、Model-View-Presenter の設計のパターン(MVCに似ています)の基本くらいは知っておくとよいでしょう。
- Model – データを扱う層です。アプリケーションのほかの部分から完全に切り離されています。やり取りするのはプレゼンターとだけです。
- View – 表側の層です。求められたデータをテンプレートで描き、利用者に見せます。
- Presenter(あるいは Controller) – つなぎの層です。プレゼンターは Model と View を結び付けます。リクエストを扱い、Model にデータを求め、それを View へ渡します。
私たちのブログのような単純なアプリケーションでは、model の層はまるごとデータベースへの問い合わせだけになります。そのために余分な PHP のコードはまだ要りません。ですから手はじめにプレゼンターとテンプレートだけを作ります。Nette ではプレゼンターごとに自分のテンプレートがあるので、同時に作っていきます。
Adminer でデータベースを作る
データを保存するには MySQL のデータベースを使います。ウェブの開発者にもっともよく選ばれているからです。とはいえ使いたくないなら、お好きなデータベースを遠慮なく選んでください。
ブログの記事を保存するデータベースの構造を用意しましょう。ごく簡単に始められます。記事のテーブルをひとつ作るだけです。
データベースを作るには Adminerをダウンロードするか、お好みのほかのデータベース管理の道具を使えます。
Adminer を開いて、quickstart という新しいデータベースを作りましょう。
posts という新しいテーブルを作り、次の列を足します。
idint。autoincrement(AI)にチェックを入れますtitlevarchar、長さ 255contenttextcreated_attimestamp
次のようになるはずです。

CREATE TABLE `posts` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`title` varchar(255) NOT NULL,
`content` text NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB CHARSET=utf8;
InnoDB の保存エンジンを使うことがとても大事です。理由はあとで分かります。今のところは、それを選んで保存を押してください。
アプリケーションから直接記事を足せるようにする前に、見本のブログの記事をいくつか手で足してみましょう。
INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1, 'Article One', 'Lorem ipsum dolor one', CURRENT_TIMESTAMP),
(2, 'Article Two', 'Lorem ipsum dolor two', CURRENT_TIMESTAMP),
(3, 'Article Three', 'Lorem ipsum dolor three', CURRENT_TIMESTAMP);
データベースにつなぐ
データベースができて記事もいくつか入ったので、真新しいページにそれを表示するときです。
まず、どのデータベースを使うのかをアプリケーションに伝える必要があります。データベース接続の設定は
config/common.neon ファイルで、DSNと資格情報を使って行います。次のようになるはずです。
database:
dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
user: *ここにあなたのユーザー名を入れます*
password: *ここにあなたのデータベースのパスワードを入れます*
このファイルを編集するときは字下げに気をつけてください。NEON 形式は空白もタブも受け付けますが、両方を混ぜてはいけません。Web Project の既定の設定ファイルはタブを使っています。
データベース接続を注入する
記事を並べるプレゼンター HomePresenter
には、データベース接続が要ります。それを受け取るために、次のようにコンストラクタを使います。
<?php
namespace App\Presentation\Home;
use Nette;
final class HomePresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private Nette\Database\Explorer $database,
) {
}
// ...
}
データベースから記事を読み込む
ではデータベースから記事を取り出して、HTML のコードを描くテンプレートへ渡しましょう。そのためにいわゆる render のメソッドがあります。
public function renderDefault(): void
{
$this->template->posts = $this->database
->table('posts')
->order('created_at DESC')
->limit(5);
}
これでプレゼンターには renderDefault() というひとつの render
のメソッドができ、データベースのデータをテンプレート(View)へ渡します。テンプレートは
app/Presentation/{プレゼンター名}/{ビュー名}.latte にあるので、この場合は
app/Presentation/Home/default.latte
です。テンプレートでは、データベースから取り出した記事の入った $posts
という変数が使えるようになります。
テンプレート
ウェブサイト全体には主のテンプレート(layout と呼び、ヘッダー、スタイル、フッターなどが入っています)があり、ビューごとに専用のテンプレート(たとえばブログの記事を表示するもの)があって、主のテンプレートの一部を上書きできます。
既定では layout のテンプレートは app/Presentation/@layout.latte
にあり、次のものを含みます。
...
{include content}
...
{include content} のタグは、content
という名前のブロックを主のテンプレートに差し込みます。このブロックはビューごとのテンプレートで定義します。私たちの場合、Home/default.latte
ファイルを次のように書き換えます。
{block content}
Hello World
{/block}
これで content のブロックが定義され、主の layout
に差し込まれます。ブラウザをもう一度読み込み直すと、「Hello
World」という文のページが見えます(ソースコードでは、@layout.latte で定義された HTML
のヘッダーとフッターも付いています)。
ではブログの記事を表示しましょう。テンプレートを次のように書き換えます。
{block content}
<h1>私のブログ</h1>
{foreach $posts as $post}
<div class="post">
<div class="date">{$post->created_at|date:'F j, Y'}</div>
<h2>{$post->title}</h2>
<div>{$post->content|truncate:256}</div>
</div>
{/foreach}
{/block}
ブラウザを読み込み直すと、ブログの記事の一覧が見えます。この一覧はまだ見た目も色味も味気ないので、きれいな CSS のスタイルを
www/css/style.css ファイルに足して、layout から読み込んでかまいません。
...
<link rel="stylesheet" href="{$basePath}/css/style.css">
</head>
...
{foreach} のタグは、$posts
変数でテンプレートへ渡されたすべての記事を回り、記事ごとに HTML
のコードの断片を表示します。PHP のコードとまったく同じように振る舞います。
|date:
の書き方はフィルタと呼ばれます。フィルタは出力を整えるのに使います。このフィルタは日付(たとえば
2013-04-12)をもっと読みやすい形(April 12, 2013)に変えます。|truncate
のフィルタは文字列を指定した長さの上限まで切り詰め、切り詰めたときは終わりに三点リーダ(…)を足します。ここは概要なので、記事の中身をすべて表示する意味はありません。そのほかの既定のフィルタはドキュメントにありますし、必要なら自分で作れます。
もうひとつあります。さっきのコードはもっと短く単純にできます。*Latte のタグ* を n:属性 に置き換えることでそうします。
{block content}
<h1>私のブログ</h1>
<div n:foreach="$posts as $post" class="post">
<div class="date">{$post->created_at|date:'F j, Y'}</div>
<h2>{$post->title}</h2>
<div>{$post->content|truncate:256}</div>
</div>
{/block}
n:foreach の属性は div のブロックを foreach
のループで包みます(さっきのコードとまったく同じように働きます)。
まとめ
これで記事がいくつか入ったごく単純な MySQL のデータベースができました。アプリケーションはこのデータベースにつなぎ、テンプレートでその記事の単純な一覧を表示します。