2
\$\begingroup\$

I am creating a social sharing counter plugin for my WordPress blog. I want to fetch the counts of number of likes, shares, Facebook comments (I am using Facebook comment box), twitter tweet counts and a count of how many users have added a particular article to reading list.

The way I have implemented is the following:

When a user goes to the single post page, the markup contains an article element and it contains data attributes like data-Facebook-like, data-Facebook-share, data-twitter etc. which contains the current values of the database. Now when the page is loaded we send Ajax requests to Facebook, Twitter and my own reading list API and fetch the count then, compare the count with the current values of the database and if the current values are unequal to the value fetched then it sends an update Ajax request to the plugin which updates the counter.

Code that is executed to handle update request:

static function __update_activity_value( $activity_value ) {
    global $wpdb;

    $wpdb->query( $wpdb->prepare( "INSERT INTO wp_activity_value 
        (post_id, comment, facebook_like, facebook_share, linkedin, pininterest, googleplus, email, twitter, bookmark, other, inlinecomment, total) 
        VALUES (%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d) ON DUPLICATE KEY UPDATE 
        comment = %d, facebook_like = %d, facebook_share = %d, linkedin = %d, pininterest = %d, googleplus = %d, 
        email = %d, twitter = %d, bookmark = %d, other = %d, inlinecomment = %d, total = %d;",
        $activity_value->post_id, $activity_value->comment, $activity_value->facebook_like, $activity_value->facebook_share, 
        $activity_value->linkedin, $activity_value->pininterest, $activity_value->googleplus, $activity_value->email, 
        $activity_value->twitter, $activity_value->bookmark, $activity_value->other, $activity_value->inlinecomment, $activity_value->get_total_count(),

        $activity_value->comment, $activity_value->facebook_like, $activity_value->facebook_share, $activity_value->linkedin, 
        $activity_value->pininterest, $activity_value->googleplus, $activity_value->email, $activity_value->twitter,
        $activity_value->bookmark, $activity_value->other, $activity_value->inlinecomment, $activity_value->get_total_count() ) );

}

static function update_activity_value() {

    check_ajax_referer( 'update_activity_value' . get_current_user_id(), 'mp_nonce' );

    $type = isset($_GET['type']) ? $_GET['type'] : NULL;
    $new_count = isset($_GET['count']) ? $_GET['count'] : NULL;
    $post_id = isset($_GET['postId']) ? $_GET['postId'] : NULL;

    if( is_null($type) || is_null($new_count) || is_null($post_id) ) {
        wp_send_json("Error");
    }

    $activity_value = self::get_activity_value( $post_id );

    switch ($type) {
        case 'comment':
            $activity_value->comment = $new_count;
            break;
        case 'facebook_like':
            $activity_value->facebook_like = $new_count;
            break;
        case 'facebook_share':
            $activity_value->facebook_share = $new_count;
            break;
        case 'twitter':
            $activity_value->twitter = $new_count;
            break;
        case 'email':
            $activity_value->email = $new_count;
            break;
        case 'linkedin':
            $activity_value->linkedin = $new_count;
            break;
        case 'pininterest':
            $activity_value->pininterest = $new_count;
            break;
        case 'googleplus':
            $activity_value->googleplus = $new_count;
            break;
        case 'bookmark':
            $activity_value->bookmark = $new_count;
            break;
        case 'inlinecomment':
            $activity_value->inlinecomment = $new_count;
            break;
        case 'other':
            $activity_value->other = $new_count;
            break;
        default:
            break;
    }
    self::__update_activity_value( $activity_value );
    wp_send_json( $activity_value->get_total_count() );
}

Questions:

  1. Is my approach for updating the count correct?
  2. The update things works well in development, but on a live site, I have noticed some weird behavior sometimes. I believe it might be because of parallel updates, so how can I add locks to provide mutual exclusion?
\$\endgroup\$
2
  • \$\begingroup\$ Are the values passed via AJAX the new total for that field? Can you explain "weird behavior"? \$\endgroup\$ May 24 '14 at 19:13
  • \$\begingroup\$ Yes the value we get from twitter, facebook etc. is the total value for the type (for eg: facebook likes) for that particular post. \$\endgroup\$
    – Ahmed
    May 24 '14 at 19:22
2
\$\begingroup\$

One improvement you can make for DRYness is to replace the switch with a simple if. This is possible because the AJAX call passes the field name in the type parameter (they all seem to match).

static $FIELD_TYPES = array(
    'comment' => true,
    'facebook_like' => true,
    'facebook_share' => true,
    'twitter' => true,
    'email' => true,
    'linkedin' => true,
    'pininterest' => true,
    'googleplus' => true,
    'bookmark' => true,
    'inlinecomment' => true,
    'other' => true,
);

...

if (isset(self::$FIELD_TYPES[$type])) {
    $activity_value->$type = $new_count;
}

Update

Since each AJAX call sends a single counter's value, you're losing updates when two readers view the same story. To fix this, denormalize the counter table:

create table wp_activity_value (
    post_id int         not null,
    counter varchar(20) not null,
    value   int         not null,
    updated timestamp   not null on update current_timestamp,

    primary key ( post_id, counter )
)

Now when two calls collide, it will always be for the same counter (e.g. facebook_like). Even if they have different new values, the worst that can happen is getting the lower value (perhaps you could pick the larger in your on duplicate key clause). The updated timestamp isn't necessary, but it's a nice feature.

\$\endgroup\$
6
  • \$\begingroup\$ I like your suggestion about DRYness and will implement it. Apart from that, each ajax gives a new value for the field but those belong to a particular type of a particular post. So in this case the post-id is the primary key. \$\endgroup\$
    – Ahmed
    May 24 '14 at 19:25
  • \$\begingroup\$ @Ahmed See my update for how to fix the lost updates problem. \$\endgroup\$ May 24 '14 at 19:36
  • \$\begingroup\$ Yes I got it. What do you think about fetching the counts on the client side and sending it to server for update. Do you think its reliable? \$\endgroup\$
    – Ahmed
    May 24 '14 at 19:45
  • \$\begingroup\$ My first inclination is to have the server do it, but then the client needs to poll to get the updated values. One problem with letting the client do it is that it's not safe. Anyone can set any value they want manually by inspecting the AJAX calls. \$\endgroup\$ May 24 '14 at 19:48
  • \$\begingroup\$ yes this is the problem, how about the other way around, let the client check and when it pings server for change, server should ping the 'facebook/twitter' server to fetch the updated count. So we are not relying anymore on the client's count. \$\endgroup\$
    – Ahmed
    May 25 '14 at 9:08

Not the answer you're looking for? Browse other questions tagged or ask your own question.