Gar*_*een 84 php mysql database design-patterns
我正在考虑设计一个在我的网站上使用的成就系统的最佳方法.数据库结构可以在最佳方式找到,告诉3个或更多连续记录缺失,这个线程实际上是从开发人员那里获得想法的扩展.
我在这个网站上有很多关于徽章/成就系统的讨论就是这个问题 - 这都是谈话而不是代码.实际的代码实现示例在哪里?
我在这里提出一个设计,我希望人们可以做出贡献,并希望为编码可扩展的成就系统创建一个好的设计.我不是说这是最好的,远非它,但它是一个可能的起点.
请随时提出您的想法.
我的系统设计理念
似乎普遍的共识是创建一个"基于事件的系统" - 每当一个已知事件发生时,如创建,删除等帖子,它就像这样调用事件类.
$event->trigger('POST_CREATED', array('id' => 8));
Run Code Online (Sandbox Code Playgroud)
然后事件类找出哪些徽章正在"监听"此事件,然后查找requires该文件,并创建该类的实例,如下所示:
require '/badges/' . $file;
$badge = new $class;
Run Code Online (Sandbox Code Playgroud)
然后它调用默认事件传递调用时收到的数据trigger;
$badge->default_event($data);
Run Code Online (Sandbox Code Playgroud)
徽章
这就是真正的魔法发生的地方.每个徽章都有自己的查询/逻辑,以确定是否应颁发徽章.每个徽章都以例如以下格式列出:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
由于上面的cron类扩展了主徽章类,它可以重用逻辑函数 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);
}
}
Run Code Online (Sandbox Code Playgroud)
即使未提供参数,cron函数仍然可以工作.
我已经实现了一个奖励系统,你可以称之为面向文档的数据库(这对玩家来说是个泥潭).我的实现中的一些亮点,翻译为PHP和MySQL:
徽章的每个细节都存储在用户数据中.如果您使用MySQL,我会确保此数据在数据库中的每个用户的一条记录中以提高性能.
每当有问题的人做某事时,代码就会触发带有给定标志的徽章代码,例如flag('POST_MESSAGE').
一个事件也可以触发一个计数器,例如一个帖子数量.increase_count( 'POST_MESSAGE').在这里你可以检查(通过一个钩子,或只是在这个方法中进行测试),如果POST_MESSAGE计数大于300,那么你应该有奖励徽章,例如:flag("300_POST").
在flag方法中,我将代码用于奖励徽章.例如,如果发送了标志300_POST,则应调用标记reward_badge("300_POST").
在flag方法中,您还应该有用户以前的标志.所以你可以说当用户有FIRST_COMMENT,FIRST_POST,FIRST_READ你授予徽章("新用户"),当你获得100_COMMENT,100_POST,300_READ时你可以授予徽章("EXPERIENCED_USER")
所有这些标志和徽章都需要以某种方式存储.使用某种方式将标志视为位.如果你想要真正有效地存储它,你可以将它们视为位并使用下面的代码:(或者如果你不想要这种复杂性,你可以使用一个简单的字符串"000000001111000".
Run Code Online (Sandbox Code Playgroud)$achievments = 0; $bits = sprintf("%032b", $achievements); /* Set bit 10 */ $bits[10] = 1; $achievements = bindec($bits); print "Bits: $bits\n"; print "Achievements: $achievements\n"; /* Reload */ $bits = sprintf("%032b", $achievments); /* Set bit 5 */ $bits[5] = 1; $achievements = bindec($bits); print "Bits: $bits\n"; print "Achievements: $achievements\n";
为用户存储文档的一种好方法是使用json并将用户数据存储在单个文本列中.使用json_encode和json_decode存储/检索数据.
为了跟踪某些其他用户操纵的某些用户数据的活动,在项目上添加数据结构并在那里使用计数器.例如读取计数.使用与上述相同的技术来授予徽章,但更新当然应该进入拥有用户的帖子.(例如文章阅读1000次徽章).
| 归档时间: |
|
| 查看次数: |
13989 次 |
| 最近记录: |