Estoy pensando en la mejor manera de diseñar un sistema de logros para usar en mi sitio. La estructura de la base de datos se puede encontrar en la mejor manera de saber que faltan 3 o más registros consecutivos y este hilo es realmente una extensión para obtener las ideas de los desarrolladores.
El problema que tengo cuando se habla mucho sobre insignias / sistemas de logros en este sitio web es solo eso: todo habla y no hay código. ¿Dónde están los ejemplos reales de implementación de código?
Propongo aquí un diseño en el que espero que la gente pueda contribuir y, con suerte, cree un buen diseño para codificar sistemas de logros extensibles. No digo que esto sea lo mejor, ni mucho menos, pero es un posible bloque de partida.
No dude en contribuir con sus ideas.
mi idea de diseño de sistema
Parece que el consenso general es crear un "sistema basado en eventos": cada vez que ocurre un evento conocido, como una publicación, se crea, elimina, etc., llama a la clase de evento así.
$event->trigger('POST_CREATED', array('id' => 8));
Luego, la clase de evento descubre qué insignias están "escuchando" para este evento, luego requires
ese archivo y crea una instancia de esa clase, así:
require '/badges/' . $file;
$badge = new $class;
Luego llama al evento predeterminado que pasa los datos recibidos cuando trigger
fue llamado;
$badge->default_event($data);
las insignias
Aquí es donde ocurre la verdadera magia. cada insignia tiene su propia consulta / lógica para determinar si se debe otorgar una insignia. Cada insignia se presenta, por ejemplo, en este 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
La función proviene de una clase extendida Badge
que básicamente verifica si el usuario ya recibió esa insignia, si no, actualizará la tabla db de la insignia. La clase de credencial también se encarga de recuperar todas las credenciales de un usuario y devolverlas en una matriz, etc. (para que las credenciales se puedan mostrar, por ejemplo, en el perfil del usuario)
¿Qué pasa cuando el sistema se implementa por primera vez en un sitio que ya está activo?
También hay una consulta de trabajo "cron" que se puede agregar a cada insignia. La razón de esto es porque cuando el sistema de insignias se implementa e inicia por primera vez, las insignias que ya deberían haberse ganado aún no se han otorgado porque se trata de un sistema basado en eventos. Por lo tanto, se ejecuta un trabajo CRON a pedido para cada insignia para otorgar lo que sea necesario. Por ejemplo, el trabajo CRON para lo anterior se vería así:
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 la clase cron anterior extiende la clase de insignia principal, puede reutilizar la función lógica try_award
La razón por la que creo una consulta especializada para esto es aunque podríamos "simular" eventos anteriores, es decir, revisar cada publicación de usuario y activar la clase de evento como $event->trigger()
si fuera muy lento, especialmente para muchas insignias. Entonces, en su lugar, creamos una consulta optimizada.
¿Qué usuario recibe el premio? todo sobre premiar a otros usuarios según el evento
La función de Badge
clase award
sigue actuando user_id
: siempre se les otorgará el premio. Por defecto, la insignia se otorga a la persona que CAUSÓ que ocurriera el evento, es decir, la identificación del usuario de la sesión (esto es cierto para la default_event
función, aunque el trabajo CRON obviamente recorre todos los usuarios y otorga premios a usuarios separados)
Así que tomemos un ejemplo, en un sitio web de desafío de codificación, los usuarios envían su entrada de codificación. Luego, el administrador juzga las entradas y, cuando está completo, publica los resultados en la página del desafío para que todos los vean. Cuando esto sucede, se llama a un evento POSTED_RESULTS.
Si desea otorgar insignias a los usuarios por todas las entradas publicadas, digamos, si se clasificaron entre los 5 primeros, debe usar el trabajo cron (aunque tenga en cuenta que esto se actualizará para todos los usuarios, no solo para ese desafío el se publicaron resultados para)
Si desea apuntar a un área más específica para actualizar con el trabajo cron, veamos si hay una manera de agregar parámetros de filtrado en el objeto del trabajo cron y obtener la función cron_job para usarlos. Por ejemplo:
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);
}
}
La función cron seguirá funcionando incluso si no se proporciona el parámetro.