Unit Testing VIII - An Improved User Class

Fixing some problems with the user class


In the previous article in this series, we created tests for our new user error system. We also saw some stubs. In this one, we'll see the new version of the User class that will pass the tests.


MODX logo

Here's the code of our new User class. It's actually called User2 and should go in the user2.class.php file.

<?php
    class User2 {
        protected $errors = array();
        protected $fields = array(
            'username' => '',
            'email' => '',
            'phone' => ''
        );

    function __construct($properties = array()){
        if (!empty($properties)) {
            foreach ($properties as $key => $value) {
                $this->set($key, $value);
            }
        }
    }

    /**
     * Set a User field's value if the value is valid
     * @param string $fieldName -- name of the field
     * @param string $value -- value to set
     * @return bool true on success, false on failure
     */
    public function set($fieldName, $value) {

        switch($fieldName) {
            case 'username':
                $valid = $this->validateUsername($value);
                $errorMsg = 'Invalid username';
                break;

            case 'email':
                $valid = $this->validateEmail($value);
                $errorMsg = 'Invalid email';
                break;

            case 'phone':
                $valid = $this->validatePhone($value);
                $errorMsg = 'Invalid phone';
                break;

            default:
                $valid = false;
                $errorMsg = 'Field Not Found';

        }

        if ($valid == false) {
            $this->addError($errorMsg);
        } else {
            $this->fields[$fieldName] = $value;
        }
        return $valid;
    }

    /**
     * Return the value of a specific field
     * @param string $fieldName -- field to get
     * @return mixed|null -- return field value
     *    or null for unknown field
     */
    public function get($fieldName) {
        if (array_key_exists($fieldName, $this->fields)) {
            return $this->fields[$fieldName];
        } else {
            $this->addError('Unknown Field: ' . $fieldName);
            return null;
        }
    }

    /**
     * Validate username
     * @param string $username
     * @return bool -- true if valid, false if not
     */
    public function validateUsername($username) {
        return (bool)(
            (strlen($username) <= 25) &&
            (strlen($username) >= 3)
        );
    }

    /**
     * Validate email
     * @param string $email
     * @return bool -- true if valid, false if not
     */
    public function validateEmail($email) {
        return (bool) strpos($email, '@') !== false;
    }


    /**
     * Validate Phone number
     *
     * @param string $phone
     * @return bool -- true if valid, false if not
     */
    public function validatePhone($phone) {
        $pattern = '/^[0-9\-,.:]+$/';
        return (bool) preg_match($pattern, $phone);
    }

    /**
     * Save User object
     * @param $user
     * @return bool
     */
    public function save() {
        return true;
    }

    /**
     * Add an error to $this->errors
     * @param string $error
     */
    public function addError($error) {
        $this->errors[] = $error;
    }

    /**
     * Get full array of errors
     * @return array
     */
    public function getErrors() {
        return $this->errors;
    }

    /**
     * Clear all errors
     */
    public function clearErrors() {
        $this->errors = array();
    }

    /**
     * Report if there are any errors
     * @return bool -- true if errors, false if not
     */
    public function hasErrors() {
        return !empty($this->errors);
    }

}

The code above is available at GitHub here.

We've added an $errors array class variable at the top to hold our array of error messages. We've also added these methods: addError() getErrors(), hasErrors(), and clearErrors(), which do just what their names suggest.

We've moved the field validation into the set() method. This makes some sense because we don't want to set the value unless it's valid. Our validation tests are still very minimal, but we'll see some more sophisticated validators, and tests for them, in the next two articles.

The validators are now in separate functions. validateUsername(), validateEmail(), and validatePhone(). Each method returns true for valid values and false for invalid ones. This makes them much easier to test (and to stub, which we'll see later on).


Possible Improvements

It might have occurred to you that our validators might be useful in other classes or other projects. It makes sense to put them in a Validator class that contains the three methods, and in the future might be expanded to validate other things like IP addresses, zip codes, or anything else that needs validating. That's the subject of our next two articles.

Moving the validators into a separate class has several advantages. First, it shouldn't really be part of the User class because it's not really a feature of a User object. Second, it allows us to tweak the validators or add new validation methods without messing with the User object at all or re-running its unit tests. Third, it allows the validation methods to be used for other objects or other projects. Finally, it lets you improve the individual validators in one spot and have the improvements available to all other classes that use the Validator class.


Coming Up

In the next two articles, we'll see tests for a Validator class and the class itself. We'll also see how to use a data provider to supply test cases for our tests, and we'll look at some more sophisticated validators.



For more information on how to use MODX to create a website, see my website Bob's Guides, or better yet, buy my book: MODX: The Official Guide.

Looking for high-quality, MODX-friendly hosting? As of May 2016, Bob's Guides is hosted at A2 hosting. (More information in the box below.)



Comments (0)


Please login to comment.

  (Login)