[Go to site: main page, start]

Nette Database

Nette Database は PHP のための強力で優雅なデータベース層で、単純さと気の利いた機能を大切にしています。データベースを扱う方法を 2 つ用意しています。素早くアプリケーションを開発するための Explorerと、クエリを直接組み立てる SQL アプローチです。

SQL アプローチ

  • 安全でパラメータ化されたクエリ
  • SQL のクエリの構造を細かく制御
  • 進んだ機能を使う込み入ったクエリを書くとき
  • 特定の SQL の関数を使って性能を最適化

Explorer

  • SQL を書かずに素早く開発
  • テーブルどうしの関係を直感的に扱う
  • クエリの自動的な最適化の恩恵を受ける
  • 速く快適にデータベースを扱うのに向く

インストール

ライブラリは Composerでダウンロードしてインストールします。

composer require nette/database

対応しているデータベース

Nette Database は次のデータベースに対応しています。

データベースサーバー DSN の名前 Explorer の対応
MySQL (>= 5.1) mysql あり
PostgreSQL (>= 9.0) pgsql あり
SQLite 3 (>= 3.8) sqlite あり
Oracle oci なし
MS SQL (PDO_SQLSRV) sqlsrv あり
MS SQL (PDO_DBLIB) mssql なし
ODBC odbc なし

データベースを扱う 2 つのやり方

Nette Database は選択肢を与えます。SQL のクエリを直接書く(SQL アプローチ)か、自動的に生成させる(Explorer)かです。同じ仕事を両方のやり方でどう片付けるか見てみましょう。

SQL アプローチ – SQL のクエリ

// レコードを挿入します
$database->query('INSERT INTO books', [
	'author_id' => $authorId,
	'title' => $bookData->title,
	'published_at' => new DateTime,
]);

// レコードを取り出します: 本の著者
$result = $database->query('
	SELECT authors.*, COUNT(books.id) AS books_count
	FROM authors
	LEFT JOIN books ON authors.id = books.author_id
	WHERE authors.active = 1
	GROUP BY authors.id
');

// 表示します(最適ではなく、N 個の追加のクエリを生みます)
foreach ($result as $author) {
	$books = $database->query('
		SELECT * FROM books
		WHERE author_id = ?
		ORDER BY published_at DESC
	', $author->id);

	echo "Author $author->name has written $author->books_count books:\n";

	foreach ($books as $book) {
		echo "- $book->title\n";
	}
}

Explorer のやり方 – SQL の自動生成

// レコードを挿入します
$database->table('books')->insert([
	'author_id' => $authorId,
	'title' => $bookData->title,
	'published_at' => new DateTime,
]);

// レコードを取り出します: 本の著者
$authors = $database->table('authors')
	->where('active', 1);

// 表示します(自動的に最適化された 2 つのクエリだけを生みます)
foreach ($authors as $author) {
	$books = $author->related('books')
		->order('published_at DESC');

	echo "Author $author->name has written {$books->count()} books:\n";

	foreach ($books as $book) {
		echo "- $book->title\n";
	}
}

Explorer のやり方は SQL のクエリを自動的に生成して最適化します。上の例では、SQL アプローチが N+1 個のクエリ(著者に 1 つ、そして著者ごとに本のクエリが 1 つずつ)を生むのに対し、Explorer はクエリを自動的に最適化して 2 つだけ、つまり著者に 1 つと、そのすべての本に 1 つを実行します。

どちらのやり方も、必要に応じてアプリケーションの中で自由に組み合わせられます。

接続と設定

データベースに接続するには、Nette\Database\Connectionクラスのインスタンスを作るだけです。

$database = new Nette\Database\Connection($dsn, $user, $password);

$dsn(Data Source Name)パラメータは PDO が使うものと同じで、たとえば host=127.0.0.1;dbname=test です。失敗すると Nette\Database\ConnectionException を投げます。

とはいえ、もっと便利な方法をアプリケーションの設定が用意しています。そこに database の区画を足すだけです。これで必要なオブジェクトが作られ、Tracyのバーにデータベースのパネルも現れます。

database:
	dsn: 'mysql:host=127.0.0.1;dbname=test'
	user: root
	password: password

そのあと接続のオブジェクトは DI コンテナからサービスとして受け取れます。たとえば次のようにです。

class Model
{
	public function __construct(
		// または Nette\Database\Explorer
		private Nette\Database\Connection $database,
	) {
	}
}

詳しくはデータベースの設定をご覧ください。

Explorer を手で作る

Nette の DI コンテナを使っていないなら、Nette\Database\Explorer のインスタンスを手で作れます。

// データベース接続
$connection = new Nette\Database\Connection('mysql:host=127.0.0.1;dbname=mydatabase', 'user', 'password');
// キャッシュの保管場所。Nette\Caching\Storage を実装します。たとえば:
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp/dir');
// データベースの構造のリフレクションを受け持ちます
$structure = new Nette\Database\Structure($connection, $storage);
// テーブル名、列、外部キーの対応づけの規則を定義します
$conventions = new Nette\Database\Conventions\DiscoveredConventions($structure);
$explorer = new Nette\Database\Explorer($connection, $structure, $conventions, $storage);

接続の管理

Connection オブジェクトが作られると、接続は自動的に確立されます。接続を遅らせたいなら lazy モードを使います。設定lazy を設定するか、次のようにします。

$database = new Nette\Database\Connection($dsn, $user, $password, ['lazy' => true]);

接続を管理するには connect()disconnect()reconnect() メソッドを使います。

  • connect() はまだ接続がなければ接続を作り、Nette\Database\ConnectionException を投げることがあります。
  • disconnect() は今のデータベース接続を切ります。
  • reconnect() は接続を切ってから、データベースに接続し直します。このメソッドも Nette\Database\ConnectionException を投げることがあります。

さらに onConnect イベントで接続にまつわる出来事を見張れます。これはデータベースに接続したあとに呼ばれるコールバックの配列です。

// データベースに接続したあとに実行されます
$database->onConnect[] = function($database) {
	echo "Connected to the database";
};

onQuery イベントも同じように働きます。これは実行されたクエリごとに(そしてクエリが失敗したときにも)呼ばれるコールバックの配列で、ログや性能の計測に役立ちます。

Tracy のデバッグバー

Tracyを使っていれば、デバッグバーの Database のパネルが自動的に有効になります。そこには実行されたすべてのクエリ、そのパラメータ、実行にかかった時間、そしてそれが呼ばれたコードの場所が表示されます。