自分のサイトで使用するアチーブメントシステムを設計するための最良の方法を考えています。データベース構造は、3つ以上の連続したレコードが欠落していることを伝えるための最良の方法で見つけることができます。このスレッドは、実際には開発者からアイデアを得るための拡張機能です。
このウェブサイトでバッジ/達成システムについて多くの話をしているときに私が抱えている問題は、それだけです-それはすべて話であり、コードはありません。実際のコード実装例はどこにありますか?
ここで、人々が貢献してくれることを願っているデザインを提案し、拡張可能なアチーブメントシステムをコーディングするための優れたデザインを作成できることを願っています。これが最善だと言っているわけではありませんが、それは可能性のあるスターティングブロックです。
お気軽にご意見をお寄せください。
私のシステム設計のアイデア
一般的なコンセンサスは、「イベントベースのシステム」を作成することであるようです。投稿の作成、削除などの既知のイベントが発生するたびに、そのようにイベントクラスが呼び出されます。
$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クラスはメインバッジクラスを拡張するため、ロジック関数を再利用できます try_award
これに特化したクエリを作成する理由は、以前のイベントを「シミュレート」できるためです。つまり、すべてのユーザー投稿を調べて$event->trigger()
、特に多くのバッジの場合、非常に遅いようにイベントクラスをトリガーできます。そのため、代わりに最適化されたクエリを作成します。
どのユーザーが賞を受賞しますか?イベントに基づいて他のユーザーに授与することについてのすべて
Badge
クラスaward
関数は、に作用するuser_id
-彼らはいつも賞を与えられます。デフォルトでは、バッジはイベントを発生させた人、つまりセッションユーザーIDに授与されます(これはdefault_event
関数にも当てはまりますが、CRONジョブは明らかにすべてのユーザーをループし、個別のユーザーを授与します)
それでは、例を見てみましょう。コーディングチャレンジのWebサイトで、ユーザーはコーディングエントリを送信します。次に、管理者はエントリを判断し、完了すると、すべての人が見ることができるように結果をチャレンジページに投稿します。これが発生すると、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関数は引き続き機能します。