Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

arsh's avatar
Level 1

Enum VS Foreign Key and Resolving on frontend.

Hi guys i am badly stuck in a Application design problem and need recommendation from you guys. I have lot of fields which have only few value (between 1 to 10)and most of them are unchangeable like YES, No or Option 1, Option 2, Option 3

I have 3 choices to implement a design in database

I should insert values as a string ( Which i consider not a good thing for large scale data).

I should create enums (in mysql) for that values (I feel thats good option but i have read some articles saying to avoid)

I should create lookup tables for these options and use foreign key constraint in parent table. (but in this case i also have arround 30 to 40 lookup tables)

If i trade off between these choice what i get is:

In Opt 1 i get database size, and peformance issues due to string scanning for query and experts may not consider it as a good design

In Opt 2 i didn't feel any performance issue but most of people saying if i have to add new value i may have issues with table restructuring and also i can't get values for options displaying in html

In Opt 3 i don't want to use joins as there may be more then 30 to 40 lookup tables to display complete record and if i don't use join join so i may need to create array to lookup values against 1,2,3,4 options or i have to use if or switch statements.

Many of developers said to me that i should go for lookup tables there is no issue with having 30 to 40 lookup tables, Please also recommend me should i create models for these lookup tables or just used approach jeffery way used in projectflyer 5th episode for countries. I would really thank full to you experts if you help me out.

Note: i also want to ask expert is having a lookup array against database lookup table good practice ?

0 likes
7 replies
Borisu's avatar

Hi,

I've been in your position before. I used to think that the third option is the best. It can scale good if you need to add new options etc. But nowadays I'm not really sure it's worth it unless you're 100% sure you're going to change the values. My suggestion is to do the enum option, at least for obvious fields like Yes/No and some simpler examples. Anything more complex could be parked in an intermediate table. One other option is to save these values in a json field. This way you can just change it whenever you want without even updating the database ;) Also worth considering: using non-relational database or even a graph database. The latter is awesome for complex relationships between objects - take a look at neo4j for example. Best of luck!

arsh's avatar
Level 1

Thanks for your kind reply, i can't store data as a json as i have to generate reporting on these facts fields. well if i store data in form of int and use lookup table for constraints. and when i have to retrive from database if i avoid lookup table join for performance improvement how should i write code to resolve int into actual value, should i create enum kinda string or i am thinking to cache these lookup table collection forever and use these tables to resolve when showing values.

lostdreamer_nl's avatar

As it looks like the values in your lists wont change (often) I would probably go for a class where the enumeration happens.

Save the integers to the DB and use a single class (or a single class that uses Chain of Responsibility to forward it's requests to other classes) in combination with some attribute mutators to make sure all data goes into the DB as an integer and comes out of your model as a string.

arsh's avatar
Level 1

Can you share some example using enumeration class. should i create a seperate class for Enumeration Like ExperienceLevelEnum having array and functions or create array in model like Job Model ?

lostdreamer_nl's avatar
Level 53

That would ultimately depend on the amount of enums and the possible categorization of them.

You dont want to create any god classes in this (class that handles just about anything) so you might start out with something like:

Class FormEnumeration {
    
    protected static $enums = [
        'field1' => [
            '0' => 'No',
            '1' => 'Yes',
            '2' => 'FileNotFound'  // joking
        ],
        'role' => [
            '1' => 'Employe',
            '2' => 'Manager',
            '3' => 'CEO',
            '4' => 'Product Owner',
        ]
    ];

    public static function toString($fieldName, $int)
    {
        if(!isSet(self::$enums[$fieldName])) {
            Throw new \Exception($fieldName .' is not listed in enums');
        }
        if(!isSet(self::$enums[$fieldName][$int])) {
            Throw new \Exception($int .' is not a valid integer for field '. $fieldName);
        }
        return self::$enums[$fieldName][$int]
    }


    public static function toInteger($fieldName, $value)
    {
        if(!isSet(self::$enums[$fieldName])) {
            Throw new \Exception($fieldName .' is not listed in enums');
        }
        $key = array_search($value, self::$enums[$fieldName]);
        if($key === false) {
            Throw new \Exception($value .' is not a valid value for field '. $fieldName);
        }
        return $key;
    }
}

And you could use it in the job model as such:


Class Job extends Model {

    public function getRoleAttribute($roleId) 
    {
        return FormEnumeration::toString('role', $roleId);
    }

    public function setRoleAttribute($roleName) 
    {
        $this->attributes['role'] = FormEnumeration::toInteger('role', $roleName);
    }

}

This would save integers to the DB, and when you ask for an attribute, it will translate it via the FormEnumeration class.

When the class gets too big, you can set it up to handle things differently from within the FormEnumeration Class, for instance by delegating it to different classes for each field:


Class FormEnumeration {
    
    public static function toString($fieldName, $int)
    {
        $className = self::getClassName($fieldName);
        return $className::toString($int);
    }

    public static function toInteger($fieldName, $value)
    {
        $className = self::getClassName($fieldName);
        return $className::toString($value);
    }
    
    public static function getClassName($fieldName) 
    {
        $className = ucfirst($fieldName) . 'FieldEnumeration';
        if(!class_exists($className)) {
            Throw new \Exception('There is no enumeration class for field '. $fieldName);
        }
        return $className;
    }
}

Class RoleFieldEnumeration {
    
    protected static $enums = [
        '1' => 'Employe',
        '2' => 'Manager',
        '3' => 'CEO',
        '4' => 'Product Owner',
    ];
    
    
    public static function toString($int)
    {
        if(!isSet(self::$enums[$int])) {
            Throw new \Exception('There is no value for '. $int .' in the Role field');
        }
        return self::$enums[$int]
    }
    
    public static function toInteger($value)
    {
        $key = array_search($value, self::$enums);
        if($key === false) {
            Throw new \Exception($value .' is not a valid value for field Role');
        }
        return $key;
    }
}

Creating a separate class for each field (or form, whatever works best in your situation) The job model would stay the same as in the previous example, but the FormEnumeration class will delegate it to a field specific class where needed.

2 likes
arsh's avatar
Level 1

@lostdreamer_nl , Thanks for your solution. and for display these options i should inject this class into view right ?

lostdreamer_nl's avatar

@arsh, with the get...Attribute and set...Attribute methods, their values are already automagically replaced.

Just us the Job model like you would always:


$job = Job::find(1);
$job->role = 'manager';
$job->save();  //  '2'  gets saved into the DB

echo $job->role;  // outputs 'manager'  again

Please or to participate in this conversation.