多层评论系统Laravel

时间:2016-01-28 22:45:44

标签: php laravel recursion laravel-5

我对刀片递归部分视图有困难。除comment.blade.php文件中的递归外,其他所有内容都适用。

我知道我需要在@include('articles.comments.comment', $comment)附近使用foreach再次召唤自己,但我不确定如何调用它。

article_comments 表格:

id
message
user_id
parent_id
created_at
updated_at

app \ Article.php 类:

class Article extends Model {

    protected $table = 'articles';

    protected $fillable = [
        'category',
        'title',
        'permalink',
        'synopsis',
        'body',
        'source',
        'show_author',
        'published_at'
    ];

    protected $dates = ['published_at'];

    public function scopePublished($query)
    {
        $query->where('published_at', '<=', Carbon::now());
    }

    public function setPublishedAtAttribute($date)
    {
        $this->attributes['published_at'] = Carbon::parse($date);
    }

    public function comments()
    {
        return $this->hasMany('App\Comments')->where('parent_id', 0);
    }

}

app \ Comments.php 类:

class Comments extends Model {

    protected $table = 'article_comments';

    protected $fillable = [
        'parent_id',
        'message',
    ];

    public function author() {
        return $this->belongsTo('App\User'); 
    }

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

    public function countChildren($node = null)
    {
        $query = $this->children();
        if (!empty($node)) {
            $query = $query->where('node', $node);
        }

        $count = 0;
        foreach ($query->get() as $child) {
            // Plus 1 to count the direct child
            $count += $child->countChildren() + 1; 
        }
        return $count;
    }

}

应用\ HTTP \控制器\ ArticlesController.php

public function show($permalink)
{
    $article = Article::where('permalink', '=', $permalink)->with('comments','comments.author','comments.children')->first();
    if ($article != null) {
        $comments = $article->comments;
        return view('articles.show', compact('article','comments'));
    } else {
        return redirect('/')->with('error', 'This article does not exist.');
    }
}

资源\视图\物品\ show.blade.php

@if (count($comments) > 0)
    <ul>
    @foreach ($comments as $comment)
        @include('articles.comments.comment', ['comment'=>$comment])
    @endforeach
    </ul>
@else
    no comments
@endif

资源\视图\物品\评论\ comment.blade.php

<li>
    {{ $comment->message }}

    @if (count($comment->children) > 0)
        <ul>
        @foreach ($comment->children as $child)
            @include('articles.comments.comment', ['comment'=>$child])
        @endforeach
        </ul>
    @endif
</li>

当前错误:

Invalid argument supplied for foreach() (View: /var/www/dev.example.com/resources/views/articles/comments/comment.blade.php) (View: 

6 个答案:

答案 0 :(得分:10)

你非常接近。我认为这应该有效:

<强>资源\视图\物品\ show.blade.php

@if (count($comments) > 0)
    <ul>
        @each('articles.comments.comment', $comments, 'comment');
    </ul>
@else
    no comments
@endif

<强>资源\视图\物品\评论\ comment.blade.php

<li>
    {{ $comment->message }}

    @if (count($comment->children) > 0)
        <ul>
            @each('articles.comments.comment', $comment->children, 'comment');
        </ul>
    @endif
</li>

应用\ HTTP \控制器\ ArticlesController.php:

$article = Article::where('permalink', '=', $permalink)->with('comments','comments.author','comments.children')->first();
$comments = $article->comments;
return view('articles.show', compact('article','comments'));

答案 1 :(得分:6)

首先,我发现您将所有评论都放在一个页面中而没有任何分页,但是,您将comments()模型上的Article方法限制为仅{{1}的评论仅获取根注释。但是,进一步查看代码,在控制器中,您需要延迟加载parent_id=0comments.author。请注意,延迟加载仅发生在第一个父注释中,之后,所有子项都必须通过急切加载关系来进行大量查询才能获得关系。

如果您希望将所有评论放在一个页面中,我建议您删除comments.children约束并一次加载评论并应用策略来订购它们。这是一个例子:

app \ Article.php

parent_id=0

应用\ HTTP \控制器\ ArticlesController.php

class Article extends Model {

    // ...

    public function comments()
    {
        return $this->hasMany('App\Comments');
    }

}

现在我们的评论全部排序而不会损害数据库,我们可以开始循环播放广告。我更喜欢使用两个文件而不是一个用于循环,所以我可以稍后重用代码。

<强>资源\视图\物品\ show.blade.php

use Illuminate\Database\Eloquent\Collection; 

// ...

public function show($permalink)
{
    $article = Article::where('permalink', '=', $permalink)->with('comments','comments.author','comments.children')->first();

    if ($article != null) {

        $comments = $this->buildCommentTree($article->comments);

        return view('articles.show', compact('article','comments'));
    } else {
        return redirect('/')->with('error', 'This article does not exist.');
    }
}


protected function buildCommentTree(Collection $comments, $root = 0)
{
    $branch = new Collection();

    $comments->each(function(Comment $comment) use ($root) {
        if ($comment->parent_id == $root) {

            $children = $this->buildCommentTree($comments, $comment->getKey());

            $branch->push($comment);
        } else {
            // This is to guarantee that children is always a Collection and to prevent Eloquent 
            // from hitting on the database looking for the children
            $children = new Collection();
        }

        $comment->setRelation('children', $children);
    });

    return $branch;

}

<强>资源\视图\物品\评论\ container.blade.php

@if (!$comments->isEmpty())
    @include("articles.comments.container", ['comments'=>$comments])
@else
    no comments
@endif

<强>资源\视图\物品\评论\ show.blade.php

@if (!$comments->isEmpty())
    <ul>
    @foreach ($comments as $comment)
        @include('articles.comments.show', ['comment'=>$comment])
    @endforeach
    </ul>
@else

答案 2 :(得分:2)

我可能会遗漏一些东西,但它看起来像是你复杂了一些代码。例如,您可以为评论模型添加user关系(您也可以删除整个count方法):

public function user()
{
    return $this->belongsTo('App\User');
}

然后,只要您需要访问评论者的用户ID或用户名,您就可以致电:

$user_id = $comment->user->id;
$username = $comment->user->username;

然后你应该能够清理很多你的控制器:

$article = Article::where('permalink', '=', $permalink)->first();
$comments = Comments::where('article_id', '=', $article->id)->get();

您的show.blade.php文件应该没问题。 您的comment.blade.php文件可以这样写:

<li>{{ $comment->message }}</li>
@if ($comment->children->count() > 0)
    <ul>
        @foreach($comment->children as $child)
            @include('articles.comments.comment', $child)
        @endforeach
    </ul>
@endif

我相信您遇到Invalid argument supplied for foreach()错误的问题与您对评论的原始查询有关。应该使用适当的关系模型来修正错误。

作为旁注,如果您需要按node列计算评论的孩子,您应该可以执行以下操作:

$comment->children()->where('node', $node)->get()->count();

答案 3 :(得分:2)

我简化了一下,但这就是我要做的事情:

雄辩的部分:

class Article extends Model
{
    public function comments()
    {
        return $this->hasMany(Comment::class)->where('parent_id', 0);
    }
}

class Comment extends Model
{
    public function article()
    {
        return $this->belongsTo(Article::class);
    }

    public function children()
    {
        return $this->hasMany(Comment::class, 'parent_id');
    }

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

控制器/路线行动部分:

// Fetching article using your example:
$article = Article::where('permalink', '=', $permalink)->first();

// returning view somewhere below...

查看部分: 文章观点:

@include('comments', ['comments' => $article->comments])

评论观点:

<ul>
    @foreach($comments as $comment)
        <li>
            <h2>{{ $comment->user->name }}</h2>
            <p>{{ $comment->message }}</p>
            @if(count($comments) > 0)
                @include('comments', ['comments' => $comment->children])
            @endif
        </li>
    @endforeach
</ul>

请注意,如果有很多嵌套注释,例如:

,则效率不高
Comment
  -> Child Comment
    -> Child Child Comment
      -> Child Child Child Comment

结构化的另一种方法是将所有注释一次性提取:

// Change the relation on the Article Eloquent model:
public function comments()
{
    return $this->hasMany(Comment::class);
}

假设parent_id的默认值为0,那么在文章视图中你可以这样做:

@include('comments', ['comments' => $article->comments, 'parent' => 0])

在您的评论视图中,您可以执行以下操作:

<ul>
    @foreach($comments as $comment)
        @if($parent === (int) $comment->parent_id)
            <li>
                <h2>{{ $comment->user->name }}</h2>
                <p>{{ $comment->message }}</p>
                @include('comments', ['comments' => $comments, 'parent' => (int) $comment->id])
            </li>
        @endif
    @endforeach
</ul>

更新: 看看你是如何坚持这一点的,我上传了一个例子:https://github.com/rojtjo/comments-example

确保查看最后一次提交,它显示了我在示例中使用的不同方法之间的差异。

答案 4 :(得分:2)

从mysql数据库开始:

评论表格式

id
commentable_id
commentable_type
parent_id
message
user_id
created_at
updated_at

文章表格结构

id
content
user_id
created_at
updated_at

您的模型将如下设置:注释模型将是多态的,因此您可以在应用程序的其他部分使用您的注释。注意:我建议您将模型移至app\models\文件夹。

app \ Comments.php 类:

namespace App

class Comments extends \Eloquent {

    /**
     * Name of the table to use for this model
     *
     * @var string
     */
    protected $table = 'comments';

    /**
     * The fields that are fillable
     *
     * @var array
     */
    protected $fillable = array(
        'commentable_type',
        'commentable_id',
        'message',
        'user_id',
        'parent_id'
    );

    /**
     * Commentable: Polymorphic relationship
     *
     * @return mixed
     */
    public function commentable()
    {
        return $this->morphTo();
    }

    /**
     * User: belongsTo relationship
     *
     * @return mixed
     */
    public function user()
    {
        return $this->belongsTo('User','user_id');
    }

    /**
    * Parent Comment: belongsTo relationship
    *
    * @return \App\Comment | null
    */
    public function parent()
    {
        return $this->belongsTo('App\Comments','parent_id');
    }

    /**
    * Children Comment: hasMany relationship
    *
    * @return \Illuminate\Database\Eloquent\Collection
    */        
    public function children()
    {
        return $this->hasMany('App\Comments','parent_id');
    }
}

app \ Articles.php 类:

namespace App

class Articles extends \Eloquent {
    /**
     * Name of the table to use for this model
     *
     * @var string
     */
    protected $table = 'articles';

    protected $fillable = [
        'user_id',
        'content'
    ];

    /**
     * User: belongsTo relationship
     *
     * @return mixed
     */
    public function user()
    {
        return $this->belongsTo('User','user_id');
    }

    /**
    * Comments: Polymorphic Relationship
    *
    * @return \Illuminate\Database\Eloquent\Collection
    */
    public function comments()
    {
        return $this->morphMany('App\Comments','commentable');
    }

}

文章控制器与添加的代码实际上是相同的,以便加载评论及其子项。

应用\ HTTP \控制器\ ArticlesController.php:

namespace App\Http\Controllers;

use App\Articles;

class ArticlesController extends Controller {
    /**
    * GET: View of an article
    *
    * @param string $permalink
    * @return \Illuminate\View\View
    */
    public function getView($permalink)
    {
        //Let's fetch the article
        $article = Articles::where('permalink','=',$permalink)
                    //Eager load relationships while applying conditions
                    ->with([
                    'comments' => function($query) {
                        //Utility method to orderBy 'created_at' DESC
                        return $query->latest();
                    },
                    'comments.children' => function($query) {
                        //Utility method to orderBy 'created_at' DESC
                        return $query->latest();
                    }
                    ])
                    ->first();

        //Generate the view
        return view('articles.show',['article'=>$article]);
    }
}

最后,显示数据的视图

<强>资源\视图\物品\ show.blade.php

<h1>My article:</h1>
<div>
    <p>
        @if($article)
            {{$article->content}}
        @endif
    </p>
</div>

@if($article)
    <!-- Comments: START -->
    <div>
        @if (count($article->comments) > 0)
            <ul>
            @foreach ($article->comments as $comment)
                @include('articles.comments.comment', ['comment'=>$comment])
            @endforeach
            </ul>
        @else
            <span>no comments</span>
        @endif
    </div>
    <!-- Comments: END -->
@endif

<强>资源\视图\物品\评论\ comment.blade.php

<li>
    {{ $comment->message }}
    <!-- Comment Children: START -->
    @if (count($comment->children) > 0)
        <ul>
            @foreach ($comment->children as $child)
                @include('articles.comments.comment', ['comment'=>$child])
            @endforeach
        </ul>
    @endif  
    <!-- Comment Children: END -->
</li>

答案 5 :(得分:0)

我重构了你的Comments.php。你可以试试这个:

应用\的comments.php

class Comments extends Model {

protected $table = 'article_comments';

protected $fillable = [
    'parent_id',
    'message',
];

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

public function countChildren($node = null)
{
    $query = $this->children();
    return (!empty($node)) ? $query->where('node', $node)->count() : $query->count();
}
}

<强>资源\视图\物品\评论\ comment.blade.php

<li>{{ $comment->message }}</li>  
@if (App\Comments::find($comment->comment_id)->countChildren() > 0)
<ul>
    <li>{{ $comment->message}}</li>
    @include('articles.comments.comment', ['comment' => $comment])
</ul>
@endif

<强>资源\视图\物品\ show.blade.php

@if (count($comments) > 0)
<ul>
    @foreach ($comments as $comment)
      @include('articles.comments.comment', ['comment' => $comment])
    @endforeach
</ul>
@else
 no comments
@endif