PHP: Error handling without throwing an exception - php

I'm sending multiple emails from a PHP application, and I want to inform the user of the emails that failed to be sent.
What is the most elegant way to do the error handling when
I don't want to throw an exception that terminates the sending of the remaining emails
The call goes through several method calls
What I want is to get the $notificationSucceeded back from Suggestion::notifyDeletionToAll() to SuggestionController somehow nicely from all notifications.
The depth of the call stack made me doubt if returning it through all the methods is the most elegant way, especially when I already have a return value from Suggestion::cancel().
Is there a better way?
Controller:
class SuggestionController {
function cancelSuggestion($suggestionId)
{
$suggestion = new Suggestion();
$suggestion->fetch($suggestionId);
$suggestionDeleted = $suggestion->cancel();
print json_encode(array(
'status' => 'ok',
'suggestionDeleted' => $suggestionDeleted,
));
}
}
Suggestion class:
class Suggestion {
/**
* Cancels membership of current user in the suggestion
*/
public function cancel()
{
$this->cancelMembership();
if (!$this->hasAcceptedMembers()) {
$this->deleteAndNotify();
return true;
}
return false;
}
/**
* Deletes the suggestion and notifies all the users in it
*/
private function deleteAndNotify()
{
$this->notifyDeletionToAll();
DB::inst()->query("DELETE FROM suggestions WHERE id = {$this->id}");
}
/**
* Notifies about the deletion of the suggestion to all members (users in the suggestion)
*/
private function notifyDeletionToAll()
{
$result = DB::inst()->query("SELECT user_id FROM suggestions_users
WHERE suggestion_id = {$this->id}");
while ($member_id = DB::inst()->fetchFirstField($result)) {
$member = new User();
$member->fetch($member_id);
$notificationSucceeded = $member->notifySuggestionDeleted($this);
}
}
}

I can't understand your question clearly. but i hope this will help you
$successfully_sent_arr = array();
$failure_notsent_arr = array();
if($mail->Send())
{
$successfully_sent_arr[] = $to_email;
//Once the last loop occurs, update this array in database
}
else
{
$failure_notsent_arr[] = $to_email;
//Once the last loop occurs, update this array in database
}

Related

How to access long polling results in wordpress plugin?

I am using Peepso plugin for my Wordpress website. Peepso is using Long polling for the part of the chats in order to create real-time chat. I don't know much on Long polling so I am confused in the way it is written inside the add-on file. What I want is to connect this website with my android application so I want to get those data into the android application in real-time. Here is the code of Peepso messages:
/**
* Creates a new message thread and adds the first message.
* #param PeepSoAjaxResponse $resp
*/
public function new_message(PeepSoAjaxResponse $resp)
{
$subject = '';
$message = htmlspecialchars($this->_input->raw('message'));
// SQL safe, forced to int
$participants = array_map('intval',$this->_input->value('recipients', array(), FALSE));
$creator_id = get_current_user_id();
$model = new PeepSoMessagesModel();
$msg_id = $model->create_new_conversation($creator_id, $message, $subject, $participants);
if (is_wp_error($msg_id)) {
$resp->success(FALSE);
$resp->error($msg_id->get_error_message());
$resp->set('subject', $subject);
$resp->set('creator_id', $creator_id);
} else {
$ps_messages = PeepSoMessages::get_instance();
do_action('peepso_messages_new_message', $msg_id);
$resp->success(TRUE);
$resp->notice(__('Message sent.', 'msgso'));
$resp->set('msg_id', $msg_id);
$resp->set('url', $ps_messages->get_message_url($msg_id));
}
}
/**
* Add a message to an existing conversation.
* #param PeepSoAjaxResponse $resp The response object to be sent.
*/
public function add_message(PeepSoAjaxResponse $resp)
{
$message = htmlspecialchars($this->_input->raw('content'));
$parent_id = $this->_input->int('parent_id');
$user_id = get_current_user_id();
// Make sure the "I am typing" flag is deleted
$mayfly = 'msgso_typing_' . $user_id. '_' . $parent_id;
PeepSo3_Mayfly::del($mayfly);
// And that another one can't be set for another full second
$mayfly_just_posted = "msgso_posted__{$user_id}_{$parent_id}";
PeepSo3_Mayfly::set($mayfly_just_posted,1,1);
$participants = new PeepSoMessageParticipants();
if (FALSE === $participants->in_conversation($user_id, $parent_id)) {
$resp->success(FALSE);
$resp->error(__('You are not part of this conversation.', 'msgso'));
} else {
$model = new PeepSoMessagesModel();
$msg_id = $model->add_to_conversation($user_id, $parent_id, $message);
if (FALSE === $msg_id) {
$resp->success(FALSE);
$resp->error(__('Failed to send message.', 'msgso'));
} else {
$resp->success(TRUE);
$resp->notice(__('Message sent.', 'msgso'));
$resp->set('msg_id', $msg_id);
}
}
}
And here is the js file that controls the long polling:
/**
* Starts long-polling to get chat stack state.
*/
startLongPolling: function () {
if (!SSE_ENABLED && !this.sessionExpired) {
this.stopLongPolling();
this.fetchChatState().done(
$.proxy(function () {
this.checkChatState();
}, this)
);
}
},
/**
* Stops long-polling to get chat stack state.
*/
stopLongPolling: function () {
if (!SSE_ENABLED) {
clearTimeout(this.checkChatTimer);
this.checkChatXHR && this.checkChatXHR.abort();
this.fetchChatXHR && this.fetchChatXHR.ret && this.fetchChatXHR.ret.abort();
this.checkChatXHR = false;
this.fetchChatXHR = false;
}
}
I tried to get those data by adding this line into the code echo json_encode($resp, JSON_PRETTY_PRINT); but nothing happened. So, how can I get the data from this code above? If you need any further information please let me know as I am new on long polling and how to connect it with android application. Any answer will be appreciated.

Form Submission Not Working with Gsuite email - but is with any other

I am a reasonably new developer and finally hit my first unsolvable problem!
I have used a reusable form and successfully tested it on two of my own Gmail accounts (example#gmail.com), however as soon as I use the client's professional G suite email (info#example.co.uk) it doesn't work, the email never arrives. I have checked & rechecked obvious things such as the spelling, cached files, spam & junk folders & re-uploaded the code several times. I have been through all of the Gsuite email settings and even added the IP to both the email whitelist & inbound gateway. Nothing works.
I am wondering if there is something in the MX records I have to do? I didn't try this as it worked with my other emails and there was nothing in the info about this.
I also considered contacting Gsuite to see if some kind of block exists in professional email?
Here is the code i am using:
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
/*
Tested working with PHP5.4 and above (including PHP 7 )
*/
require_once './vendor/autoload.php';
use FormGuide\Handlx\FormHandler;
$pp = new FormHandler();
$validator = $pp->getValidator();
$validator->fields(['name','email'])->areRequired()->maxLength(50);
$validator->field('email')->isEmail();
$validator->field('comments')->maxLength(6000);
$pp->sendEmailTo('info#example.co.uk'); // ← Your email here
echo $pp->process($_POST);
Update, I checked the Logs and I am getting this error Message:
PHP Fatal error: Uncaught Error: Class 'FormGuide\PHPFormValidator\FormValidator' not found in /home4/macatac5/public_html/anderscapeslandscaping.co.uk/src/FormHandler.php:41
This is the code it relates to:
<?php
namespace FormGuide\Handlx;
use FormGuide\PHPFormValidator\FormValidator;
use PHPMailer;
use FormGuide\Handlx\Microtemplate;
use Gregwar\Captcha\CaptchaBuilder;
/**
* FormHandler
* A wrapper class that handles common form handling tasks
* - handles Form validations using PHPFormValidator class
* - sends email using PHPMailer
* - can handle captcha validation
* - can handle file uploads and attaching the upload to email
*
* ==== Sample usage ====
* $fh = FormHandler::create()->validate(function($validator)
* {
* $validator->fields(['name','email'])
* ->areRequired()->maxLength(50);
* $validator->field('email')->isEmail();
*
* })->useMailTemplate(__DIR__.'/templ/email.php')
* ->sendEmailTo('you#website.com');
*
* $fh->process($_POST);
*/
class FormHandler
{
private $emails;
public $validator;
private $mailer;
private $mail_template;
private $captcha;
private $attachments;
private $recaptcha;
public function __construct()
{
$this->emails = array();
$this->validator = FormValidator::create();
$this->mailer = new PHPMailer;
$this->mail_template='';
$this->mailer->Subject = "Contact Form Submission ";
$host = isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:'localhost';
$from_email ='forms#'.$host;
$this->mailer->setFrom($from_email,'Contact Form',false);
$this->captcha = false;
$this->attachments = [];
$this->recaptcha =null;
}
/**
* sendEmailTo: add a recipient email address
* #param string/array $email_s one or more emails. If more than one emails, pass the emails as array
* #return The form handler object itself so that the methods can be chained
*/
public function sendEmailTo($email_s)
{
if(is_array($email_s))
{
$this->emails =array_merge($this->emails, $email_s);
}
else
{
$this->emails[] = $email_s;
}
return $this;
}
public function useMailTemplate($templ_path)
{
$this->mail_template = $templ_path;
return $this;
}
/**
* [attachFiles find the file uplods and attach to the email]
* #param array $fields The array of field names
*/
public function attachFiles($fields)
{
$this->attachments = array_merge($this->attachments, $fields);
return $this;
}
public function getRecipients()
{
return $this->emails;
}
/**
* [validate add Validations. This function takes a call back function which receives the PHPFormValidator object]
* #param function $validator_fn The funtion gets a validator parameter using which, you can add validations
*/
public function validate($validator_fn)
{
$validator_fn($this->validator);
return $this;
}
public function requireReCaptcha($config_fn=null)
{
$this->recaptcha = new ReCaptchaValidator();
$this->recaptcha->enable(true);
if($config_fn)
{
$config_fn($this->recaptcha);
}
return $this;
}
public function getReCaptcha()
{
return $this->recaptcha;
}
public function requireCaptcha($enable=true)
{
$this->captcha = $enable;
return $this;
}
public function getValidator()
{
return $this->validator;
}
public function configMailer($mailconfig_fn)
{
$mailconfig_fn($this->mailer);
return $this;
}
public function getMailer()
{
return $this->mailer;
}
public static function create()
{
return new FormHandler();
}
public function process($post_data)
{
if($this->captcha === true)
{
$res = $this->validate_captcha($post_data);
if($res !== true)
{
return $res;
}
}
if($this->recaptcha !== null &&
$this->recaptcha->isEnabled())
{
if($this->recaptcha->validate() !== true)
{
return json_encode([
'result'=>'recaptcha_validation_failed',
'errors'=>['captcha'=>'ReCaptcha Validation Failed.']
]);
}
}
$this->validator->test($post_data);
//if(false == $this->validator->test($post_data))
if($this->validator->hasErrors())
{
return json_encode([
'result'=>'validation_failed',
'errors'=>$this->validator->getErrors(/*associative*/ true)
]);
}
if(!empty($this->emails))
{
foreach($this->emails as $email)
{
$this->mailer->addAddress($email);
}
$this->compose_mail($post_data);
if(!empty($this->attachments))
{
$this->attach_files();
}
if(!$this->mailer->send())
{
return json_encode([
'result'=>'error_sending_email',
'errors'=> ['mail'=> $this->mailer->ErrorInfo]
]);
}
}
return json_encode(['result'=>'success']);
}
private function validate_captcha($post)
{
#session_start();
if(empty($post['captcha']))
{
return json_encode([
'result'=>'captcha_error',
'errors'=>['captcha'=>'Captcha code not entered']
]);
}
else
{
$usercaptcha = trim($post['captcha']);
if($_SESSION['user_phrase'] !== $usercaptcha)
{
return json_encode([
'result'=>'captcha_error',
'errors'=>['captcha'=>'Captcha code does not match']
]);
}
}
return true;
}
private function attach_files()
{
foreach($this->attachments as $file_field)
{
if (!array_key_exists($file_field, $_FILES))
{
continue;
}
$filename = $_FILES[$file_field]['name'];
$uploadfile = tempnam(sys_get_temp_dir(), sha1($filename));
if (!move_uploaded_file($_FILES[$file_field]['tmp_name'],
$uploadfile))
{
continue;
}
$this->mailer->addAttachment($uploadfile, $filename);
}
}
private function compose_mail($post)
{
$content = "Form submission: \n\n";
foreach($post as $name=>$value)
{
$content .= ucwords($name).":\n";
$content .= "$value\n\n";
}
$this->mailer->Body = $content;
}
}
Just a school boy MX error in the end. Hadn't double checked the details were matching and had an old record in there causing confusion. Thanks for the help anyways!
If you say that sending emails to a normal Gmail free account account works but you use a G Suite account it won't most likely is some G Suite settings or the DNS Domain settings the G Suite Domain.
If is as you said the MX record then refer to this:
Have you tried to send email from gmail to G suite (or from any other client, it doesn't matter), does it work?
To check if you MX record point at Goolge you can use this toolbox, you only have to put your domain and select MX: https://toolbox.googleapps.com/apps/dig/#MX/
If there are none (the records are shown in Bold), then you need to add the MX record for the Google Server, which are the following:
ASPMX.L.GOOGLE.COM : priorità 1
ALT1.ASPMX.L.GOOGLE.COM : priorità 5
ALT2.ASPMX.L.GOOGLE.COM : priorità 5
ALT3.ASPMX.L.GOOGLE.COM : priorità 10
ALT4.ASPMX.L.GOOGLE.COM : priorità 10
Set up MX records for G Suite Gmail: https://support.google.com/a/answer/140034?hl=en
If this is not the issue then it may be something else, if I understood right your code is sending to an email, and not actually using the account to send via SMPT
right?
Are there any other errors?
Are you the Super Admin of this G Suite Account?
Have tried to enable Less secure Apps? Control access to less secure apps: https://support.google.com/a/answer/6260879?hl=en

How should I return an error (and a message) in large project?

I'm writing a large project, and here's a class that I'll use it often:
class Star
{
/**
* Add
*
* Add a star to something.
*
* #param int $ID The ID of the thing.
*/
function Add($ID)
{
if($this->Starred($ID))
return 'You starred it already.';
if(!$this->Existing($ID))
return 'The one you tried to star does no longer exist.';
$this->DB->Star($ID);
return 'Starred successfully!';
}
}
$Star = new Star();
But I will use it in different ways like: single page or inside a function,
here's the problem, sometimes, I want to know the return code not the message,
but when I use it in the single page, I want it to return the messaage,
so if I change the Add() function to this:
function Add($ID)
{
if($this->Starred($ID))
return 0;
if(!$this->Existing($ID))
return 1;
$this->DB->Star($ID);
return 2;
}
I can now use it in my functions like this to handle an error:
/** Leaves a comment */
$Comment->Say('Hello.', $ID);
/** Auto star the post because we commented on it */
if($Star->Add($ID) == 2)
{
/** Remove the comment because the post does no longer exist */
$Comment->Remove('Hello.', $ID);
return 'Sorry ; _ ;, the post does no longer exist.';
}
but what if I need to return a message in many other pages?
I need to write this code every time?
switch($Star->Add($ID))
{
case 0:
return 'You starred it already.';
break;
case 1:
return 'The one you tried to star does no longer exist.';
break;
case 2:
return 'Starred successfully!';
break;
}
I'm just confuse about it, any help would be appreciated.
For a direct solution to your code read the Edit 1 section.
I'm currently working on a rather large project and I'm using a ErrorHandler class that I made. I found that working with a generic error handler class has made it easier.
class ErrorHandler
{
/**
* #var string an array containing all the errors set.
*/
private static $errors = [];
/**
* Set an error.
*
* #param string $error - The error message you'd like to set.
* #return string - The error being set to $errors array.
*/
public static function add($error)
{
return self::$errors[] = $error;
}
/**
* Get all the errors.
*
* #return boolean if the $errors array is empty it will return false, otherwise it will return the errors.
*/
public static function get()
{
foreach (self::$errors as $error) {
if (empty(trim($error)))
return false;
}
return self::$errors;
}
}
Basically how I use it is like this, say I needed to validate a form input say a login, I'd first check if the user pressed the submit button, then I'd use the ternary operator to run some validations and if it fails I use the ErrorHandler class.
if(isset($_POST['login'])) {
$emailAddress = someValidationsHere ? doSomethingWithValidInput : ErrorHandler::add("Email field is empty or format is invalid.");
$password = someValidationsHere ? doSomethingWithValidInput : ErrorHandler::add("Password field can't be empty and can't use special characters.");
if(!ErrorHandler::get()) {
echo User::login($emailAddress, $password, $autoLogin);
} else {
$errors = ErrorHandler::get();
foreach($errors as $error) {
echo $error . "<br/>";
}
}
}
So what that bottom if statement does is check if the ErrorHandler::get functions does not return false which in that case no need to show an error message and you can progress with the code, else it will display the error page, this way you can show multiple errors and have custom formatting.
I prefer this method as it is more of a long term solution as you may change the ID's then you'd have to go through all your code and change the code manually. Also it gives your code some sort of structure and that keeps your code clean.
Edit 1
How is this class? You now know the error codes using the const value and you can parse the error code to a message using the getMessage function. Also your code is more understandable and adaptable.
Why is it more...
understandable?
Because now when you (or someone else) looks at this code they see the clean name from the const so ALREADY_STARRED_ERROR will let the developer know instantly what the error means.
adaptable?
Well now you can change your hard coded errors and it wouldn't affect the code in anyway, so if in the future you wish to changed it because of a spelling mistake or other errors, you can change the array message.
<?php
class Star
{
const ALREADY_STARRED_ERROR = 1;
const NOT_FOUND_ERROR= 2;
const SUCCESSFUL_ENTRY = 3;
function getMessage($code)
{
$messages = [
1 => "You starred it already.",
2 => "The one you tried to star does no longer exist.",
3 => "Starred successfully!"
];
return $message[$code];
}
/**
* Add
*
* Add a star to something.
*
* #param int $ID The ID of the thing.
*/
function Add($ID)
{
if($this->Starred($ID))
return self::ALREADY_STARRED_ERROR;
if(!$this->Existing($ID))
return self::NOT_FOUND_ERROR;
$this->DB->Star($ID);
return self::SUCCESSFUL_ENTRY;
}
}
?>
I'd like to think Edit 1 addressed both the issues you had.
sometimes, I want to know the return code not the message,
but when I use it in the single page, I want it to return the messaage,
Place the switch to a function, like AddWithMessage:
function AddWithMessage($Star)
{
switch($Star->Add($ID))
{
case 0:
return 'You starred it already.';
break;
case 1:
return 'The one you tried to star does no longer exist.';
break;
case 2:
return 'Starred successfully!';
break;
}
}
Then use it across any single page you need instead of Add

Return unique id when use INSERT and SELECT in one moment

i am writing a program using mvc , and because the program i am writing is for admitting bank transactions, the resaults this program gives me is very important to me. and i need this to work without any logical errors and everything should workout with needed focus and giving me the exact thing i expect it to give. the thing that i want it to do for me is that some information will be sent out to the program with web service (like price, info, etc...) and then using a controller it checks the information and calls for the module, and sends the information to it. the it puts the module information to a stdclass and then using the PDO module it creates a transaction. and while the transaction is at work an insert command is sent for the database, and then we have a SELECT command and the program works well even after the insert command but while SELECT is at work , we have repetitive IDs which is the problem here:
my code:
//WSDL Creattor And Class
<?php
ini_set("soap.wsdl_cache_enabled", 1);
set_time_limit(300);
class wsdl extends Controller
{
public function index()
{
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') {
$servidorSoap = new SoapServer('http://' . $_SERVER['SERVER_NAME'] . '/wsdl');
$servidorSoap->setClass('wsdl');
$servidorSoap->handle();
}
}
public function request($pin = null, $amount = 1, $callback = null, $orderid = null, $desc = null)
{
if(empty($callback))
return -10; // call back is empty.
if (empty($pin) && $result->pin != $pin)
return -1; // pin invalid
$this->Model("payment");
$au = $this->mod->Request($result, $amount, $callback, $orderid, $desc);
return $au;
}
}
payment model:
<?php
class Payment_mod extends Model
{
public function Request($gateway, $amount, $callback, $orderid, $description)
{
// using pdo module
$this->DB->CONNECT->beginTransaction();
try {
$trans = new stdClass;
$trans->gateway_id = $gateway->id;
$trans->au = str_replace('.', '_temp_', microtime(true));
$trans->price = $amount;
$trans->order_id = $orderid;
$trans->ip = $_SERVER['REMOTE_ADDR'];
$trans->time = time();
$trans->call_back = $callback;
$trans->description = $description;
$this->DB->Insert("trans", $trans); // insert into table trans …
$id = $this->DB->Fetch("trans", "`gateway_id` =" . $trans->gateway_id . " AND `price`=".$trans->price." AND time = " . $trans->time)->id; // select id from trans where …
$u = new stdClass;
$u->t_id = $id;
$this->DB->Insert("test",$u); // insert into test . it for test table for check result
$this->DB->CONNECT->commit();
} catch (Exception $e) {
$this->DB->CONNECT->rollBack();
return -9; // return unknow error.
}
return $id;
}
}
for understanding where the problem exactly is and how many times each ID repeats itself, i added a table and i got the amount of return data of SELECT and put it into the database. and when i hold the f5 key for some seconds it shows that each ID repeats nearly 9 times.
screenshot:
http://uploads.im/biWEn.jpg
note: this picture shows the amount of repeat of IDs.
where is the problem? i need this to repeat a specific ID for each call.
i did the exact thing in YII framework and i had no problem there and the progam worked exactly as i needed it to. i'd be glad if you help me.

Implementing not automatic badges with PHP and MYSQL

I have users' table users, where I store information like post_count and so on. I want to have ~50 badges and it is going to be even more than that in future.
So, I want to have a page where member of website could go and take the badge, not automatically give him it like in SO. And after he clicks a button called smth like "Take 'Made 10 posts' badge" the system checks if he has posted 10 posts and doesn't have this badge already, and if it's ok, give him the badge and insert into the new table the badge's id and user_id that member couldn't take it twice.
But I have so many badges, so do I really need to put so many if's to check for all badges? What would be your suggestion on this? How can I make it more optimal if it's even possible?
Thank you.
optimal would be IMHO the the following:
have an object for the user with functions that return user specific attributes/metrics that you initialise with the proper user id (you probably wanna make this a singleton/static for some elements...):
<?
class User {
public function initUser($id) {
/* initialise the user. maby load all metrics now, or if they
are intensive on demand when the functions are called.
you can cache them in a class variable*/
}
public function getPostCount() {
// return number of posts
}
public function getRegisterDate() {
// return register date
}
public function getNumberOfLogins() {
// return the number of logins the user has made over time
}
}
?>
have a badge object that is initialised with an id/key and loads dependencies from your database:
<?
class Badge {
protected $dependencies = array();
public function initBadge($id) {
$this->loadDependencies($id);
}
protected function loadDependencies() {
// load data from mysql and store it into dependencies like so:
$dependencies = array(array(
'value' => 300,
'type' => 'PostCount',
'compare => 'greater',
),...);
$this->dependencies = $dependencies;
}
public function getDependencies() {
return $this->dependencies;
}
}
?>
then you could have a class that controls the awarding of batches (you can also do it inside user...)
and checks dependencies and prints failed dependencies etc...
<?
class BadgeAwarder {
protected $badge = null;
protected $user = null;
public function awardBadge($userid,$badge) {
if(is_null($this->badge)) {
$this->badge = new Badge; // or something else for strange freaky badges, passed by $badge
}
$this->badge->initBadge($badge);
if(is_null($this->user)) {
$this->user = new User;
$this->user->initUser($userid);
}
$allowed = $this->checkDependencies();
if($allowed === true) {
// grant badge, print congratulations
} else if(is_array($failed)) {
// sorry, you failed tu full fill thef ollowing dependencies: print_r($failed);
} else {
echo "error?";
}
}
protected function checkDependencies() {
$failed = array();
foreach($this->badge->getDependencies() as $depdency) {
$value = call_user_func(array($this->badge, 'get'.$depdency['type']));
if(!$this->compare($value,$depdency['value'],$dependency['compare'])) {
$failed[] = $dependency;
}
}
if(count($failed) > 0) {
return $failed;
} else {
return true;
}
}
protected function compare($val1,$val2,$operator) {
if($operator == 'greater') {
return ($val1 > $val2);
}
}
}
?>
you can extend to this class if you have very custom batches that require weird calculations.
hope i brought you on the right track.
untested andp robably full of syntax errors.
welcome to the world of object oriented programming. still wanna do this?
Maybe throw the information into a table and check against that? If it's based on the number of posts, have fields for badge_name and post_count and check that way?

Categories