查看计数器 - 如何只为每个用户计算一次? - PHP / Laravel 4

时间:2013-07-07 10:13:03

标签: php mysql laravel laravel-4

我正计划用PHP编写一个用于博客的视图计数。计数器的目的是指示博客文章的阅读频率,例如StackOverflow在问题页面上显示“浏览过n次”。

首先,我想将计数器数据存储在文件中,但是通过研究我改变了主意,将MySQL与几个InnoDB表一起使用,因为这些表的执行速度比硬盘查找速度快,从长远来看也不那么费力,而且本身也提供了有用的功能。 / p>

我坚持的问题是如何只为每位用户计算一次?

限制IP不是一种解决方案,因为动态IP和网络通常只有一个IP与所有连接的设备(例如学校,办公室,公共接入点)共享连接。

修改

灵感来自fideloper’s answer我正在研究基于会话的解决方案。

我正在为会话使用单独的数据库表,因此我会按照建议将其合并到解决方案中。

这是我到目前为止(使用Laravel 4 Eloquent ORM):

SQL表设置:

-- 
-- Sessions Table
-- 
CREATE TABLE sessions (
    id VARCHAR(255) NOT NULL COLLATE 'utf8_unicode_ci',
    payload TEXT NOT NULL COLLATE 'utf8_unicode_ci',
    last_activity INT(11) NOT NULL,

    UNIQUE INDEX sessions_id_unique (id)
) COLLATE='utf8_unicode_ci' ENGINE=InnoDB;

-- 
-- Views Table
-- 
CREATE TABLE post_views (
    id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
    post_id INT(11) UNSIGNED NOT NULL,
    sess_id VARCHAR(255) NOT NULL COLLATE 'utf8_unicode_ci',
    viewed_at TIMESTAMP NULL DEFAULT NULL,

    PRIMARY KEY (id)
) COLLATE='utf8_unicode_ci' ENGINE=InnoDB;

-- 
-- Posts Table
-- 
CREATE TABLE posts (
    id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
    user_id INT(11) UNSIGNED NOT NULL,
    title VARCHAR(255) NOT NULL COLLATE 'utf8_unicode_ci',
    excerpt TEXT NOT NULL COLLATE 'utf8_unicode_ci',
    slug VARCHAR(255) NOT NULL COLLATE 'utf8_unicode_ci',
    content LONGTEXT NOT NULL COLLATE 'utf8_unicode_ci',
    ...

    views INT(10) UNSIGNED NOT NULL DEFAULT '0',

    PRIMARY KEY (id)
) COLLATE='utf8_unicode_ci' ENGINE=InnoDB;


PHP视图计数器类:

<?php namespace Services\Posts;
/**
 * Blog Post Views Counter
 *
 * Using Laravel 4 with Eloquent ORM
 */

class ViewsCounter {

    public $post_id;

    /**
     * Sets the post id
     * 
     * @param  int $id
     * @return void
     */
    public function setPostID($id)
    {
        $this->post_id = $id;
    }

    /**
     * Handles the view count
     * 
     * @return bool  True or false on failure
     */
    public function countView()
    {
        // Get the post data from the db
        try
        {
            $post = \Post::findOrFail($this->post_id);
        }
        catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e)
        {
            return false;
        }

        // Get the current session id
        $sess_id = session_id();

        // Check if this view was already counted by 
        // looking up the session id in the views table
        $post_views = \PostViews::where('sess_id', '=', $sess_id)
                            ->where('post_id', '=', $this->post_id)
                            ->remember(10)
                            ->first();

        // The sess_id was not found so count the view
        if (empty($post_views))
        {
            $post_views->post_id = $this->post_id;
            $post_views->sess_id = $sess_id;
            $post_views->viewed_at = date('Y-m-d H:i:s');
            $post_views->save();

            // A cronjob is run every 24 hours to count the views per 
            // post and update the count values in the posts table.
            // The post_views table will be truncated when done.
        }

        return true;
    }

}

建议表示赞赏。

1 个答案:

答案 0 :(得分:8)

计算+1个观看次数每位用户的问题很有意思,因为您需要考虑会话。

您确实需要为每个会话ID执行+1次计数(因为它会按用户重新生成,但更有可能为每次访问每位用户提供+1次查看次数...具体取决于您的代码)。请注意,这意味着在新会话中再次访问该网站的用户将再次计算一次视图计数。

如果您需要追踪会话ID,可能需要执行以下操作:

  1. 为每个视图添加一个新数据库行(与增加单个值相对)。这样,您可以跟踪会话ID以及视图计数(这也意味着最终的大表!)
  2. 如果未找到会话ID,则仅添加新行(如果找到,则此次访问中用户已有+1点数)。
  3. 对于某些可伸缩性:添加第二个表,该表跟踪每个视图的访问次数。使用CRON或类似版本定期更新。然后,您可以始终写入一个表(优化写入表)并从另一个表中读取(优化读取)。
  4. 要获得额外的可扩展性,请使用第二个表来计算每24小时的查看次数。然后,您可以从视图计数表中删除视图。这样,您永远不会将您的查看次数表填满到很大的比例,但是您可以节省每24小时的总观看次数。
  5. 也可以随时缩短24小时。您可能(最终)会在更新视图计数时计算一些额外的视图计数,并且要比较的会话ID将被删除。

    希望能为你激发一些想法。