基本的なC.R.U.D.オペレーション
このガイドではPropelを使ってデータベースへの基本的なC.R.U.D.(作成、所得、更新、削除)操作のやり方を紹介します。ここでもクイックスタートガイドで扱ったシンプルにしたbookstoreの例を使います。
このガイドを進まれる前に、インストールやクイックスタートガイドを済ませてPropelを使えるようにしておいてください。
作成 (CREATE)
データをデータベースに追加するにはPropelが生成したオブジェクトのインスタンスを作り、save()関数を呼びます。Propelはそのインスタンス化されたオブジェクトに合ったINSERT SQLを生成します。
簡単な挿入(INSERT)
新しい列を挿入(INSERT)する最もシンプルな例:
<?php /* Propelを初期化などなど */ $author = new Author(); $author->setFirstName("Jack"); $author->setLastName("London"); $author->save();
内部的にはこのコードは以下の様なSQLをデータベースに対して発行します。
INSERT INTO author (first_name, last_name) VALUES ('Jack', 'London');
関連した列の挿入
Propelは新規のオブジェクトに他の関連したオブジェクトを追加された場合、INSERT文を自動的にカスケード処理することで、関連した列への挿入を簡単に行います。この例では新規のAuthorとPublisherオブジェクトが作られ、それらが新規のBookオブジェクトに追加されています。この3つのオブジェクトはBook->save()関数が呼ばれたときに3つとも保存されます。
<?php /* Propelを初期化などなど */ // 1) Authorを作成 ('author'テーブル内の列) include_once 'bookstore/Author.php'; $author = new Author(); $author->setFirstName("Leo"); $author->setLastName("Tolstoy"); // 注:まだ''save()''を呼んで保存していません。 // 2) Publisherを作成 ('publisher'テーブル内の列) include_once 'bookstore/Publisher.php'; $pub = new Publisher(); $pub->setName("Viking Press"); // 注:まだ''save()''を呼んで保存していません。 // 3) Bookを作成 ('book'テーブル内の列) include_once 'bookstore/Book.php'; $book = new Book(); $book->setTitle("War & Peace"); $book->setIsbn("0140444173"); $book->setPublisher($pub); $book->setAuthor($author); $book->save(); // 3つオブジェクト全部を保存します!
Propelは各オブジェクトが保存されたかどうかを知っているため、Bookオブジェクトが追加される前に他のオブジェクトの保存も行っています。(関係ページに関係している列に関して詳しい説明があります。)
所得(RETRIEVE)
オブジェクトをデータベースから所得するのはhydratingとも呼ばれますが、基本的にはSELECTクエリをデータベースに対して発行し、返ってきた列を適当なオブジェクトの新規インスタンス内に格納することを指します。
Propelでは生成されたPeerクラスを使うことで、データベースから既に存在する列を所得します。Peerクラスは特定のテーブルに対する操作の為のスタティック関数だけを集めたクラスです。各テーブルに対していくつかの関数が必ず生成され、これらを使うことで1つはあるいは複数の行を所得することができます。
Primary Keyを使っての所得
一番シンプルな所得の方法はデータベースから1つだけのオブジェクト(列)を所得することで、これは生成されたretrieveByPK()関数を使って行います。この関数にprimary keyの値を渡すことでそれに合ったオブジェクトが返されます。
1行だけのPrimary Key
普通はテーブル内にPrimary Keyになり得る行が1つだけで存在するはずです。その場合はretrieveByPK()関数にパラメーターを1つだけ渡します。
<?php $firstBook = BookPeer::retrieveByPK(1); // ここで$firstBookがBookオブジェクトになるか、該当する列がない場合、NULLが返されます。
複数行に渡るPrimary Key
場合によってはprimary keyが複数行の組み合わせになっていることもありますね。そんな時はretrieveByPK()関数にprimary keyになり得る行にあたる複数のパラメーターを渡すことができるようになります。
例えば複数行に渡るprimary keyのあるテーブルがあった場合、この様に定義します:
<table name="multicolpk_example" phpName="MultiColPKExample">
<column name="id1" type="INTEGER" primaryKey="true"/>
<column name="id2" type="INTEGER" primaryKey="true"/>
<!-- その他の行 ... -->
</table>
... retrieveByPK()関数はこの様に呼び出します:
<?php $myObject = MultiColPKExamplePeer::retrieveByPK(1,2);
Primary Keyから複数のオブジェクトを所得
生成されたretrieveByPKs()関数を使ってprimary keyの配列を渡すことで、複数のPrimary Keyを使って複数のオブジェクトを所得することもできます。
<?php $selectedBooks = BookPeer::retrieveByPKs(array(1,2,3,4,5,6,7)); // $selectedBooksに複数のBookオブジェクトが入っています。
注:これは1行だけのPrimary Keyで定義されているテーブルにのみ有効です。
データベースへのクエリの発行
primary key以外を使って複数の列を所得するには2つの方法があります:1)PropelのCriteriaクラスを使う、2)カスタムSQLを書く。Criteriaクラスを使うと比較的シンプルにクエリを書けます。Criteriaクラスはデータベースの文法に依存せず、ロジックもシンプルにまとめれるため、ほとんどのクエリを書くのにはこちらを使います。しかし非常に複雑なクエリを書いてカスタムSQLを使い、Propelオブジェクトを所得する方が効率的なことが多いです。
シンプルなCriteria
以下にCriteriaを使って複数のオブジェクトを所得するシンプルな例をいくつか紹介します。
例1:Karlというfirst nameをもっているが、last nameがMarxでない全ての著者を見つける。
<?php $c = new Criteria(); $c->add(AuthorPeer::FIRST_NAME, "Karl"); $c->add(AuthorPeer::LAST_NAME, "Marx", Criteria::NOT_EQUAL); $authors = AuthorPeer::doSelect($c); // $authorsにはAuthorオブジェクトが入っています
... このようなSQLクエリが発行されます:
SELECT ... FROM author WHERE author.FIRST_NAME = 'Karl' AND author.LAST_NAME <> 'Marx';
例2:last nameがTolstoy, Dostoevsky, Bakhtinのどれかの著者全てを見つける
<?php $c = new Criteria(); $c->add(AuthorPeer::LAST_NAME, array("Tolstoy", "Dostoevsky", "Bakhtin"), Criteria::IN); $authors = AuthorPeer::doSelect($c); // $authorsにはAuthorオブジェクトが入っています
... このようなSQLクエリが発行されます:
SELECT ... FROM author WHERE author.LAST_NAME IN ('Tolstoy', 'Dostoevsky', 'Bakhtin');
複雑なロジックのCriteria
Criteria同士をロジック関係(AND, ORなど)でつなげたい場合は、各Criterionオブジェクトをつなげる必要があります。Criteria->add()関数を呼ぶことでCriterionオブジェクトが透過的にCriteriaオブジェクトに統合されます。
例1:first nameが"Leo"あるいはlast nameが"Tolstoy", "Dostoevsky", "Bakhtin"のどれかの全ての著者を見つけます。
<?php $c = new Criteria(); $cton1 = $c->getNewCriterion(AuthorPeer::FIRST_NAME, "Leo"); $cton2 = $c->getNewCriterion(AuthorPeer::LAST_NAME, array("Tolstoy", "Dostoevsky", "Bakhtin"), Criteria::IN); // つなげます $cton1->addOr($cton2); // Criteriaに追加 $c->add($cton1);
... このようなSQLクエリが発行されます:
SELECT ... FROM author WHERE (author.FIRST_NAME = 'Leo' OR author.LAST_NAME IN ('Tolstoy', 'Dostoevsky', 'Bakhtin'));
同じ行へのクエリはCriteriaショートカットを使うこともできます。
例2:first nameが'Leo'か'Karl'である全ての著者を見つける
明示的なCriterionを使った場合:
<?php $c = new Criteria(); $cton1 = $c->getNewCriterion(AuthorPeer::FIRST_NAME, "Leo"); $cton2 = $c->getNewCriterion(AuthorPeer::FIRST_NAME, "Karl"); // つなげます $cton1->addOr($cton2); // Criteriaに追加 $c->add($cton1);
ショートカットCriteria関数を使った場合:
<?php $c = new Criteria(); $c->add(AuthorPeer::FIRST_NAME, "Leo"); $c->addOr(AuthorPeer::FIRST_NAME, "Karl");
ショートカットCriteria関数には様々な制限があることに注意してください。ショートカット関数は同じ行を指している時にしか使えません。これらの関数を使って構文間の関係をつなげることはCriterion間のオブジェクトの関係を分かりにくいものにして、デバッグのしにくいクエリが生成されるため、この方法はお勧めしません。(Propel 2ではこれらの関数は変わるか、無くなるはずです。)
カスタムSQLを使う
Propelはあなたにやっかいな問題を起こすためにではなく、あなたを助けるためにあります。Criteriaを使って複雑なクエリを書こうとするとぐちゃぐちゃになってしまい、SQL文を直接書くより読みにくく、メンテナンスのしにくい代物になってします。Propelは各データベースでは差の無くすためのCriteriaがもたらす制限をとっぱらうためオブジェクトの所得の際にSQL文を使うことができるようになっています。
ということで、もう少しコードを追加するだけで、SQLを使ってデータベースからオブジェクトを所得することができるようになります。SQLを使ってデータベースに対してクエリを発行するにはPeerクラスにあるpopulateObjects()関数を使います。この関数は内部的にdoSelect()関数を呼びます。この関数にはCreoleの番号によりインデックス化されたResultSetオブジェクトが渡されます。(このインデックス化はexecuteQuery()関数内の!ResultSet::FETCHMODE_NUMオプションが有効にされていることで行われます。)
例1:サブクエリを使ってオブジェクトを所得
<?php $con = Propel::getConnection(DATABASE_NAME); $sql = "SELECT books.* FROM books WHERE NOT EXISTS (SELECT id FROM review WHERE book_id = book.id)"; $stmt = $con->prepare($sql); $stmt->execute(); $books = BookPeer::populateObjects($stmt);
カスタムSQLを使う場合は以下の点に注意してください:
- ResultSet行は数字でインデックスがついている必要があります
- ResultSetには全ての行を含める必要があります
- ResultSetはschema.xmlファイルにあるのと同じ順番に行がなっている必要があります
更新(UPDATE)
データベースの行を更新するには、まずオブジェクトを所得して、中身を変更し保存します。既に所得と作成のセクションでやったことを組み合わせればいいだけです。
<?php // 1) primary keyでオブジェクトを所得 $myBook = BookPeer::retrieveByPK(1); // 2) 値を更新して、保存する $myBook ->setTitle("War & Peace"); $myBook->save();
これで基本的にはおしまいです。もちろん作成のセクションであったのと同じ方法で関係している列を更新することも可能です。
<?php /* Propelの初期化などなど */ // 1) Authorの所得 $author = AuthorPeer::retrieveByPK(1); // 2) Bookの所得 $book = BookPeer::retrieveByPK(1); // 3) そして$authorを$bookのauthorに指定します。 $book->setAuthor($author); $book->save();
削除(DELETE)
オブジェクトを削除するにはPeerクラスかオブジェクトクラスを使います。
Peerを使い
作成されたdoDelete()を使うことで、テーブル内の行を削除することができます。この関数にprimary keyや適当なオブジェクトを渡すことができます。Criteriaオブジェクトを渡すこともできますが、primary keyでしか削除できないことを考えるとあまりいいやり方ではありません。
例1: primary keyを使っての削除
<?php BookPeer::doDelete(1);
例2: インスタンス化されたオブジェクトを使っての削除
<?php $book = BookPeer::retrieveByPK(1); BookPeer::doDelete($book);
オブジェクトを使う
他のC.R.U.D.オペレーショントンと統一性を保つため、オブジェクトクラスを使って行を削除することもできます。この方法を好む人もいますが、削除後にデーターベースに該当する列の無いオブジェクトが残ってしまうため、このやり方が"変だ"と思う人もいます。どちらの方法を使うかはあなた次第です。:)
$book = BookPeer::retrieveByPK(1); $book->delete(); // ($bookオブジェクトはこの先無効です)
