19
May '14

“Whoa there! Don’t cross the streams!” I hear you cry, and I don’t necessarily disagree with you. Passing by reference is second only to eval* and goto* calls with its ability to reduce decent code into an impenetrable forest of obfuscated chaos. However, when used with restraint, in certain circumstances passing by reference can be a very useful tool.

Consider for a moment you are writing a user management tool and, in the best ORM fashion, you are making your controller as thin as possible and getting all of your business logic into your models. You might have an add user action that looks something like this (examples are in RESTful Laravel ‘cos it’s awesome):

public function store() {
    if($input = \Input::all()) {
        if(\User::create($input)) {
            \Session::flash('success', 'User added');
            return \Redirect::route('user.index');
        }
        else {
            \Session::flash('error', 'Could not add user');
            return \Redirect::route('user.create')->withInput();
        }
    }
    else {
        \Session::flash('error', 'No input given');
        return \Redirect::route('user.create');
    }
}

So that’s about as thin as we can make a controller, but we have no field validation here and it’s not very intelligent. So what if we overrode the model’s default create method:

public static function create($input) {
    $rules = array (
        'first_name' => 'required',
        'last_name' => 'required',
        'email' => 'required|email'
    );

    $validation = \Validator::make($input, $rules);

    if($validation->passes())
        return parent::create($input);
    else
        return false;
}

It’s better but we still don’t have our validation messages being passed back. Now there are three options from this point; we can move our validation into the controller (less than ideal as it bloats our controller), we can have some sort or logic where you get a boolean value back from the model if validation passes or your array of error messages if it fails (which takes some figuring out once we get back to the controller), or we can pass our error messages back out of the model by reference:

Controller:

public function store() {
    if($input = \Input::all()) {
        if(\User::create($input, $messages)) {
            \Session::flash('success', 'User added');
            return \Redirect::route('user.index');
        }
        else {
            \Session::flash('error', 'Could not add user');
            return \Redirect::route('user.create')->withInput()->withErrors($messages);
        }
    }
    else {
        \Session::flash('error', 'No input given');
        return \Redirect::route('user.create');
    }
}

Model:

public static function create($input, &messages = array()) {
    $rules = array (
        'first_name' => 'required',
        'last_name' => 'required',
        'email' => 'required|email'
    );

    $validation = \Validator::make($input, $rules);

    if($validation->passes())
        return parent::create($input);
    else {
        $messages = $validation->messages();
        return false;
    }
}

Here we have the best of both worlds; we’re returning a boolean to indicate success or failure, but we have also passed our validation messages back by reference. As such, if validation failed we are able to send the validation messages back to the view without introducing any additional business logic or bloat to the controller.

* Don’t ever ever use eval or goto calls – if you find yourself considering these in your code then step away from your computer, go into a quiet darkened room and think carefully about your life choices. Programming is not for you.
It actually amazes me that PHP still supports these calls, but I do have respect for whatever genius wrote the PHP man page for goto.