Last Updated: February 25, 2016
·
534
· zejesago

JavaScript Conditionals with Different Server Configurations

November 30, 2013

Background

I’ve been developing a website recently that uses Laravel for the API and AngularJS for the front-end. The time then came that I had to present the website for beta testing on the staging server.

Using a MySQL database, I stored booleans as 0s and 1s in TINYINT data types (since MySQL doesn’t have a boolean data type). For example, retrieving data from the model would typically look something like $post->is_published, which would return either a 0 or 1.

Everything ran smoothly except for one thing: JavaScript conditional statements.

The Problem

After Google-ing the whole morning for similar issues, it turns out that PHP’s MySQL drivers handle the returned type differently. libmysql returns the 0s and 1s as strings while mysqlnd returns as integers. Since my development/local server is using PHP 5.4, mysqlnd is the default driver used. However, the server was installed with PHP 5.3 and is using the libmysql driver. Therefore, locally, I had been expecting my attributes to return integers, but was return strings on the server instead.

Although it doesn’t pose an immediate threat to PHP, this can potentially break JavaScript code. Apparently, JavaScript handles strings as boolean, differently with PHP. Consider the following examples:

Handling conditional statements in PHP doesn’t break the logic.

var_dump((bool) 0);    // bool(false)
var_dump((bool) 1);    // bool(true)
var_dump((bool) ‘0’);  // bool(false)
var_dump((bool) ‘1’);  // bool(true)

JavaScript, however breaks the same rules.

!! 0;    // false
!! 1;    // true
!! ‘0’;  // true
!! ‘1’;  // true

Possible Solutions

There are many ways of addressing this type of problem. It’s simply a matter of practicality.

  1. Update the staging server to PHP 5.4
  2. Change the staging server’s MySQL driver to mysqlnd.
  3. Refactor all of my JavaScript’s conditional statements.
!! 1==0;    // false
!! 1==1;    // true
!! 1==‘0’;  // false
!! 1==‘1’;  // true

The Solution

My colleague suggested utilizing the framework’s model and looping through each of the attribute before returning it. I initially thought of using Laravel’s Accessors & Mutators, but due to time constraints, I eventually followed his suggestion. So far, all of the issues have passed testing. Here’s how I implemented the fix.

Create a base model class (thanks dlf1987 for the idea).

<?php

class BaseModel extends Eloquent {

    /**
     * Convert the model's attributes to an array.
     *
     * @return array
     */
    public function attributesToArray()
    {
        $attributes = parent::attributesToArray();
        foreach ($attributes as $key => $attribute)
        {
            if ('0' === $attribute)
            {
                $attributes[$key] = 0;
            }
            elseif ('1' === $attribute)
            {
                $attributes[$key] = 1;
            }
        }
        return $attributes;
    }

}

After including this in your app, change all of your other models to extend BaseModel instead of Eloquent. I know it’s not elegant, but it’s sure is simple enough to work. The attributesToArray() is available in Illuminate\Database\Eloquent\Model alias Eloquent. It is called by toArray() which is called by toJson(), which is also called by __toString(). Simply put, most of the direct output (queries returned immediately to the client) will pass through attributesToArray().