ATOM-Group's avatar

Enum tables and models. What do you guys do for naming conventions?

So, I was recently taking a stroll through the database structure of one of my projects, and even though I lived and breathed this project for months, my gut reaction was "what.... the... fuck....". Tables, tables everywhere.

But after inspecting them, I realized most of them are harmless enum tables, which got me thinking, why shouldn't I name enum tables as such? And that got me thinking even more, should I use Eloquent models for those enum tables to make all of my queries stay in Eloquent land, or should I suck it up and use Eloquent models only for my main data entities, and then use joins to get the enum data I need?

What do you guys do to keep your databases organized and models from proliferating?

0 likes
9 replies
bestmomo's avatar

It is a shame not to use Eloquent to manage relations with enum tables (or any other one). From my point of view it is not annoying to have many models, simply well organize them.

pmall's avatar

What do you mean by enum table ?

1 like
ATOM-Group's avatar

An enum (enumeration) table is a table used for normalization of a database. It enumerates available options for a specific column in another table (which is different from an enum column type).

Consider an account table. One of the columns would be billing_period_id which corresponds to an enumeration table of allowed options called billing_periods.

id - name
-----------------------
1 - Weekly
2 - Bi-Weekly
3 - Monthly
4 - Bi-Monthly
5 - Semi-Annually
6 - Annually

You would then use that enum table for both normalization, as well as populating a dropdown selector when allowing the customer to select the billing period they want.

pmall's avatar

Hum ok. At first glance I would keep access them with Eloquent (mainly for relationships) but if there is many of them I would put them under another namespace than my main entities.

thepsion5's avatar

I feel like enum tables are kind of a weird concept, since an enum doesn't change enough to justify storing it as a database table. I typically store the enum as a tinyint and then use a simple enum class to define class constants for the corresponding value. This is a class I tossed together in a few minutes for my current project:

class OathType
{
    const CONSTITUTIONAL        = 1;
    const ELECTION_COMMISSION   = 2;
    const GENERAL_ASSEMBLY      = 3;
    const GOVERNOR_APPOINTMENT  = 4;
    const JUDICIAL              = 5;

    protected static $typeLabels = array(
        self::CONSTITUTIONAL        => 'Constitutional',
        self::ELECTION_COMMISSION   => 'Election Commission',
        self::GENERAL_ASSEMBLY      => 'General Assembly',
        self::GOVERNOR_APPOINTMENT  => 'Governor',
        self::JUDICIAL              => 'Judicial'
    );

    /**
     * @param int $typeValue
     * @return string
     */
    public static function label($typeValue)
    {
        return isset( static::$typeLabels[$typeValue] ) ? static::$typeLabels[$typeValue] : '';
    }

    /**
     * @return array
     */
    public static function labels()
    {
        return static::$typeLabels;
    }

    /**
     * @return array
     */
    public static function values()
    {
        return array_keys( static::$typeLabels );
    }
}

If it got much more complex than that I'd probably use this solid enum package so I could do things like $type = OathType::CONSTITUTIONAL(); and function setType(OathType $type) { }.

7 likes
ATOM-Group's avatar

@thepsion5, so then if you had say, and Oath Eloquent model which had a column called type which then stored the corresponding integer, in your model would you have something like this?

class Oath extends Eloquent  {
     public function getTypeAttribute($value)
     {
          return (new OathType)->label($value);
     }
}

echo $oath->type; 
thepsion5's avatar

I'd have that as a function like $oath->getTypeLabel() instead of an accessor, otherwise i'd be worried about accidentally saving the label to the database instead of the value.

pmall's avatar

I'd have that as a function like $oath->getTypeLabel() instead of an accessor, otherwise i'd be worried about accidentally saving the label to the database instead of the value.

It should work with accessors / mutators no ? Elegant solution by the way.

cm's avatar

This is exactly what I was looking for. Two additional things:

  1. I would use accessors / mutators like @tag / @pmall suggested.

  2. I modified the type class to support localization like so:

class OathType {

    const CONSTITUTIONAL = 1;
    const ELECTION_COMMISSION = 2;
    const GENERAL_ASSEMBLY = 3;
    const GOVERNOR_APPOINTMENT = 4;
    const JUDICIAL = 5;

    protected static $typeLabels = array(
        self::CONSTITUTIONAL       => 'typelabels.oath_constitutional',
        self::ELECTION_COMMISSION  => 'typelabels.oath_election_commission',
        self::GENERAL_ASSEMBLY     => 'typelabels.oath_general_assembly',
        self::GOVERNOR_APPOINTMENT => 'typelabels.oath_governor',
        self::JUDICIAL             => 'typelabels.oath_judicial'
    );

    public static function label($typeValue) {
        return isset(static::$typeLabels[$typeValue]) ? trans(static::$typeLabels[$typeValue]) : '';
    }

    public static function labels() {
        return array_map(function ($label) {
            return trans($label);
        }, static::$typeLabels);
    }

    public static function values() {
        return array_keys(static::$typeLabels);
    }
}

Then, I create a file typelabels.php in resources/lang/en and all other language folders that contain the labels.

And to use this thing:

$oath = new Oath;
$oath->type = OathType::GENERAL_ASSEMBLY;
$oath->save(); // stores 3 in DB

echo $oath->type; // outputs my translated label

Or maybe $oath->setType(OathType::GENERAL_ASSEMBLY); and validate that the passed parameter is an existing key in the OathType class. Otherwise you could call $oath->type = 7; and it still would work.

Or do validation in the setTypeAttribute mutator. However, it's probably better to introduce some basic EnumType class that always checks if the setter sets an existing key.

1 like

Please or to participate in this conversation.