在 Laravel 中为博客生成存档列表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16494821/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 07:45:59  来源:igfitidea点击:

Generating archive list for a blog in Laravel

phpsqllaravel

提问by SUB0DH

I am trying to generate an archive list for blog articles. The archive list should display year and date in reverse chronological order as follows:

我正在尝试为博客文章生成存档列表。存档列表应按时间倒序显示年份和日期,如下所示:

2013 (21)
    - May (2)
    - April (3)
    - March (5)
    - February (1)
    - January (10)
2012 (10)
    - December (6)
    - November (4)

The number inside ()are the number of posts in that time period. When the year or month of the year has been selected, only the blog posts from that selected time period should be displayed.

里面()的数字是那个时间段的帖子数。选择年份或月份后,应仅显示所选时间段内的博客文章。

So far I've only been able to find out the year and month of each blog post by doing:

到目前为止,我只能通过执行以下操作来找出每篇博文的年份和月份:

$posts = Post::all();
$archive = array();
foreach ($posts as $post) {
    $year = date('Y', strtotime($post->created_at));
    $month = date('m', strtotime($post->created_at));
}

How do I go about achieving the above objectives?

我如何去实现上述目标?

回答by peterm

For generating a links in some sort of navigation panel you can do most of the processing on DB side and not fetching all the blog posts records with a query like this

为了在某​​种导航面板中生成链接,您可以在数据库端进行大部分处理,而不是使用这样的查询获取所有博客文章记录

SELECT YEAR(created_at) year,
       MONTH(created_at) month,
       MONTHNAME(created_at) month_name,
       COUNT(*) post_count
  FROM post
 GROUP BY year, MONTH(created_at)
 ORDER BY year DESC, month DESC;

Output:

输出:

| YEAR | MONTH | MONTH_NAME | POST_COUNT |
------------------------------------------
| 2013 |     5 |        May |          5 |
| 2013 |     4 |      April |          3 |
| 2013 |     3 |      March |          4 |
| 2013 |     2 |   February |          3 |
| 2013 |     1 |    January |          2 |
| 2012 |    12 |   December |          2 |
| 2012 |    11 |   November |          3 |

I'm not an expert in laravel, but it should be achieved with something similar to this

我不是 laravel 的专家,但应该用类似的东西来实现

$links = DB::table('post')
    ->select(DB::raw('YEAR(created_at) year, MONTH(created_at) month, MONTHNAME(created_at) month_name, COUNT(*) post_count'))
    ->groupBy('year')
    ->groupBy('month')
    ->orderBy('year', 'desc')
    ->orderBy('month', 'desc')
    ->get();

If you want you can add subtotals to year rows like this

如果你愿意,你可以像这样向年份行添加小计

SELECT YEAR(created_at) year,
       MONTH(created_at) month,
       MONTHNAME(created_at) month_name,
       COUNT(*) post_count
  FROM post
 GROUP BY year, MONTH(created_at)
UNION ALL
SELECT YEAR(created_at) year,
       13 month,
       NULL month_name,
       COUNT(*) post_count
  FROM post
 GROUP BY year
 ORDER BY year DESC, month DESC;

Output:

输出:

| YEAR | MONTH | MONTH_NAME | POST_COUNT |
------------------------------------------
| 2013 |    13 |     (null) |         17 |
| 2013 |     5 |        May |          5 |
| 2013 |     4 |      April |          3 |
| 2013 |     3 |      March |          4 |
| 2013 |     2 |   February |          3 |
| 2013 |     1 |    January |          2 |
| 2012 |    13 |     (null) |          5 |
| 2012 |    12 |   December |          2 |
| 2012 |    11 |   November |          3 |

SQLFiddle

SQL小提琴

回答by Hyman McDade

This is the most elegant way to do this would be to pass a closure into the groupBy() collection method.

最优雅的方法是将闭包传递给 groupBy() 集合方法。

$posts_by_date = Post::all()->groupBy(function($date) {
    return Carbon::parse($date->created_at)->format('Y-m');
});

Then you can loop through it in your blade template similar to this:

然后你可以在你的刀片模板中循环遍历它,类似于:

@foreach ($posts_by_date as $date => $posts)
    <h2>{{ $date }}</h2>
    @foreach ($posts as $post)
        <h3>{{ $post->title }}</h3>
        {{ $post->content }}
    @endforeach
@endforeach

回答by Raymond Wensveen

I was stuck on this for a while as well. By using some more laravel functionality I came to the following solution:

我也被困在这个问题上一段时间。通过使用更多的 Laravel 功能,我得出了以下解决方案:

To create the archive:

要创建存档:

$archive = Post::orderBy('created_at', 'desc')
        ->whereNotNull('created_at')
        ->get()
        ->groupBy(function(Post $post) {
            return $post->created_at->format('Y');
        })
        ->map(function ($item) {
            return $item
                ->sortByDesc('created_at')
                ->groupBy( function ( $item ) {
                    return $item->created_at->format('F');
                });
        });

Then to Display (including bootstrap 4 classes):

然后显示(包括引导程序 4 个类):

<div class="archive mt-5">
<h4>Archive</h4>

@foreach($archive as $year => $months)
    <div>
        <div id="heading_{{ $loop->index }}">
            <h6 class="mb-0">
                <button class="btn btn-link py-0 my-0" data-toggle="collapse"
                        data-target="#collapse_{{ $loop->index }}"
                        aria-expanded="true"
                        aria-controls="collapse_{{ $loop->index }}">
                    >
                </button>
                {{ $year }}
            </h6>
        </div>

        <div id="collapse_{{ $loop->index }}" class="collapse" aria-labelledby="heading_{{ $loop->index }}"
             data-parent="#accordion">
            <div>
                <ul style="list-style-type: none;">
                    @foreach($months as $month => $posts)
                        <li class="">
                                {{ $month }} ( {{ count($posts) }} )
                        </li>

                    @endforeach
                </ul>
            </div>
        </div>
    </div>
@endforeach

回答by Ryan

I think you're close, but you need to test the data, is that what you're asking?

我认为你很接近,但你需要测试数据,这就是你要问的吗?

You could also query for the data.

您还可以查询数据。

I'm not saying this is the only way to solve the problem, but how would this work? The for each statement could to store the data into an array, something like this:

我并不是说这是解决问题的唯一方法,但这将如何工作?for each 语句可以将数据存储到数组中,如下所示:

function checkdate($datein,$posts){

    foreach ($posts as $post){
        $year = date('Y', strtotime($post->created_at)))
        $month = date('m', strtotime($post->created_at)))

        if(($year == $datein->year)||($month == $datein->month)){
        //good
        }
        else {
        //bad
        }
    }
}

That would do the trick, but... what if we had filtered data in the first place. I'll bet Laravel has a better way to handle it.

那会奏效,但是……如果我们首先过滤数据会怎样。我敢打赌 Laravel 有更好的方法来处理它。

Yep! http://four.laravel.com/docs/eloquent#query-scopes

是的! http://four.laravel.com/docs/eloquent#query-scopes

keep reading about polymorphism and querying relations, dynamic properties...

继续阅读关于多态性和查询关系、动态属性...