我想在我的应用程序中实现页面视图计数器.到目前为止我所做的是使用这种方法:
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));
}
Run Code Online (Sandbox Code Playgroud)
我检索这样的热门帖子列表:
$popular_posts = Post::orderBy('views', 'desc')->take(10)->get();
Run Code Online (Sandbox Code Playgroud)
但是,我想知道是否有更好的方法可以做到这一点?使用我目前的方法,我可以获得过去24小时内观看次数最多的帖子列表吗?这就是全部,谢谢!
Jea*_*cos 10
正如在@ 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();
});
Run Code Online (Sandbox Code Playgroud)
然后,创建相应的模型:
<?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');
$postsViews->save();
}
}
Run Code Online (Sandbox Code Playgroud)
最后,你的方法:
public function showpost($titleslug)
{
$post = PostsViews::where('titleslug', '=' ,$titleslug)->firstOrFail();
PostsViews::createViewLog($post);
//Rest of method...
}
Run Code Online (Sandbox Code Playgroud)
要搜索过去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.*'));
Run Code Online (Sandbox Code Playgroud)
请注意,在PostsViews中,如果您不想考虑来自同一会话的匹配,您可以使用有助于进一步过滤列表的数据,例如会话ID.
您可能需要根据最终代码调整此解决方案的某些方面.
首先,非常感谢“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();
});
Run Code Online (Sandbox Code Playgroud)
然后,创建相应的模型。请注意使用表格的第一个大写字母和单数形式创建“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
}
}
Run Code Online (Sandbox Code Playgroud)
然后运行迁移以生成此表
php artisan migrate
Run Code Online (Sandbox Code Playgroud)
最后,你的方法:
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...
}
Run Code Online (Sandbox Code Playgroud)
要搜索过去 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.*']);
Run Code Online (Sandbox Code Playgroud)
请注意,在 PostView 中,您有一些数据可以帮助进一步过滤您的列表,例如会话 ID,以防您不想考虑来自同一会话的命中。
您可能需要将此解决方案的某些方面调整到您的最终代码中。
所以这些是我想指出的一些修改,您可能还想放置一个额外的列client_internet_ip,您可以在其中存储\Request::ip()它,如果需要,它也可以用作过滤器。
我希望它有帮助
Eloquent Viewable包可以用于此目的。它提供了更灵活的方法来执行此类操作(计算页面浏览量)。
注意:Eloquent Viewable 包需要 PHP 7+ 和 Laravel 5.5+。
使模型可见:
只需将Viewable特征添加到模型定义中,例如:
use Illuminate\Database\Eloquent\Model;
use CyrildeWit\EloquentViewable\Viewable;
class Post extends Model
{
use Viewable;
// ...
}
Run Code Online (Sandbox Code Playgroud)
然后在控制器中:
public function show(Post $post)
{
$post->addView();
return view('blog.post', compact('post'));
}
Run Code Online (Sandbox Code Playgroud)
之后你可以做这样的事情:(有关更多详细信息,请参阅软件包安装指南)
// Get the total number of views
$post->getViews();
// Get the total number of views since the given date
$post->getViews(Period::since(Carbon::parse('2014-02-23 00:00:00')));
// Get the total number of views between the given date range
$post->getViews(Period::create(Carbon::parse('2014-00-00 00:00:00'), Carbon::parse('2016-00-00 00:00:00')));
// Get the total number of views in the past 6 weeks (from today)
$post->getViews(Period::pastWeeks(6));
// Get the total number of views in the past 2 hours (from now)
$post->getViews(Period::subHours(2));
// Store a new view in the database
$post->addView();
Run Code Online (Sandbox Code Playgroud)
实现与接受的答案相同的想法,但提供更多的功能和灵活性。
小智 6
2020 更新(2)/ 与 Laravel 6 的 Eloquent 关系
如果您不想向应用程序添加包。我根据“Jean Marcos”和“Learner”对问题的贡献以及我自己的研究开发了以下解决方案。
所有功劳都归功于“Jean Marcos”和“Learner”,我觉得我应该和Learner一样,并以对其他人有益的方式更新代码。
首先,确保您在数据库中有一个会话表。否则,请按照 Laravel 文档中的步骤进行操作:HTTP Session
确保会话存储在表中。如果没有,请确保将 .env 中的 SESSION_DRIVER 变量设置为“数据库”而不是“文件”,然后执行 composer dump-autoload。
之后,您就可以开始了。您可以首先运行以下控制台命令:
php artisan make:model PostView -m
Run Code Online (Sandbox Code Playgroud)
这将生成模型和迁移文件。
在迁移文件中放置以下 Schema。小心列名。例如,我的帖子表具有“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();
});
Run Code Online (Sandbox Code Playgroud)
然后将以下代码放入 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();
}
}
Run Code Online (Sandbox Code Playgroud)
现在在 Post 模型中编写以下代码。这将创建 posts 表和 post_views 表之间的关系。
use App\PostView;
public function postView()
{
return $this->hasMany(PostView::class);
}
Run Code Online (Sandbox Code Playgroud)
在同一个 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();
}
Run Code Online (Sandbox Code Playgroud)
您现在已准备好运行迁移。
php artisan migrate
Run Code Online (Sandbox Code Playgroud)
当用户要求发布时。应在 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;
}
Run Code Online (Sandbox Code Playgroud)
因为我有一个单独的列用于帖子表中的视图。要搜索过去 24 小时内查看次数最多的帖子,您可以在控制器中编写此代码。如果没有分页,请删除分页:
public function mostViwedPosts()
{
return Posts::with('user')->where('created_at','>=', now()->subdays(1))->orderBy('views', 'desc')->latest()->paginate(5);
}
Run Code Online (Sandbox Code Playgroud)
我希望这会帮助/节省某人的时间。
| 归档时间: |
|
| 查看次数: |
8374 次 |
| 最近记录: |