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

发布于 2024-11-03 00:51:26 字数 266 浏览 7 评论 0原文

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

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

我们如何以树形格式的数组形式获取包含这些数字的标签?

非常感谢您的帮助。

更新:有一个日期时间参数 Ebook.published 定义何时对书籍进行计数或不计数。所有具有 codeEbook.published < 的电子书NOW() 应该被计算在内。

We have two models. Ebooks HABTM Tags, where tags follows the tree behavior.

For each tag we need two numbers. First, the number of ebooks associated to the tag, and secondly the number of ebooks associated to the tag + the number of associated ebooks for each descendant.

How can we get the tags with these numbers in an array in tree format?

Thank you very much for any help.

Update: There is a datetime parameter Ebook.published which defines when the book is to be counted or not. All the ebooks that have codeEbook.published < NOW() should be counted.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

独闯女儿国 2024-11-10 00:51:26

Cake对此没有基本支持。您将需要即时进行计算或使用要更新的自定义代码创建自己的计数器缓存。这很混乱。

我建议重写电子书控制器中的 beforeSave() 和 afterSave() 函数。如果进行更新,请在 beforeSave() 中获取与电子书关联的当前现有标签集。在 afterSave() 中获取新的标签集并将其与前一组标签合并。如果有任何更改,请迭代所有标签并调用 $this->Tag->getPath($id) 以获取所有祖先的列表。您现在将拥有受保存影响的所有标签。您现在可以迭代它们并更新计数。

Cake has no basic support for this. You will need to do the calculations on the fly or create your own counter cache with custom code to update. This is messy.

I'd suggest overriding the beforeSave() and afterSave() function in your Ebooks controller. If updating, grab the current existing set of tags associated with the Ebook in your beforeSave(). In the afterSave() grab the new set of tags and merge it with the previous set. If there are any changes, iterate through all the tags and call $this->Tag->getPath($id) to get a list of all the ancestors. You'll now have all the tags that were affected by the save. You can now iterate through them and update the counts.

谁对谁错谁最难过 2024-11-10 00:51:26

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

在标签模型中

// 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> 
                
            <?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>
                 
            <?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>

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

Actually, I found a simpler solution since I already have build the function that returns the tags tree. I used a query for each tag to get the actual count of that moment. Here is what I 've build. Feel free to use it according your needs.

In Tag model

// 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);
    }

}

In tags_controller.php

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

In the view

<?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> 
                
            <?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>
                 
            <?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>

Perhaps its not the best solution but it works. Hope that helps

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文