Estou pensando na melhor maneira de projetar um sistema de conquistas para usar em meu site. A estrutura do banco de dados pode ser encontrada em Best way to tell 3 ou mais consecutivos records ausentes e este thread é realmente uma extensão para obter as idéias dos desenvolvedores.
O problema que tenho com muita conversa sobre sistemas de emblemas / conquistas neste site é apenas isso - é tudo conversa e nenhum código. Onde estão os exemplos reais de implementação de código?
Proponho aqui um design com o qual espero que as pessoas possam contribuir e, com sorte, criem um bom design para a codificação de sistemas de realização extensíveis. Não estou dizendo que seja o melhor, longe disso, mas é um possível ponto de partida.
Fique à vontade para contribuir com suas idéias.
minha ideia de design de sistema
Parece que o consenso geral é criar um "sistema baseado em eventos" - sempre que um evento conhecido ocorre como uma postagem é criada, excluída, etc., ele chama a classe de evento assim ..
$event->trigger('POST_CREATED', array('id' => 8));
A classe de evento então descobre quais emblemas estão "ouvindo" este evento, em seguida, requires
naquele arquivo, e cria uma instância dessa classe, assim:
require '/badges/' . $file;
$badge = new $class;
Em seguida, chama o evento padrão passando os dados recebidos quando trigger
foi chamado;
$badge->default_event($data);
os emblemas
É aqui que a verdadeira magia acontece. cada emblema tem sua própria consulta / lógica para determinar se um emblema deve ser concedido. Cada crachá é definido, por exemplo, neste formato:
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
A função vem de uma classe estendida Badge
que basicamente verifica se o usuário já recebeu esse emblema, caso contrário, atualizará a tabela de banco de dados do emblema. A classe de crachás também se encarrega de recuperar todos os crachás de um usuário e devolvê-los em uma matriz, etc (para que os crachás possam ser exibidos, por exemplo, no perfil do usuário)
e quando o sistema é implementado pela primeira vez em um site já ativo?
Há também uma consulta de trabalho "cron" que pode ser adicionada a cada emblema. A razão para isso é porque quando o sistema de crachás é implementado e inicializado pela primeira vez, os crachás que já deveriam ter sido ganhos ainda não foram concedidos porque este é um sistema baseado em eventos. Portanto, um trabalho CRON é executado sob demanda para cada emblema para premiar tudo o que precisa ser. Por exemplo, o trabalho CRON para o acima seria assim:
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);
}
}
}
Como a classe cron acima estende a classe de emblema principal, ela pode reutilizar a função lógica try_award
A razão pela qual eu criei uma consulta especializada para isso é que poderíamos "simular" eventos anteriores, ou seja, percorrer cada postagem do usuário e acionar a classe de evento como $event->trigger()
se fosse muito lento, especialmente para muitos emblemas. Em vez disso, criamos uma consulta otimizada.
qual usuário recebe o prêmio? tudo sobre como premiar outros usuários com base no evento
A função da Badge
classe award
atua user_id
- eles sempre receberão o prêmio. Por padrão, o emblema é concedido à pessoa que CAUSOU o evento acontecer, ou seja, o ID do usuário da sessão (isso é verdadeiro para a default_event
função, embora o trabalho CRON obviamente passe por todos os usuários e conceda usuários separados)
Então, vamos dar um exemplo, em um site de desafio de codificação, os usuários enviam sua entrada de codificação. O administrador então avalia as inscrições e, quando concluídas, publica os resultados na página de desafio para que todos possam ver. Quando isso acontece, um evento POSTED_RESULTS é chamado.
Se você quiser premiar os usuários por todas as entradas postadas, digamos, se eles foram classificados entre os 5 primeiros, você deve usar o cron job (embora tenha em mente que isso será atualizado para todos os usuários, não apenas para aquele desafio os resultados foram postados para)
Se você deseja direcionar uma área mais específica para atualizar com o cron job, vamos ver se há uma maneira de adicionar parâmetros de filtragem ao objeto do cron job e obter a função cron_job para usá-los. Por exemplo:
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);
}
}
A função cron ainda funcionará mesmo se o parâmetro não for fornecido.