使用Laravel计算页面浏览量

am46iovg  于 2023-02-20  发布在  其他
关注(0)|答案(5)|浏览(167)

我想在我的应用程序中实现页面查看计数器。到目前为止,我所做的是使用这个方法:

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小时内最受欢迎的职位?这是所有的,谢谢!

dddzy1tm

dddzy1tm1#

正如@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');
            $postsViews->save();
    }

}

最后,您的方法:

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。
您可能需要根据最终代码调整此解决方案的某些方面。

ttcibm8c

ttcibm8c2#

2020更新(2)/为Laravel 6提供雄辩的关系

如果你不想在你的应用程序中添加一个包,我根据“让·马科斯”和“学习者”对这个问题的贡献以及我自己的研究开发了下面的解决方案。
所有的功劳都要归功于“让·马科斯”和“学习者”,我觉得我应该和学习者做同样的事情,以一种对他人有益的方式更新代码。
首先,确保数据库中有一个会话表。否则,请按照Laravel文档中的步骤操作:HTTP Session
确保会话存储在表中。如果没有,请确保将.env中的SESSION_DRIVER变量更改为'database'而不是'file',然后执行Composer转储-自动加载。
之后,您就可以开始了。您可以通过运行以下控制台命令来启动:

php artisan make:model PostView -m

这将生成模型和迁移文件。
在迁移文件中放入以下Schema。注意列名。例如,我的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

当用户请求post时,应在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;
        }

因为我在post表中有一个单独的视图列。要搜索过去24小时内浏览次数最多的帖子,请在控制器中编写以下代码。如果没有分页,请删除paginate:

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

我希望这能帮助/保存一些人的时间。

dfty9e19

dfty9e193#

2020更新

首先,非常感谢"让·马科斯"给出了他令人敬畏的答案。所有的功劳都归他,我只是结合我对拉拉维尔的了解粘贴了一个略有修改的答案。
创建一个表来保存视图记录,并使用snake_case的复数形式命名:帖子浏览次数

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();
});

然后,创建相应的模型。请注意创建"PascalCase"模型名称和表的单数形式,因此它应该如下所示:* * 后视图**

<?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(),如果需要,它也可以用作过滤器。

  • 希望能有所帮助 *
gupuwyp2

gupuwyp24#

Eloquent Viewable包可以用于此目的。它提供了更灵活的方法来做类似的事情(统计页面浏览量)。
注:Eloquent Viewable软件包需要PHP 7+和Laravel 5.5+。

使模型可见:

只需将Viewabletrait添加到模型定义中,如下所示:

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

class Post extends Model
{
    use Viewable;

    // ...
}

然后在控制器中:

public function show(Post $post)
{
    $post->addView();

    return view('blog.post', compact('post'));
}

然后你可以做这样的事情:(see package installation guide for more details

// 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();

实现与已接受答案相同的想法,但提供更多功能和灵活性。

7gcisfzg

7gcisfzg5#

首先感谢user 33192分享了eloquent viewable,只是想让大家看完文档后更清楚一些,看看文档就可以安装包了。
在您的Post模型中执行此操作:

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

class Post extends Model implements Viewable
{
    use InteractsWithViews;

    // ...
}

在您的posts控制器中,使用record方法保存一个视图;

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>

相关问题