4

一部のサイトにユーザー定義のブックマークを保存するために、複合キーを持つテーブルがあります。

CREATE TABLE bookmarks (
    user_id int not null,
    book_id int not null,
    page_id int,
    ...
);
CREATE UNIQUE INDEX ON bookmarks(user_id, book_id, page_id);

page_id は NULL にできますが、user_id と book_id はできないことに注意してください。page_id が null の場合、ブックマークはブック全体に設定されます。それ以外の場合は、特定のページに設定されます。

対応する ActiveRecord クラスは、いくつかの関係を定義します:

public function relations() {
    return array(
        "user"  => array(self::BELONGS_TO, "User",     "user_id"),
        "book"  => array(self::BELONGS_TO, "Book",     "book_id"),
        "page"  => array(self::BELONGS_TO, "Page",     "page_id"),
    );
}

そして primaryKey() メソッド::

public function primaryKey() {
    return array("user_id", "book_id", "orig_id");
}

ここで、一部のユーザーの本全体のすべてのブックマークを取得したいと考えています。私もです:

$bookmarks = Bookmark::model()->findAll(array(
    "condition" => "t.user_id = :user_id AND t.page_id IS NULL",
    "params" => array(":user_id" => 1),
));

それはうまく機能し、4 つのレコードを返しますが、明らかに、books テーブルからいくつかの関連データを使用したいと考えています。

$bookmarks = Bookmark::model()->findAll(array(
    "with" => "book",
    "condition" => "t.user_id = :user_id AND t.page_id IS NULL",
    "params" => array(":user_id" => 1),
));

生成された SQL ステートメントは必要なすべてのデータを選択しますが、CActiveRecord クラスによって認識されません。もう 1 つの奇妙な点は、すべてのページ ブックマークを取得しようとすると、すべて問題ないことです。

$bookmarks = Bookmark::model()->findAll(array(
    "with" => "book",
    "condition" => "t.user_id = :user_id AND t.page_id IS NOT NULL",
    "params" => array(":user_id" => 1),
));

私は何を間違っていますか?2 番目の例の式でデータを返す方法を教えてください。PHP 5.4.0、Yii 1.1.8、PostgreSQL 9.1.4、+32°C 外。

4

1 に答える 1

3

問題は次の方法で解決できます。

  1. 代理 PKBookmarkテーブルに追加します (自動インクリメンタル シーケンスなど)。

  2. 機能を削除primaryKey()します。

より便利なコードを使用することもできます:

public function relations()
{
    return array(
        //...
        'wholeBook' => array(self::BELONGS_TO, 'Book', 'book_id', 'on'=>"page_id IS NULL", 'joinType'=>'INNER JOIN'),
        //...
    );
}

次に、コントローラーで次のようにします。

$bookmarks = Bookmark::model()->with('wholeBook')->findAllByAttributes(array('user_id'=>1));

実際のところ、PRIMARY の代わりに UNIQUE キーprimaryKey()を使用し、ハックを使用して、複合 PK で NULL 列を使用できないことを回避します。ActiveRecord は一種の ORM であるため、SQL のロジックはすべて AR に変換する必要があり (Yii は DB スキーマを自動的にロードします)、正しいロジックでなければなりません。

私があなただったら、SQL を次のように正規化します。 正規化された DB 構造

そのため、異なる関係を持つ 2 つの異なる種類のブックマークがあります。リレーション構造ではなく、ビュー ロジックでのみ結合する必要があります。私見では

于 2012-08-07T15:10:03.800 に答える