2

NodesメインモデルとしてCMS を構築しようとしています。各Node belongsToa NodeType、および everyNodeは、 any/every other に関連付けることができますNode

だから-これはHABTMが必要だと思った:

//Node model
public $hasAndBelongsToMany = array(
    'AssociatedNode' => array(
        'className' => 'Node',
        'foreignKey' => 'node_id',
        'associationForeignKey' => 'associated_node_id',
        'joinTable' => 'node_associations'
    )
);

問題は、それが機能する唯一の方法は、関連付けごとに2つの行がある場合です。

関連行が 1 つだけの例:

ノード

  • 小胞体 (id=1)
  • ジョージ・クルーニー (id=2)

これら 2 つのノード間の関係を説明する結合テーブル内の 1 つの行:

  • 'node_id' = 1
  • 'associated_node_id' = 2

今 - TV Shows と Contain をクエリすると、それは Actor ノードです:

$nodes = $this->Node->find('all', array(
        'conditions' => array(
            'Node.node_type_id' => '645' //tv shows
        ),
        'contain' => array(
            'AssociatedNode' => array(
                'conditions' => array(
                    'AssociatedNode.node_type_id' => '239' //actors
                ),
            )
        )
    ));

これは機能し、私は ER を取得します -> ジョージ クルーニー。

しかし、ジョージ・クルーニーが出演しているすべてのショーをプルしたい場合はどうすればよいでしょうか?

$nodes = $this->Node->find('all', array(
        'conditions' => array(
            'Node.node_type_id' => '239' //actors
        ),
        'contain' => array(
            'AssociatedNode' => array(
                'conditions' => array(
                    'AssociatedNode.node_type_id' => '645' //tv shows
                ),
            )
        )
    ));

これは、George Clooney の ID が「node_id」フィールドにあり、ER の ID が「associated_node_id」フィールドにあることを探しているため、機能しません。実際には逆になっています。

私が考えた唯一の解決策は、すべての関連付けに対して2行を保持することです。しかし、これはやり過ぎのようです。しかし、関連付けが保存または削除されるたびに、各複製を他の複製と確実に同期させる何らかのカスタムを考え出す必要があります...など-これはワームの大きな缶のようです。

足りないものはありますか?

4

3 に答える 3

3

おそらくカスタム クエリを使用することもできますが、標準の Cake 関数を維持するために、ノード間の 2 つの関係を宣言することが考えられます。

public $hasAndBelongsToMany = array(
  'AssociatedNode1' => array(
      'className' => 'Node',
      'foreignKey' => 'node_id',
      'associationForeignKey' => 'associated_node_id',
      'joinTable' => 'node_associations'
  ),
  'AssociatedNode2' => array(
      'className' => 'Node',
      'foreignKey' => 'associated_node_id',
      'associationForeignKey' => 'node_id',
      'joinTable' => 'node_associations'
  )
);

その後、 afterFind コールバックで両方の配列をマージできます。

function afterFind($results)
{
  foreach($results as &$result)
  {
    if(isset($result['AssociatedNode1']) || isset($result['AssociatedNode2']))
    {
      $associated_nodes = array();

      if(isset($result['AssociatedNode1']))
      {
        foreach($result['AssociatedNode1'] as $associated_node)
        {
          $associated_nodes[] = $associated_node;
        }
      }

      if(isset($result['AssociatedNode2']))
      {
        foreach($result['AssociatedNode2'] as $associated_node)
        {
          $associated_nodes[] = $associated_node;
        }
      }

      $result['AssociatedNode'] = $associated_nodes;
    }
  }
  unset($result);

  return $results;
}

ただし、これにより、contain(); の呼び出しで AssociatedNode1 と AssociatedNode2 の両方を宣言する必要があります。

于 2012-09-17T12:51:16.810 に答える
1

ユースケースの詳細はわかりませんが、いくつかの代替オプションがあります。

ツリービヘイビアの使用を検討するかもしれません-これは、ツリーに物事を格納するために構築されており、あなたがしていることのように聞こえます。私自身は使ったことがないので、あなたの使い方にどれだけ当てはまるかわかりません。

一方、関係を一貫した方向(つまり、常にTV Show-> Actor)に保存し、クエリが実行される方向を知っている場合(TV Showsのツリーを検索するのに対して、俳優を検索するのではなく、テレビ番組)、逆方向に進んでいるときに、AssociatedNodeにクエリを実行できるはずです。

$nodes = $this->AssociatedNode->find('all', array(
    'conditions' => array(
        'AssociatedNode.node_type_id' => '239' //actors
    ),
    'contain' => array(
        'Node' => array(
            'conditions' => array(
                'Node.node_type_id' => '645' //tv shows
            ),
        )
    )
));

この場合、わかりやすくするために、「AssociatedNode」ではなく「ChildNode」を使用する方がよい場合があります。

しかし、繰り返しになりますが、これらの答えはどちらもユースケースの詳細によって異なります。nIcOの答えは一般的な解決策として適しています。それは(必然的に)厄介で、おそらく遅いですが、それは厄介さをうまく抽象化します。

于 2012-09-19T22:59:39.887 に答える
0

私が過去に行ったことの 1 つは、結合テーブルのモデルを作成することです。そこに余分なデータを保存し、クエリでやりたいことを何でもすることができました。次に、その結​​合モデルの両側で、hasMany アソシエーションを定義します (beensTo も同様です)。次に、結合モデルを使用して検索を実行し、(コントローラーから) 次のように記述できます。

$this->Node->NodesNode->find('all', array('conditions'=>array("or"=>array('node_id'=>$id,'sub_node_id'=>$id))));

私見:ケーキの慣例を使用することを実際に強制するものは何もありません。私はケーキが大好きですが、ケーキと ORM の両方が非常に簡単なことを複雑にすることがあります。独自のクエリを作成し、結果を自分で解析したいだけかもしれません。おそらく、別の動作やモデルのオーバーヘッドを処理するよりも高速であり、さらに、指定されたデフォルトよりもはるかに優れたクエリを作成できる可能性があります。

最後に、1 つのモデルを複数の目的で使用している場合は注意してください。その 1 つのモデルが本当にすべてをサポートする必要があるかどうかをよく考えてください。それを行ったときはいつでも、1年か2年以内に全体を書き直しただけであることがわかりました。一部のノードがこのように追加の動作を必要とし、他のノードが何か他のものを必要とし、if ステートメント (またはおそらくもっと賢い何か) がいたるところに散らばっているボトルネックにすぐにぶつかります。さらに、データベースでクレイジーなツリーベースのクエリを実行すると、処理が本当に遅くなります。

于 2012-09-21T19:50:08.727 に答える