7

報奨金:

良い解決策への+500repの報奨金。私は2週間、この壁に真剣に頭をぶつけて、助けの準備ができています。

テーブル/モデル(関連付けを表示するために簡略化)

  • ノード
    • id
    • 名前
    • node_type_id
  • node_associations
    • id
    • node_id
    • other_node_id
  • node_types
    • id
    • 名前

一般的なアイデア:

ユーザーはノードタイプを作成できます(例:「テレビ局」、「テレビ番組」、「俳優」など)。ノードタイプが何であるか、およびそれぞれの間の関連付けを事前に知っていれば、それらのモデルを作成するだけですが、ユーザーが必要なノードタイプを作成できるように、これを非常にオープンエンドにする必要があります。次に、(特定のノードタイプの)各ノードは、他のノードタイプの他のノードに関連付けることができます。

説明と私が試したこと:

すべてのノードは、他のすべてのノードに関連付けることができる必要があります。

私の仮定は、それを行うには、関連付けテーブルが必要であるということです。そのため、とを含む「node_associations」という名前のテーブルを作成node_idother_node_idました。

それから私は自分の協会を設立しました(hasManyを使って私は信じています):(以下は私の設立の私の最高の回想です...それは少しずれているかもしれません)

//Node model
public $hasMany = array(
    'Node' => array(
        'className' => 'NodeAssociation',
        'foreignKey' => 'node_id'

    ),
    'OtherNode' => array(
        'className' => 'NodeAssociation',
        'foreignKey' => 'other_node_id'
    )
);

//NodeAssociation model
public $belongsTo = array(
    'Node' => array(
        'className' => 'Node',
        'foreignKey' => 'node_id'

    ),
    'OtherNode' => array(
        'className' => 'Node',
        'foreignKey' => 'other_node_id'
    )
);

最初、私はそれを持っていると思いました-これは理にかなっています。しかし、それから私はデータを取得しようとし始め、過去2週間壁に頭をぶつけてきました。

問題の例:

次のノードがあるとしましょう。

  • NBC
  • ER
  • ジョージ・クルーニー
  • アンソニー・エドワーズ
  • 今夜のショー:レノ
  • ジェイ・レノ
  • ファミリーガイ

すべてのテレビ局をプルし、(例として)俳優を含むテレビ番組を含めることができるようにデータ構造を設定するにはどうすればよいですか?これは、通常のモデル設定では簡単です。

$this->TvStation->find('all', array(
    'contain' => array(
        'TvShow' => array(
            'Actor'
        )
    )
));

そして、多分私はすべての男性俳優を検索し、テレビ局を含むテレビ番組を含めたいと思います。または、午後9時に開始し、俳優と放送局が含まれるテレビ番組...など。

しかし、HABTMまたはHasMany Through self(さらに重要なことに、未知のデータセット)では、モデルがどのフィールド(node_idまたはother_node_id)であるかわからず、全体として、どのように取得するかについて頭を悩ませることはできません。コンテンツ。

4

4 に答える 4

2

アイデア

これを慣例で解決してみましょう。node_idはアルファベット順で最初に来るモデルになり、other_node_idは2番目に来るモデルになります。

含まれているモデルごとに、ノードクラスへのオンザフライでHABTMアソシエーションを作成し、各アソシエーションのエイリアスを作成します(bindNodesおよびbindNodeメソッドを参照)。

クエリを実行する各テーブルに条件を追加してnode_type_id、そのタイプのノードの結果のみを返します。NodeTypeのIDはを介して選択されgetNodeTypeId()、キャッシュする必要があります。

深く関連するアソシエーションで条件を使用して結果をフィルタリングするには、手動で追加の結合を追加し、一意のエイリアスを使用して結合可能なものごとに結合を作成し、条件を適用できるように各ノードタイプ自体をエイリアスで結合する必要があります(例:選択アクターx)を持つすべてのTvChannel。Nodeクラスでこのためのヘルパーメソッドを作成します。

ノート

私は自分のデモのforeignKeyためにnode_idそしてassociationForeignKeyのために使用しました。other_node_id

ノード(不完全)

<?php
/**
 * @property Model NodeType
 */
class Node extends AppModel {

    public $useTable = 'nodes';

    public $belongsTo = [
        'NodeType',
    ];

    public function findNodes($type = 'first', $query = []) {
        $node = ClassRegistry::init(['class' => 'Node', 'alias' => $query['node']]);
        return $node->find($type, $query);
    }

    // TODO: cache this
    public function nodeTypeId($name = null) {
        if ($name === null) {
            $name = $this->alias;
        }
        return $this->NodeType->field('id', ['name' => $name]);
    }

    public function find($type = 'first', $query = []) {
        $query = array_merge_recursive($query, ['conditions' => ["{$this->alias}.node_type_id" => $this->nodeTypeId()]]);
        if (!empty($query['contain'])) {
            $query['contain'] = $this->bindNodes($query['contain']);
        }
        return parent::find($type, $query);
    }

    // could be done better    
    public function bindNodes($contain) {
        $parsed = [];
        foreach($contain as $assoc => $deeperAssoc) {
            if (is_numeric($assoc)) {
                $assoc = $deeperAssoc;
                $deeperAssoc = [];
            }
            if (in_array($assoc, ['conditions', 'order', 'offset', 'limit', 'fields'])) {
                continue;
            }
            $parsed[$assoc] = array_merge_recursive($deeperAssoc, [
                'conditions' => [
                    "{$assoc}.node_type_id" => $this->nodeTypeId($assoc),
                ],
            ]);
            $this->bindNode($assoc);
            if (!empty($deeperAssoc)) {
                $parsed[$assoc] = array_merge($parsed[$assoc], $this->{$assoc}->bindNodes($deeperAssoc));
                foreach($parsed[$assoc] as $k => $v) {
                    if (is_numeric($k)) {
                        unset($parsed[$assoc][$k]);
                    }
                }
            }
        }
        return $parsed;
    }

    public function bindNode($alias) {
        $models = [$this->alias, $alias];
        sort($models);
        $this->bindModel(array(
            'hasAndBelongsToMany' => array(
                $alias => array(
                    'className' => 'Node',
                    'foreignKey' => ($models[0] === $this->alias) ? 'foreignKey' : 'associationForeignKey',
                    'associationForeignKey' => ($models[0] === $alias) ? 'foreignKey' : 'associationForeignKey',
                    'joinTable' => 'node_associations',
                )
            )
        ), false);
    }

}

$results = $this->Node->findNodes('all', [
    'node' => 'TvStation', // the top-level node to fetch
    'contain' => [         // all child associated nodes to fetch
        'TvShow' => [
            'Actor',
        ]
    ],
]);
于 2012-09-01T06:19:34.273 に答える
1

モデル間の関係が間違っていると思います。私はそれで十分だと思います:

// Node Model
public $hasAdBelongsToMany = array(
    'AssociatedNode' => array(
        'className' => 'Node',
        'foreignKey' => 'node_id'
        'associationForeignKey' => 'associated_node_id',
        'joinTable' => 'nodes_nodes'
    )
);

//テーブル

ノード

  • id
  • 名前
  • node_type_id

ノード_ノード

  • id
  • node_id
  • Associated_node_id

node_types

  • id
  • 名前

次に、ContainableBehaviorを使用してデータをフェッチしてみてください。たとえば、TVStationに属するすべてのTVShowを検索するには、次のようにします。

$options = array(
    'contain' => array(
        'AssociatedNode' => array(
            'conditions' => array(
                'AssociatedNode.node_type_id' => $id_of_tvshows_type
            )
        )
    ),
    conditions => array(
        'node_type_id' => $id_of_tvstations_type
    )
);
$nodes = $this->Node->find('all', $options);

編集 :

第2レベルの条件を設定することもできます(このセクションの最後の例を参照してください。「タグ」モデルの条件を確認してください)。これを試して:

$options = array(
    'contain' => array(
        'AssociatedNode' => array(
            'conditions' => array(
                'AssociatedNode.node_type_id' => $id_of_tvshows_type
            ),
            'AssociatedNode' => array(
                'conditions' => array( 'AssociatedNode.type_id' => $id_of_actors_type)
            )
        )
    ),
    conditions => array(
        'node_type_id' => $id_of_tvstations_type
    )
);
$nodes = $this->Node->find('all', $options);
于 2012-09-01T18:57:17.000 に答える
1

残念ながら、問題の一部は、ソリューションにユーザーデータをコードに含める必要があることだと思います。すべてのノードタイプはユーザーデータであるため、それらが無限に存在する可能性があるため、アプリケーションのクラスメソッドとしてそれらを使用しようとしないようにする必要があります。代わりに、必要なデータ操作をモデル化するメソッドを作成してみます。

提供されているデータモデルに見られる省略の1つは、タイプ間の関係を記録する方法です。あなたの例では、TvStation-> TvShows-> Actorなどの関係について言及していますが、これらのデータ関係はどこで定義/保存されていますか?すべてのノードタイプがユーザー定義のデータであるため、これらの関係をどこかにレコードストアする必要があると思います。node_typesには、特定のタイプの有効な子タイプまたは目的の子タイプに関する追加のメタデータが必要なようです。これをどこかに記録しておくと、クエリを作成するときの状況が少し簡単になる可能性があります。データベースに尋ねるすべての質問やクエリについて考えると役立つ場合があります。データベースにあるデータでこれらすべての質問に答えられない場合は、おそらくいくつかのテーブルが欠落しています。モデルの関連付けは、テーブルにすでに存在するデータリレーションの単なるプロキシです。ギャップがある場合は、データモデルにギャップがある可能性があります。

これがあなたが探している答えではないと思いますが、うまくいけば、それはあなたが正しいものを見つけるのに役立つでしょう。

于 2012-09-01T23:17:02.470 に答える
-1

ノードモデルでメソッドを作成してみませんか?

何かのようなもの:

    <?php 
        // first argument is a nested array filled with  integers 
(corresponding to node_type_id)
        //second one id of a node
    //third one corresponds to the data you want(empty at beginning in most case)
    public function custom_find($conditions,$id,&$array){

        //there may several type of nodes wanted: for instances actors and director of a serie, so we loop
        foreach($conditions as $key_condition=>$condition){

            //test to know if we have reached the 'bottom' of the nested array: if yes it will be an integer '2', if no it will be an array like '2'=>array(...)
            if(is_array($condition))){
                   //this is the case where there is deeper levels remaining

                        //a find request: we ask for the node defined by its id,
 //and the child nodes constrained by their type: ex: all actors of "Breaking Bad"
                        $this->id=$id;
                $result=$this->find('all',array(
                        'contain' => array(
                                'OtherNode' => array(
                                        'conditions'=>array('node_type_id'=>$key_condition)
                                )
                        )
                )
             );

                //we add to $array the nodes found. Ex: we add all the actors of the serie, with type_id as key
                        $array[$key_condition]=$result['OtherNode'];

                         //Then  on each node we just defined we call the function recursively. Note it's $condition not $conditions
                foreach($array[$key_condition] as &$value){
                    $this->custom_find($condition,$value['Node']['id'],$value);
                }

            }else{
                //if we simply add data
                        $this->id=$id;
                $result=$this->find('all',array(
                        'contain' => array(
                                'OtherNode' => array(
                                        'conditions'=>array('node_type_id'=>$value)
                                )
                        )
                )
             );

             $array[$condition]=$result['OtherNode'];
            }

        }



    }

そのコードはほぼ間違いなく間違っています。それは私が何を意味するのかをあなたに理解させるためだけのものです。

編集:

内容:

これは、条件のネストされた配列とノードのIDを受け取り、ノードのネストされた配列を返す再帰関数です。

例:$ conditions = array( '2'、 '4' => array( '5'、 '6' => array( '4')))

使い方:

単一のノードの場合、配列内の条件に対応するすべての子ノードが返されます。次に、条件が1レベル深い子に対して、レベルがなくなるまで同じことを行います。

于 2012-09-01T18:02:49.677 に答える