使用树模型计算关联记录(全深度)

时间:2011-04-25 19:28:41

标签: cakephp tree count descendant

我们有两个型号。电子书HABTM标签,其中标签遵循​​树行为。

对于每个标签,我们需要两个数字。首先,与标签相关的电子书数量,其次是与标签相关的电子书数量+每个后代的相关电子书数量。

我们如何在树形格式的数组中获取带有这些数字的标签?

非常感谢您的帮助。

更新:有一个日期时间参数Ebook.published,它定义了书籍的计算时间。所有具有codeEbook.published< NOW()应该计算在内。

2 个答案:

答案 0 :(得分:1)

Cake对此没有基本的支持。您需要动态进行计算或使用自定义代码创建自己的计数器缓存以进行更新。这太乱了。

我建议在Ebooks控制器中覆盖beforeSave()和afterSave()函数。如果要更新,请在beforeSave()中获取与Ebook关联的当前现有标签集。在afterSave()中获取新的标记集并将其与前一个集合合并。如果有任何更改,请遍历所有标记并调用$this->Tag->getPath($id)以获取所有祖先的列表。您现在将拥有受保存影响的所有标记。您现在可以遍历它们并更新计数。

答案 1 :(得分:0)

实际上,我找到了一个更简单的解决方案,因为我已经构建了返回标签树的函数。我使用每个标签的查询来获得该时刻的实际计数。这就是我的构建。您可以根据自己的需要随意使用它。

在Tag模型中

// Returns the number of published ebooks of selected tag
function count_direct_published($tag_id = 0){
    $temp = $this->query('SELECT count(*) as count FROM ebooks_tags LEFT JOIN ebooks ON ebooks_tags.ebook_id = ebooks.id WHERE ebooks.published < NOW() AND ebooks_tags.tag_id = '.$tag_id);
    return $temp[0][0]['count'];
}


// Returns an array in tree format with $id tag and all his children
// $id = 0 start from the top (parent_id = null), or, from $id = the top's tag id
// $limit = boolean (default false)
// $level = Is the limit of depth applied only if $limit = true
// $ext = true Means this is the first time the function is called
// You can run tree_builder(), returns all the tree,
function tree_builder($id = 0, $limit = false, $level = 1, $ext = 1){
    if($ext == 1){
        $ext = 0;
        $undo = true; 
    }else{
        $undo = false;
    }
$this->recursive=-1;

    $this->contain('EbooksTag');

    $var = array();
    $count_all = 0;
    // If limit = too big , exit
    if($limit !== false && $level > $limit){
        return '';
    }
    // Or else, 
    // If $id=0, find all the children
    if($id == 0){
        $tags = $this->find('all',array('conditions'=>array( 'Tag.parent_id IS NULL'), 'order'=>array('Tag.gre')));
    // If $id!=0 && runs internally
    }elseif($id != 0 && !$undo ){
        $tags = $this->find('all',array('conditions'=>array( 'Tag.parent_id'=>$id ), 'order'=>array('Tag.gre')));
    }
    // If $id!=0 && is called from outside 
    elseif($id != 0 && $undo){
        $tags = $this->find('all',array('conditions'=>array( 'Tag.id'=>$id )));

    }

    foreach($tags as $key => $tag){
        $var[] = $tag;
        $next = $this->tree_builder($tag['Tag']['id'], $limit, $level+1, $ext);
        end($var); // move the internal pointer to the end of the array
        $last_key = key($var); // fetches the key of the element pointed to by the internal pointer
        $var[$last_key]['children'] = $next['var'];
        $counter_direct = $this->count_direct_published($id);
        $var[$last_key]['Tag']['count_all'] = $next['count_all']+$counter_direct;
        $count_all += $var[$last_key]['Tag']['count_all'];

    }

    if( $undo )
    {
        return $var;
    }else{
        return array('count_all'=> $count_all, 'var' => $var);
    }

}

在tags_controller.php中

$this->set('tags', $this->Tag->tree_builder());

在视图中

<?php foreach($tags as $tag){?>
    <?php // Ο Γονέας σε dropdown box ?>
    <div class="main-categ">
    <?php echo $tag['Tag']['gre']; ?>
    <?php echo $html->image('layout/arrows.png', array('alt'=> "Expand")); ?>
    </div>



    <div class="collapse"> 
        <?php // Τα στοιχεία του γονέα ?>
        <div class="tag-1">
            <span class="tag-1">
                <?php // Αν ?>
                <?php if($tag['Tag']['count_direct']>0){
                    // Display link
                     echo $html->link($tag['Tag']['gre'],array('action'=>'view',$tag['Tag']['id']));
                     echo ' ('.$tag['Tag']['count_direct'].')';
                }else{
                    // Display text
                     echo $tag['Tag']['gre'];

                }  ?>
            </span> 
            &nbsp;&nbsp;&nbsp;&nbsp;
            <?php echo $html->link( 'view all' ,array('action'=>'view_all',$tag['Tag']['id'])); ?>
             (<?php echo $tag['Tag']['count_all']; ?>)
        </div>  

        <?php // Για κάθε πρώτο παιδί ?>
        <?php foreach($tag['children'] as $tag_1){ ?>
        <div>
            <span class="tag-2">
                <?php if($tag_1['Tag']['count_direct']>0){
                    // Display link
                     echo $html->link($tag_1['Tag']['gre'],array('action'=>'view',$tag_1['Tag']['id']));
                     echo ' ('.$tag_1['Tag']['count_direct'].')';
                }else{
                    // Display text
                     echo $tag_1['Tag']['gre'];

                }  ?>
            </span>
            &nbsp;&nbsp;&nbsp;&nbsp; 
            <?php echo $html->link( 'view all' ,array('action'=>'view_all',$tag_1['Tag']['id'])); ?>
             (<?php echo $tag_1['Tag']['count_all']; ?>)

                <?php // Τα δεύτερα παιδιά ?>
                <?php $i=0; ?>
                <?php foreach($tag_1['children'] as $tag_2){ ?>
                    <?php if($i==0){ echo '<ul class="split">'; $i++; } ?>
                    <li>
                    <?php if($tag_2['Tag']['count_direct']>0){
                            // Display link
                             echo $html->link($tag_2['Tag']['gre'],array('action'=>'view',$tag_2['Tag']['id']));
                             echo ' ('.$tag_2['Tag']['count_direct'].')';
                        }else{
                            // Display text
                             echo $tag_2['Tag']['gre'];

                        }  ?>                       
                    </li>
                <?php } ?>
                <?php if($i==1) echo '</ul>'; ?>

            <div class="clear"></div>       
        </div>

        <?php } ?>      

    </div>

也许它不是最好的解决方案,但它有效。希望有所帮助