使用Laravel计算页面视图

时间:2017-07-31 18:03:26

标签: laravel laravel-5

我想在我的应用中实现网页浏览计数器。到目前为止我所做的是使用这种方法:

public function showpost($titleslug) {
        $post = Post::where('titleslug','=',$titleslug)->firstOrFail();
        $viewed = Session::get('viewed_post', []);
        if (!in_array($post->id, $viewed)) {
            $post->increment('views');
            Session::push('viewed_post', $post->id);
        }
        return view('posts/show', compact('post', $post));
    }

我检索这样的热门帖子列表:

$popular_posts = Post::orderBy('views', 'desc')->take(10)->get();

但是,我想知道是否有更好的方法可以做到这一点?使用我目前的方法,我可以获得过去24小时内观看次数最多的帖子列表吗?这就是全部,谢谢!

4 个答案:

答案 0 :(得分:8)

正如@ milo526评论中引用的那样,您可以以独特的方式记录所有点击,而不是增量。通过这种方式,您可以搜索访问信息,包括按大多数人查看的帖子列表。

创建一个表来保存您的查看记录:

Schema::create("posts_views", function(Blueprint $table)
        {
            $table->engine = "InnoDB";

            $table->increments("id");
            $table->increments("id_post");
            $table->string("titleslug");
            $table->string("url");
            $table->string("session_id");
            $table->string("user_id");
            $table->string("ip");
            $table->string("agent");
            $table->timestamps();
        });

然后,创建相应的模型:

<?php namespace App\Models;

class PostsViews extends \Eloquent {

    protected $table = 'posts_views';

    public static function createViewLog($post) {
            $postsViews= new PostsViews();
            $postsViews->id_post = $post->id;
            $postsViews->titleslug = $post->titleslug;
            $postsViews->url = \Request::url();
            $postsViews->session_id = \Request::getSession()->getId();
            $postsViews->user_id = \Auth::user()->id;
            $postsViews->ip = \Request::getClientIp();
            $postsViews->agent = \Request::header('User-Agent');
    }

}

最后,你的方法:

public function showpost($titleslug)
{
    $post = PostsViews::where('titleslug', '=' ,$titleslug)->firstOrFail();

    PostsViews::createViewLog($post);

    //Rest of method...
}

要搜索过去24小时内观看次数最多的帖子:

$posts = Posts::join("posts_views", "posts_views.id_post", "=", "posts.id")
            ->where("created_at", ">=", date("Y-m-d H:i:s", strtotime('-24 hours', time())))
            ->groupBy("posts.id")
            ->orderBy(DB::raw('COUNT(posts.id)', 'desc'))
            ->get(array(DB::raw('COUNT(posts.id) as total_views'), 'posts.*'));

请注意,在PostsViews中,如果您不想考虑来自同一会话的点击,您可以使用有助于进一步过滤您的商家信息的数据,例如会话ID。

您可能需要根据最终代码调整此解决方案的某些方面。

答案 1 :(得分:6)

2020更新(2)/ Laravel 6具有雄辩的关系

如果您不想将包添加到您的应用程序。我根据对问题的“让·马科斯”和“学习者”的贡献以及我自己的研究开发了以下解决方案。

所有功劳归功于“让·马科斯”和“学习者”,我觉得我应该像学习者一样做,并以对他人有利的方式更新代码。

首先,确保数据库中有一个会话表。否则,请按照Laravel文档中的步骤进行操作:HTTP Session

确保会话存储在表中。如果没有,请确保将.env设置为'database'而不是'file'更改SESSION_DRIVER变量,然后再执行composer dump-autoload。

在那之后,您都准备就绪。您可以通过运行以下控制台命令开始:

php artisan make:model PostView -m 

这将同时生成模型文件和迁移文件。

在迁移文件中放入以下架构。请谨慎使用列名称。例如,我的posts表具有“ slug”列标题名称,而不是问题中提到的“ titleslug”。

  Schema::create('post_views', function (Blueprint $table) {

        $table->increments("id");
        $table->unsignedInteger("post_id");
        $table->string("titleslug");
        $table->string("url");
        $table->string("session_id");
        $table->unsignedInteger('user_id')->nullable();
        $table->string("ip");
        $table->string("agent");
        $table->timestamps();
    });

然后将以下代码放入PostView模型文件中。

<?php

namespace App;

use App\Post;
use Illuminate\Database\Eloquent\Model;

class PostView extends Model
{

    public function postView()
    {
        return $this->belongsTo(Post::class);
    }

    public static function createViewLog($post) {
        $postViews= new PostView();
        $postViews->post_id = $post->id;
        $postViews->slug = $post->slug;
        $postViews->url = request()->url();
        $postViews->session_id = request()->getSession()->getId();
        $postViews->user_id = (auth()->check())?auth()->id():null; 
        $postViews->ip = request()->ip();
        $postViews->agent = request()->header('User-Agent');
        $postViews->save();
    }
}

现在在Post模型内部编写以下代码。这将在posts表和post_views表之间创建关系。

use App\PostView;

   public function postView()
    {
        return $this->hasMany(PostView::class);
    }

在相同的Post模型中,应放置以下代码。如果用户未登录,则代码将测试IP匹配。否则,它将同时测试会话ID和用户ID,因为每个用户可能有多个会话。

public function showPost()
{
    if(auth()->id()==null){
        return $this->postView()
        ->where('ip', '=',  request()->ip())->exists();
    }

    return $this->postView()
    ->where(function($postViewsQuery) { $postViewsQuery
        ->where('session_id', '=', request()->getSession()->getId())
        ->orWhere('user_id', '=', (auth()->check()));})->exists();  
}

您现在准备运行迁移。

php artisan migrate

当用户要求发布信息时。以下函数应在PostController文件中定位:

use App\PostView;

     public function show(Post $post)
        {
//Some bits from the following query ("category", "user") are made for my own application, but I felt like leaving it for inspiration. 
            $post = Post::with('category', 'user')->withCount('favorites')->find($post->id);

            if($post->showPost()){// this will test if the user viwed the post or not
                return $post;
            }

            $post->increment('views');//I have a separate column for views in the post table. This will increment the views column in the posts table.
            PostView::createViewLog($post);
            return $post;
        }

因为我在发布表中有单独的视图列。要搜索最近24小时内观看次数最多的帖子,请在控制器中编写此代码。如果您没有分页,请删除分页:

public function mostViwedPosts()
{
    return Posts::with('user')->where('created_at','>=', now()->subdays(1))->orderBy('views', 'desc')->latest()->paginate(5);
}

我希望这会帮助/节省别人的时间。

答案 2 :(得分:2)

2018更新

首先,非常感谢“ Jean Marcos”的出色回答。所有的功劳都归功于他,我只是在粘贴一个略微修改过的答案,同时结合了我对Laravel的了解。

创建一个表来保存您的视图记录,并使用 snake_case复数形式:post_views

Schema::create("post_views", function(Blueprint $table)
{
      $table->engine = "InnoDB";//this is basically optional as you are not using foreign key relationship so you could go with MyISAM as well

      $table->increments("id");

      //please note to use integer NOT increments as "Jean Marcos' answer" because it will throw error "Incorrect table definition; there can be only one auto column and it must be defined as a key" when running migration.
      $table->unsignedInteger("post_id");//note that the Laravel way of defining foreign keys is "table-singular-name_id", so it's preferable to use that

      $table->string("titleslug");
      $table->string("url");
      $table->string("session_id");
      $table->unsignedInteger('user_id')->nullable();//here note to make it nullable if your page is accessible publically as well not only by logged in users. Also its more appropriate to have "unsignedInteger" type instead of "string" type as mentioned in Jean Marcos' answer because user_id will save same data as id field of users table which in most cases will be an auto incremented id.
      $table->string("ip");
      $table->string("agent");
      $table->timestamps();
});

然后,创建相应的模型。 请注意使用表的首个大写字母和单数形式创建“ camelCase”模型名称,因此应类似于: PostView

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class PostView extends Model
{
    public static function createViewLog($post) {
        $postViews= new PostView();
        $postViews->listing_id = $post->id;
        $postViews->url = \Request::url();
        $postViews->session_id = \Request::getSession()->getId();
        $postViews->user_id = (\Auth::check())?\Auth::id():null; //this check will either put the user id or null, no need to use \Auth()->user()->id as we have an inbuild function to get auth id
        $postViews->ip = \Request::getClientIp();
        $postViews->agent = \Request::header('User-Agent');
        $postViews->save();//please note to save it at lease, very important
    }
}

然后运行迁移以生成该表

php artisan migrate

最后,您的方法:

public function showpost($titleslug)
{
    $post = PostView::where('titleslug', '=' ,$titleslug)->firstOrFail();

    \App\PostView::createViewLog($post);//or add `use App\PostView;` in beginning of the file in order to use only `PostView` here 

    //Rest of method...
}

要搜索过去24小时内观看次数最多的帖子:

$posts = Posts::join("post_views", "post_views.id_post", "=", "posts.id")
            ->where("created_at", ">=", date("Y-m-d H:i:s", strtotime('-24 hours', time())))
            ->groupBy("posts.id")
            ->orderBy(DB::raw('COUNT(posts.id)'), 'desc')//here its very minute mistake of a paranthesis in Jean Marcos' answer, which results ASC ordering instead of DESC so be careful with this line
            ->get([DB::raw('COUNT(posts.id) as total_views'), 'posts.*']);

请注意,在PostView中,如果您不想考虑来自同一会话的点击,则您的数据可以帮助进一步过滤列表,例如会话ID。

您可能需要使此解决方案的某些方面适应最终代码。

因此,我想指出的只是一些修改,您可能还想添加一列“ client_internet_ip”,您可以在其中存储\Request::ip(),并在需要时用作过滤器。

我希望对您有帮助

答案 3 :(得分:0)

首先感谢user33192分享了eloquent viewable。在查看文档后,只想让其他人更清楚。查看文档以安装该软件包。

在您的帖子模型中执行此操作:

use Illuminate\Database\Eloquent\Model;
use CyrildeWit\EloquentViewable\InteractsWithViews;
use CyrildeWit\EloquentViewable\Viewable;

class Post extends Model implements Viewable
{
    use InteractsWithViews;

    // ...
}

在您的帖子控制器中,使用记录方法保存视图;

public function show($slug)
{
    $post = Post::where('slug',$slug)->first();
    views($post)->record();
    return view('posts.show',compact('post'));
}

在您的视图中,您可以根据需要返回视图(我的是 posts.show )。检查文档以了解更多。我只会发表一篇帖子的全部观点。

<button class="btn btn-primary">
    {{ views($post)->count() }} <i class="fa fa-eye"></i>
</button>