php - Best way to code Achievements system -


i'm thinking of best way design achievements system use on site. database structure can found @ best way tell 3 or more consecutive records missing , thread extension ideas developers.

the problem have lots of talk badges/achievement systems on website -- it's talk , no code. where's actual code implemention examples?

i propose here design hope people contribute , create design coding extensible achievement systems. i'm not saying best, far it, it's possible starting block.

please feel free contribute ideas.


my system design idea

it seems general consensus create "event based system" -- whenever known event occurs post created, deleted, etc calls event class so..

$event->trigger('post_created', array('id' => 8)); 

the event class finds out badges "listening" event, requires file, , creates instance of class, so:

require '/badges/' . $file; $badge = new $class; 

it calls default event passing data received when trigger called;

$badge->default_event($data); 

the badges

this real magic happens. each badge has own query/logic determine if badge should awarded. each badge set out in e.g. format:

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(*) posts                     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 function comes extended class badge checks see if user has awarded badge, if not, update badge db table. badge class takes care of retrieving badges user , returning in array, etc (so badges can e.g. displayed on user profile)

what when system first implemented on live site?

there "cron" job query can added each badge. reason because when badge system first implemented , initilaised, badges should have been earned have not yet awarded because event based system. cron job run on demand each badge award needs be. example cron job above like:

class badge_name_cron extends badge_name {   function cron_job()  {   $r = mysql_query('select count(*) post_count, user_id posts');    while ($obj = mysql_fetch_object($r))   {    $this->user_id = $obj->user_id; //make sure we're operating on right user     $this->try_award($obj->post_count);   }  }  } 

as above cron class extends main badge class, can re-use logic function try_award

the reason why create specialised query although "simulate" previous events, i.e. go through every user post , trigger event class $event->trigger() slow, many badges. instead create optimized query.

what user gets award? awarding other users based on event

the badge class award function acts on user_id -- given award. default badge awarded person caused event happen i.e. session user id (this true default_event function, although cron job loops through users , awards seperate users)

so let's take example, on coding challenge website users submit coding entry. admin judges entries , when complete, posts results challenge page see. when happens, posted_results event called.

if want award badges users entries posted, lets say, if ranked within top 5, should use cron job (although bare in mind update users, not challenge results posted for)

if want target more specific area update cron job, let's see if there way add filtering parameters cron job object, , cron_job function use them. example:

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                        challenge_entries                        $where");      while ($obj = mysql_fetch_object($r))    {       $this->user_id = $obj->user_id; //award correct user!       $this->try_award($obj->position);    } } 

the cron function still work if parameter not supplied.

i've implemented reward system once in call document oriented database (this mud players). highlights implementation, translated php , mysql:

  • every detail badge stored in users data. if use mysql have made sure data in 1 record per user in database performance.

  • every time person in question something, code triggers badge code given flag, instance flag('post_message').

  • one event trigger counter, instance count of number of posts. increase_count('post_message'). in here have check (either hook, or having test in method) if post_message count > 300 should have reward badge, instance: flag("300_post").

  • in flag method, i'd put code reward badges. instance, if flag 300_post sent, badge reward_badge("300_post") should called.

  • in flag method, should have users previous flags present. when user has first_comment, first_post, first_read grant badge("new user"), , when 100_comment, 100_post, 300_read can grant badge("experienced_user")

  • all of these flags , badges need stored somehow. use way think of flags bits. if want stored efficiently, think of them bits , use code below: (or use bare string "000000001111000" if don't want complexity.

$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"; 
  • a nice way of storing document user use json , store users data in single text column. use json_encode , json_decode store/retrieve data.

  • for tracking activity on of users data manipulated other user, add data structure on item , use counters there well. instance read count. use same technique described above awarding badges, update should of course go owning users post. (for instance article read 1000 times badge).


Comments

Popular posts from this blog

android - Spacing between the stars of a rating bar? -

aspxgridview - Devexpress grid - header filter does not work if column is initially hidden -

c# - How to execute a particular part of code asynchronously in a class -