DBIx::Class::Manual::Intro

テキトーに訳す。
DBIx::Class::Manual::Intro - Introduction to DBIx::Class - metacpan.org

INTRODUCTION

SQLを書くのに飽きてPerlネイティブのDB I/Fが欲しい人。
Class::DBIより優れた方法を探してる人。
そういう人向け。

THE DBIx::Class WAY

DBIx::Classのやり方を身につけるのに役立つことを紹介。

Tables become ResultSources

DBIx::Classはテーブル構造を知る必要がある。
それはDBIx::Class::ResultSourceを定義することで可能になる。
それぞれのテーブルはカラムや関連を定義したResultSourceを1つ持つ。
大事なこと。

A ResultSource == Table

(たいていの場合)

It's all about the ResultSet

いくつかのResultSourceを定義したので、今度はそれらの定義を実際に使って、クエリからPerlオブジェクトを生成したい。
3つのカラム(albumid, artist, title)を持ったテーブル(album)を表すResultSourceを定義したとしよう。このテーブルに問い合わせたいときには、そのResultSourceのためのDBIx::Class::ResultSetを作成する。例えば、

SELECT albumid, artist, title FROM album;

の結果はalbumテーブルのResultSourceからひとつのResultSetオブジェクトになる。
これも同様に可能だ。

SELECT title FROM album GROUP BY title;

大事なこと。

DBISQLクエリを扱うときはいつでもDBIx::Class::ResultSetを作成することになる。

Search is like "prepare"

DBIx::Classは実際にデータベースからデータを取り出す必要があるときまで待つようにしている。戻り値としてResultSetを受け取ったとき、nextやfirstのようなデータにアクセスするメソッドを使うまでクエリは実行されない。
大事なこと。

クエリを実行するのはResultSetを作るときではなく、データを取り出すとき。

SETTING UP DBIx::Class

設定、使用方法を見ていこう。
設定方法は手動と自動とがある。

Setting it up manually

DBIx::Class::Schemaクラスを継承してベーススキーマクラスを作成する。

package My::Schema;
use base qw/DBIx::Class::Schema/;

このクラスではload_classes()メソッドを使ってresult_source(tableとmodel)クラスをロードする。クラスを指定する方法は、
手動

# load My::Schema::Album and My::Schema::Artist
__PACKAGE__->load_classes(qw/ Album Artist /);

名前空間

# load My::Schema::Album and My::Schema::Artist and My::OtherSchema::LinerNotes
__PACKAGE__->load_classes(
    {
      'My::Schema' => [qw/ Album Artist /],
      'My::OtherSchema' => [qw/ LinerNotes /]
    }
);

またはその名前空間にあるすべてのクラスを自動でロードすることができる。

# load My::Schema::*
__PACKAGE__->load_classes();

次にロードしたいクラスを作成する。

package My::Schema::Album;
use base qw/DBIx::Class/;

それぞれのクラスでload_components()メソッドを使用してコンポーネントをロードする。これは"Core"とその他必要なものを指定するようにする。例えば、オートインクリメントの主キーがある場合。

__PACKAGE__->load_components(qw/ PK::Auto Core /);

PK::Autoは多くのデータベースでサポートされている。詳しくはDBIx::Class::Storage::DBIを参照。
テーブルをセット。

__PACKAGE__->table('album');

カラムを追加。

__PACKAGE__->add_columns(qw/ albumid artist title /);

それぞれのカラムはaccessor, data_typeなどの持ってると役に立つ情報と一緒に設定ができる。add_columnsにハッシュを渡せばいい。

  __PACKAGE__->add_columns(albumid =>
                            { accessor  => 'album',
                              data_type => 'integer',
                              size      => 16,
                              is_nullable => 0,
                              is_auto_increment => 1,
                              default_value => '',
                            },
                          artist =>
                            { data_type => 'integer',
                              size      => 16,
                              is_nullable => 0,
                              is_auto_increment => 0,
                              default_value => '',
                            },
                          title  => 
                            { data_type => 'varchar',
                              size      => 256,
                              is_nullable => 0,
                              is_auto_increment => 0,
                              default_value => '',
                            }
                         );

ほとんどのデータはDBIx::Classから直接使われることはないが、DBIx::Class::WebFormなどの関連モジュールから使われる。また、スキーマからテーブルを作成することもできる。詳しくはSQL::Translatorを参照。
使用可能な属性についてはDBIx::Class::ResultSourceを参照。
各カラムのアクセサは自動的に作られるので、My::Schema::Albumはalbumid()(とalbum()), artist(), title()を持っている。
主キーを定義する。

__PACKAGE__->set_primary_key('albumid');

複合主キーの場合はリストで指定する。

__PACKAGE__->set_primary_key( qw/ albumid artistid / );

関連を定義する。他のテーブルのIDとなるカラムにはbelongs_toを使用し、このテーブルの外部キーを含んだオブジェクトを取り出すためのアクセサを事前定義するためにhas_manyを使用する。

__PACKAGE__->has_many('albums', 'My::Schema::Album', 'artistid');

これ以上の方法についてはDBIx::Class::RelationShipを参照。

Using DBIx::Class::Schema::Loader

これは外部のモジュールであり、DBIx::Classの一部ではない。
Class::DBI::Loaderのように、これはデータベースを調査してテーブルクラスを自動的に作成する。簡単なセットアップ。

  package My::Schema;
  use base qw/DBIx::Class::Schema::Loader/;

  __PACKAGE__->loader_options( relationships => 1 );

  1;

実際の読み込み処理はスキーマインスタンスが作成(接続)されたときに行われる。
他のオプションと詳細についてはDBIx::Class::Schema::Loaderを参照。

Connecting

スキーマに接続するには接続の詳細を与える必要がある。引数はDBIのconnectと同じ。

my $schema = My::Schema->connect('dbi:SQLite:/home/me/myapp/my.db');

スキーマインスタンスは必要なだけ作れるので、二つ目のデータベースがある場合にはこうする。

my $other_schema = My::Schema->connect( $dsn, $user, $password, $attrs );

DBIx::Class::Schemaはコネクションをキャッシュしないので、複数のコネクションを使う場合は手動でやる必要がある。
接続したときになにかSQL分を実行したい場合は5つ目の引数を追加する。

  my $another_schema = My::Schema->connect(
      $dsn,
      $user,
      $password,
      $attrs,
      { on_connect_do => ?@on_connect_sql_statments }
  );

詳細はDBIx::Class::Schema::Storage::DBIのconnect_infoを参照。

Basic usage

手動にしろ自動にしろ、ベースクラスを定義したらデータベースとやり取りできる。
$schemaオブジェクトを使ってデータベースにアクセスするには、resultsetメソッドを使用してそれぞれのテーブルを表すResultSetを取り出せばよい。
主キーでレコードを取得するにはこうする。

my $album = $schema->resultset('Album')->find(14);

これはWHERE節にalbumid = 14を指定してSELECTを実行する。戻り値はこの行を表すMy::Schema::Albumのインスタンスで、これを使ってカラムにアクセスしたり更新したりできる。

  $album->title('Physical Graffiti');
  my $title = $album->title; # $title holds 'Physical Graffiti'

代わりにset_columnsとget_columnアクセサを使うこともできる。

  $album->set_column('title', 'Presence');
  $title = $album->get_column('title');

Class::DBIのように、変更をデータベースに反映するためにupdateをコールする。

$album->update;

必要ならローカルでの変更を破棄することもできる。

$album->discard_changes if $album->is_changed;

is_changedを使って、オブジェクトがローカルで変更されているかをチェックできる。

Adding and removing rows

新しいレコードを作成するにはcreateメソッドを使用する。戻り値は新しいレコードへアクセスするためのMy::Schema::Albumのインスタンスとなる。

  my $new_album = $schema->resultset('Album')->create({ 
    title  => 'Wish You Were Here',
    artist => 'Pink Floyd'
  });

新しいレコードにデータを追加する。

  $new_album->label('Capitol');
  $new_album->year('1975');
  $new_album->update;

データベースから削除する。

$new_album->delete;

削除はレコードを最初に取り出してからではなく、ResultSetオブジェクトに対して直接行うこともできる。

  # Delete all of Falco's albums
  $schema->resultset('Album')->search({ artist => 'Falco' })->delete;
Finding your objects

DBIx::Classにはデータを取り出す方法がいくつかある。1つの例。

  # Find all of Santana's albums
  my $rs = $schema->resultset('Album')->search({ artist => 'Santana' });

スカラーコンテキストでは、searchはDBIx::Class::ResultSetオブジェクトを返す。1番目のアルバムを見るにはこうする。

my $album = $rs->first;
print $album->title;

アルバム全体をループしてそれぞれを更新する。

  while (my $album = $rs->next) {
    print $album->artist . ' - ' . $album->title;
    $album->year(2001);
    $album->update;
  }

それを1回でやることもできる。

$rs->update({ year => 2001 });

他に何ができるかはDBIx::Class::ResultSetのMETHODSを参照。
リストコンテキストでは、searchはマッチした行をすべて返す。

  # Fetch immediately all of Carlos Santana's albums
  my @albums = $schema->resultset('Album')->search(
    { artist => 'Carlos Santana' }
  );
  foreach my $album (@albums) {
    print $album->artist . ' - ' . $album->title;
  }

LIKE検索するメソッドもある。

  # Find albums whose artist starts with 'Jimi'
  my $rs = $schema->resultset('Album')->search_like({ artist => 'Jimi%' });

独自のWHERE節を使う。

  # Find Peter Frampton albums from the year 1986
  my $where = 'artist = ? AND year = ?';
  my @bind  = ( 'Peter Frampton', 1986 );
  my $rs    = $schema->resultset('Album')->search_literal( $where, @bind );

複雑なクエリを生成するのに推奨する方法は、SQL::Abstractの構文をserchに渡す方法。

  my $rs = $schema->resultset('Album')->search({
    artist  => { '!=', 'Janis Joplin' },
    year    => { '<' => 1980 },
    albumid => { '-in' => [ 1, 14, 15, 65, 43 ] }
  });

これは次のようなWHERE節になる。

  WHERE artist != 'Janis Joplin'
    AND year < 1980
    AND albumid IN (1, 14, 15, 65, 43)

複雑なクエリの例はDBIx::Class::Manual::Cookbookを参照。
検索には属性を与えることもできる。

  my @albums = My::Schema->resultset('Album')->search(
    { artist => 'Bob Marley' },
    { rows => 2, order_by => 'year DESC' }
  );

@albumsはBob Marleyのアルバムを新しい方から2枚保持している。
属性についてはDBIx::Class::ResultSetのATTRIBUTESを参照。