3

モデルのいくつかが hasMany/belongsTo 関連付けによってリンクされているアプリがあります。したがって、たとえば、A は B が多く、B は C が多く、C は多くが D であり、D は E が多くあります。また、E は D に属し、D は C に属し、C は B に属し、B は A に属します。各クエリで返される情報量を制御するのに最適ですが、テーブル D を含む条件を使用しているときにテーブル A からデータを取得しようとすると問題が発生するようです。たとえば、ここに私の 'A' の例を示します。モデル:

class A extends AppModel {
    var $name = 'A';

    var $hasMany = array(
        'B' => array('dependent' => true)
    );

    function findDependentOnE($condition) {
        return $this->find('all', array(
            'contain' => array(
                'B' => array(
                    'C' => array(
                        'D' => array(
                            'E' => array(
                                'conditions' => array(
                                    'E.myfield' => $some_value
                                )
                            )
                        )
                    )
                )
            )
        ));
    }
}

これでも「A」のすべてのレコードが返されます。関連する「E」レコードが条件を満たさない場合は、次のようになります。

Array(
    [0] => array(
        [A] => array(
            [field1] => // stuff
            [field2] => // more stuff
            // ...etc
        ),
        [B] => array(
            [field1] => // stuff
            [field2] => // more stuff
            // ...etc
        ),
        [C] => array(
            [field1] => // stuff
            [field2] => // more stuff
            // ...etc
        ),
        [D] => array(
            [field1] => // stuff
            [field2] => // more stuff
            // ...etc
        ),
        [E] => array( 
            // empty if 'E.myfield' != $some_value'
        )
    ),
    [1] => array( // ...etc )
)

If 'E.myfield' != $some_value の場合、レコードを返してほしくありません。

これが私の問題を十分に明確に表現していることを願っています...

基本的に、次のクエリが必要ですが、データベースに依存しない/CakePHP-y のような方法で:

SELECT * 
FROM A INNER JOIN
        (B INNER JOIN 
            (C INNER JOIN 
                (D INNER JOIN 
                    E ON D.id=E.d_id) 
                ON C.id=D.c_id) 
            ON B.id=C.b_id) 
        ON A.id=B.a_id 
    WHERE E.myfield = $some_value
4

1 に答える 1

2

あなたの問題は、 Containable の動作とcontainオプションの動作に関する誤解ですModel::find。最初のコードサンプルのModel::find呼び出しは、大まかに次のように変換されます。

すべての A を見つけます。次に、各 A に関連付けられたすべての B を見つけます。次に、各 B に関連付けられたすべての C を見つけます。次に、各 C に関連付けられたすべての D を見つけます。最後に、E の 1 つのフィールドが指定された値と一致する各 D に関連付けられているすべての E を検索します。

条件ステートメントは、D の結果のみをフィルター処理します。チェーンを C、B、A の順にたどるわけではありません。SQL ログをスキャンすると、containチェーンの各レベルを引き出す膨大な数のクエリが表示されます。

データベースから直接、CakePHP が希望どおりに結果を返すようにするにはhasOne、A と E の間の関連付けを構成する必要があります。説明したような長いチェーンでは、これはかなり扱いにくいかもしれません。次のようになります (読み取り: 未テスト):

$this->bindModel(array('hasOne'=>array(
    'B'=>array(
        'foreignKey' => false,
        'conditions' => array('A.id = B.a_id')
    ),
    'C'=>array(
        'foreignKey' => false,
        'conditions' => array('B.id = C.b_id')
    ),
    'D'=>array(
        'foreignKey' => false,
        'conditions' => array('C.id = D.c_id')
    ),
    'E'=>array(
        'foreignKey' => false,
        'conditions' => array('D.id = E.d_id')
    )
)));

$this->find('all', array(
    'conditions' => array( 'E.my_field' => $some_value )
));

E.my_value別の方法は、呼び出しから条件を完全に削除し、代わりに最後にModel::findかなり複雑な処理を実行することです。Set::extract

$results = $this->find('all', array(
    'contain' => array(
        'B' => array(
            'C' => array(
                'D' => array(
                    'E' => array()
                )
            )
        )
    )
));
return Set::extract("/A/B/C/D/E[my_field={$some_value}]/../../../../", $results);

Set::extractただし、特に多くの行を操作している場合は、パフォーマンスが深刻な問題になります。

編集Set::extract:この操作をスケーリングする必要がある場合、オプションがどれほどひどいアイデアであるかを強調したいだけです。これは、フィルタリングの負担全体をデータベース エンジンから PHP の配列関数に移します。

于 2010-03-25T16:11:32.520 に答える