Skip to content

Instantly share code, notes, and snippets.

@cmaas
Last active August 29, 2015 14:15
Show Gist options
  • Save cmaas/347a6bab9abc9bc93005 to your computer and use it in GitHub Desktop.
Save cmaas/347a6bab9abc9bc93005 to your computer and use it in GitHub Desktop.
Laravel: Basic Value Object with Predefined Range of Accepted Values (Type Object)

Laravel: Basic Value Object with Predefined Range of Accepted Values

Your model has a list of accepted values. Say, your student score can be low, medium, or high. You want to store these values as Integers in the database and as a programmer, you want to work with constants like so:

Score::MEDIUM

Also, your HTML <select> field should have options for all available Constant values and label them with a translated label from your language file.

How do you glue all of this together?

My other gist shows a basic value object

But for this task, I extended ValueObject and call it TypeObject:

/**
 * Representation of a value object that accepts a range of predefined
 * Constants and labels their numeric representation with a translated string
 *
 * A TypeObject should use one or more Constants that are treated as the set of accepted values.
 * Each Constant is mapped to a string representation, but the string representation works
 * with keys for language files. This way, TypeObject can be used with different languages.
 *
 * @package Demo
 */
abstract class TypeObject extends ValueObject {

    /**
     * @var array Constant => Laravel string key for a language file
     */
    protected static $typeLabels = [];

    /**
     * @param mixed $value
     * @return string Translated string (uses Laravel's trans() method)
     */
    public static function label($value) {
        return isset(static::$typeLabels[$value]) ? trans(static::$typeLabels[$value]) : '';
    }

    /**
     * Returns an array of all labels for all constants
     *
     * @return array
     */
    public static function labels() {
        $translatedLabels = array();
        foreach(static::$typeLabels as $key => $label) {
            $translatedLabels[$key] = trans($label);
        }
        return $translatedLabels;
    }

    /**
     * Returns an array of all accepted constant values
     *
     * @return array
     */
    public static function values() {
        return array_keys(static::$typeLabels);
    }

    /**
     * Checks if $value is defined by a constant
     *
     * @param mixed $value
     * @throws \InvalidArgumentException if value validation fails
     */
    protected function validateValue($value) {
        if (!in_array($value, $this->values())) {
            throw new \InvalidArgumentException("Invalid value [$value] for " . static::class);
        }
    }
}

Basic Usage

A value object now can extend TypeObject like so:

class Score extends TypeObject {

    const LOW = 1;
    const MEDIUM = 2;
    const HIGH = 3;

    protected static $typeLabels = [
        self::LOW    => 'typelabels.student_score_low',
        self::MEDIUM => 'typelabels.student_score_medium',
        self::HIGH   => 'typelabels.student_score_high',
    ];
}

The model uses TypeObject just like ValueObject in my other gist (see above).

And the model can be used like this:

$student = new Student;
$student->name = 'John Smith';
$student->email = new EmailAddress('johnny@example.com');
$student->score = new Score(Score::MEDIUM);
$student->save();

See how you can work comfortablly with Constant values?

In your view, you can just do this:

{!! Form::select('score', Demo\Score::labels(), $student->score) !!}

This creates a nice dropdown and all options use the correct Constant values and map them to their translated labels:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment