1

CakePHP フレームワーク内で、私が長い間問題を抱えていたことの 1 つは、2 つのモデル間の同時hasOneおよびhasMany関係を定義することです。例えば:

BlogEntry hasMany Comment
BlogEntry hasOne MostRecentComment(最新のフィールドのMostRecentCommentはどこですか)Commentcreated

BlogEntry モデル プロパティでこれらの関係を定義するのは問題があります。CakePHP の ORM は has-one 関係を として実装しているINNER JOINため、複数の Comment があるとすぐに、BlogEntry::find('all')呼び出しは BlogEntry ごとに複数の結果を返します。

過去にいくつかの方法でこれらの状況を回避してきました。

  1. モデル コールバックを使用して (場合によっては、コントローラーやビューでも!)、MostRecentComment を次のようにシミュレートしまし
    $this->data['MostRecentComment'] = $this->data['Comment'][0];
    Comment.created。また、MostRecentComment フィールドでソートする Cake の組み込みのページネーション機能もありません (たとえば、BlogEntry の結果をMostRecentComment.created.

  2. 追加の外部キーの維持、BlogEntry.most_recent_comment_id. これは維持するのが面倒で、Cake の ORM を壊しBlogEntry belongsTo MostRecentCommentます。それは機能しますが、見た目が...間違っています。

これらの解決策には多くの要望が残されていたので、先日この問題に腰を下ろし、より良い解決策に取り組みました。私は最終的な解決策を以下に投稿しましたが、これまでずっと私を逃れてきた驚くほど単純な解決策があることを発見すると、わくわくします (そしておそらく少し悔やまれます)。または、私の基準を満たす他のソリューション:

  • レベルで MostRecentComment フィールドで並べ替えることができる必要がありModel::findます (つまり、結果のメッセージだけではありません)。
  • commentsまたはblog_entriesテーブルにフィールドを追加する必要はありません。
  • CakePHP ORM の「精神」を尊重する必要があります。

(この質問のタイトルが可能な限り簡潔/有益であるかどうかもわかりません。)

4

1 に答える 1

0

私が開発したソリューションは次のとおりです。

class BlogEntry extends AppModel
{
    var $hasMany = array( 'Comment' );

    function beforeFind( $queryData )
    {
        $this->_bindMostRecentComment();

        return $queryData;
    }

    function _bindMostRecentComment()
    {
        if ( isset($this->hasOne['MostRecentComment'])) { return; }

        $dbo = $this->Comment->getDatasource();
        $subQuery = String::insert("`MostRecentComment`.`id` = (:q)", array(
            'q'=>$dbo->buildStatement(array(
                'fields' => array( String::insert(':sqInnerComment:eq.:sqid:eq', array('sq'=>$dbo->startQuote, 'eq'=>$dbo->endQuote))),
                'table'  => $dbo->fullTableName($this->Comment),
                'alias'  => 'InnerComment',
                'limit'  => 1,
                'order'  => array('InnerComment.created'=>'DESC'),
                'group'  => null,
                'conditions' => array(
                    'InnerComment.blog_entry_id = BlogEntry.id'
                )
            ), $this->Comment)
        ));

        $this->bindModel(array('hasOne'=>array(
            'MostRecentComment'=>array(
                'className' => 'Comment',
                'conditions' => array( $subQuery )
            )
        )),false);

        return;
    }

    // Other model stuff
}

概念は単純です。この_bindMostRecentCommentメソッドは、関連付け条件でサブクエリを使用して、最新のコメントのみが BlogEntry クエリに結合されるようにする、かなり標準的な has-one 関連付けを定義します。メソッド自体は呼び出しの直前に呼び出されModel::find()、各 BlogEntry の MostRecentComment をフィルター処理または並べ替えできます。

この関連付けをクラス メンバーで定義できることはhasOneわかっていますが、生の SQL を大量に作成する必要があり、一時停止します。

BlogEntry のコンストラクターから呼び出すことをお勧めします_bindMostRecentCommentが、(ドキュメントによると) バインディングを永続的にする Model::bindModel() パラメータが機能していないように見えるため、バインディングは beforeFind コールバックで行う必要があります。

于 2010-03-24T18:40:56.863 に答える