Laravel 嵌套子项计数(类别)

Laravel Nested Children Count (Categories)

我在 Laravel 网站上使用嵌套类别,并采用相邻列表样式 mysql table 布局。最多有 7 层分类,(使用 google categories taxonomy)。请查看下图的一小部分数据样本,以及我的类别模型关系的代码。

每个类别都有零到多个子类别(使用相同的类别模型),以及它下面的零到多个页面。 我的目标是找到一种方法来计算某个类别下的所有页面(包括其子类别下的页面)。

我的类别模型具有以下所有功能正常的关系:

public function pages() {
    return $this->hasMany('App\Models\Page');
}

public function children() {
    return $this->hasMany('App\Models\Category', 'parent_id' );
}

public function parent() {
    return $this->belongsTo('App\Models\Category', 'parent_id' );
}

public function childrenRecursive() {
   return $this->children()->orderBy( 'name' )->with('childrenRecursive', 'pages');
}

public function parentRecursive() {
   return $this->parent()->with('parentRecursive');
}

这不是我使用关系的方式,但偷偷摸摸的方式是:

public function childCount(){
  return DB::table('categories')->where('slug', 'LIKE', $this->slug . '%')->count() - 1;
}

要获取页面,可能:

public function pageCount(){
  return Page::whereIn('category_id', 
     DB::table('categories')->select('id')->where('slug', 'LIKE', $this->slug . '%')->get()->pluck('id')
  )->count();
}

2 个查询可能会被优化,但会起作用

因为我急于使用以下代码行和我的类别模型在第一个位置加载所有这些类别和页面:

$categories = Category::with( 'childrenRecursive' )->whereNull( 'parent_id' )->get();

我能够使用递归公式在不减慢页面速度的情况下总结每个类别下的页面数:

foreach( $categories as $category ) {
    $category->pagesCount = 0;
    $category->pagesCount += $category->pages->count();
    foreach( $category->childrenRecursive as $child ) {
        $category->pagesCount += countChildPages( $child );
    }
}

function countChildPages( $category ) {
    $category->pagesCount = 0;
    $category->pagesCount += $category->pages->count();
    foreach( $category->childrenRecursive as $child ) {
        $category->pagesCount += countChildPages( $child );
    }
    return $category->pagesCount;
}

然后只要我需要在页面上使用它,我就访问计数($category->pagesCount)。




sitemap.html 页的示例

foreach( $categories as $category ) {
    $category->pagesCount = 0;
    $category->pagesCount += $category->pages->count();
    foreach( $category->childrenRecursive as $child ) {
        $category->pagesCount += countChildPages( $child );
    }
}

foreach( $categories as $category ) {
    if( $category->pagesCount > 0 ) {
        echo '<div class="category">';
            echo '<h2><a href="https://example.com/' . $category->slug . '">' . $category->name . ' (' . $category->pagesCount . ')</a></h2>';
            foreach( $category->pages as $page ) {
                showPage( $page );
            }
            foreach( $category->childrenRecursive as $child ) {
                showSubCategory( $child );
            }
        echo '</div>';
    }
}

function countChildPages( $category ) {
    $category->pagesCount = 0;
    $category->pagesCount += $category->pages->count();
    foreach( $category->childrenRecursive as $child ) {
        $category->pagesCount += countChildPages( $child );
    }
    return $category->pagesCount;
}

function showSubCategory( $category ) {
    if( $category->pagesCount > 0 ) {
        echo '<div class="category">';
            echo '<h2><a href="https://example.com/' . $category->slug . '">' . $category->name . ' (' . $category->pagesCount . ')</a></h2>';
            foreach( $category->pages as $page ) {
                showPage( $page );
            }
            foreach( $category->childrenRecursive as $child ) {
                showSubCategory( $child );
            }
        echo '</div>';
    }
}

function showPage( $page ) {
    echo '<div class="category page">';
        echo '<h2><a href="https://example.com/' . $page->slug . '">' . $page->title . '</a></h2>';
    echo '</div>';
}