【发布时间】:2011-05-10 17:02:18
【问题描述】:
我正在考虑设计用于我的网站的成就系统的最佳方式。数据库结构可以在Best way to tell 3 or more consecutive records missing 找到,这个线程真的是从开发人员那里获得想法的扩展。
我在这个网站上有很多关于徽章/成就系统的讨论的问题就是 - 都是空谈,没有代码。实际的代码实现示例在哪里?
我在这里提出一个设计,希望人们可以为可扩展成就系统的编码做出贡献,并希望创建一个好的设计。我并不是说这是最好的,远非如此,但它是一个可能的起点。
请随时贡献您的想法。
我的系统设计理念
似乎普遍的共识是创建一个“基于事件的系统”——无论何时发生已知事件,例如创建、删除帖子等,它都会像这样调用事件类..
$event->trigger('POST_CREATED', array('id' => 8));
然后事件类找出哪些标记正在“侦听”该事件,然后它requires 那个文件,并创建该类的一个实例,如下所示:
require '/badges/' . $file;
$badge = new $class;
然后调用默认事件,传递调用trigger时收到的数据;
$badge->default_event($data);
徽章
这就是真正的魔法发生的地方。每个徽章都有自己的查询/逻辑来确定是否应该授予徽章。每个徽章都列在例如这种格式:
class Badge_Name extends Badge
{
const _BADGE_500 = 'POST_500';
const _BADGE_300 = 'POST_300';
const _BADGE_100 = 'POST_100';
function get_user_post_count()
{
$escaped_user_id = mysql_real_escape_string($this->user_id);
$r = mysql_query("SELECT COUNT(*) FROM posts
WHERE userid='$escaped_user_id'");
if ($row = mysql_fetch_row($r))
{
return $row[0];
}
return 0;
}
function default_event($data)
{
$post_count = $this->get_user_post_count();
$this->try_award($post_count);
}
function try_award($post_count)
{
if ($post_count > 500)
{
$this->award(self::_BADGE_500);
}
else if ($post_count > 300)
{
$this->award(self::_BADGE_300);
}
else if ($post_count > 100)
{
$this->award(self::_BADGE_100);
}
}
}
award 函数来自扩展类Badge,它基本上检查用户是否已经获得了该徽章,如果没有,将更新徽章数据库表。徽章类还负责检索用户的所有徽章并将其以数组等形式返回(因此徽章可以例如显示在用户个人资料中)
当系统首次在已经上线的网站上实施时会怎样?
还有一个可以添加到每个徽章的“cron”作业查询。这样做的原因是,当徽章系统第一次实施和初始化时,应该已经获得的徽章还没有被授予,因为这是一个基于事件的系统。因此,CRON 作业会根据需要为每个徽章运行,以奖励任何需要的东西。例如,上面的 CRON 作业如下所示:
class Badge_Name_Cron extends Badge_Name
{
function cron_job()
{
$r = mysql_query('SELECT COUNT(*) as post_count, user_id FROM posts');
while ($obj = mysql_fetch_object($r))
{
$this->user_id = $obj->user_id; //make sure we're operating on the right user
$this->try_award($obj->post_count);
}
}
}
由于上面的cron类扩展了主badge类,它可以重用逻辑函数try_award
我为此创建一个专门的查询的原因是虽然我们可以“模拟”以前的事件,即遍历每个用户帖子并触发像$event->trigger() 这样的事件类,但它会非常慢,尤其是对于许多徽章。所以我们改为创建一个优化的查询。
哪些用户获得了奖励?所有关于根据事件奖励其他用户
Badge 类award 函数作用于user_id——他们将永远获得奖励。默认情况下,徽章被授予导致事件发生的人,即会话用户 id(这对于 default_event 函数是正确的,尽管 CRON 作业显然会遍历所有用户并奖励单独的用户)
让我们举个例子,在编码挑战网站上,用户提交他们的编码条目。管理员然后判断条目,并在完成后将结果发布到挑战页面以供所有人查看。发生这种情况时,会调用 POSTED_RESULTS 事件。
如果您想为发布的所有条目授予用户徽章,比如说,如果他们排名在前 5 位,您应该使用 cron 作业(但请记住,这将为所有用户更新,而不仅仅是针对结果发布的挑战)
如果您想针对更具体的区域使用 cron 作业进行更新,让我们看看是否有办法将过滤参数添加到 cron 作业对象中,并获取 cron_job 函数来使用它们。例如:
class Badge_Top5 extends Badge
{
const _BADGE_NAME = 'top5';
function try_award($position)
{
if ($position <= 5)
{
$this->award(self::_BADGE_NAME);
}
}
}
class Badge_Top5_Cron extends Badge_Top5
{
function cron_job($challenge_id = 0)
{
$where = '';
if ($challenge_id)
{
$escaped_challenge_id = mysql_real_escape_string($challenge_id);
$where = "WHERE challenge_id = '$escaped_challenge_id'";
}
$r = mysql_query("SELECT position, user_id
FROM challenge_entries
$where");
while ($obj = mysql_fetch_object($r))
{
$this->user_id = $obj->user_id; //award the correct user!
$this->try_award($obj->position);
}
}
即使没有提供参数,cron 函数仍然可以工作。
【问题讨论】:
-
相关但不重复。请阅读第二段。 “这个网站上有很多关于徽章/成就系统的讨论,我遇到的问题就是——都是空谈,没有代码。实际的代码实现示例在哪里?”
-
好吧,编写工作代码只是在一定程度上可行。我想说人们只给你理论是很正常的,一旦任何实现都太复杂了。
标签: php mysql database design-patterns