DEV Community

Matt Daneshvar
Matt Daneshvar

Posted on • Edited on

Alternatives to using a constants.php file in your Laravel project

One of the common patterns I have come across in Laravel projects is the use of a constants.php file carrying a long list of all constants used across the application.

While there may be good use cases for having such a file, I'm writing this article to suggest a few alternative approaches. My intent is not to advertise these as "best practices", but leave it for you to decide what fits best your specific situation.

Let's assume that our original constants.php file looks like this:

const ADMIN_ROLE = 1;
const EDITOR_ROLE = 2;
const PUBLISHER_ROLE = 3;
Enter fullscreen mode Exit fullscreen mode

Configuration files

As pointed out by the most upvoted answer in this popular thread, one way to manage constant values in your Laravel app is to park them as configurations.

My only variation to this approach is to use a more specific file name depending on the context, which in this case could be roles.php:

<?php

return [
  'admin' => 1,
  'editor' => 2,
  'publisher' => 3
];
Enter fullscreen mode Exit fullscreen mode

This would allow me to access those values throughout my application using:

config('roles.admin'); // Using helper functions

Config::get('roles.admin'); // Using facades
Enter fullscreen mode Exit fullscreen mode

The benefit of this approach compared to using a plain constants.php file is that it uses the framework's original mechanics to store and access values. Laravel configurations are familiar to most developers, and so people new to your code won't have to learn something new to use them.

The downside is that, unless you use a plugin such as Laravel Idea, most IDEs won't autocomplete config keys for you.

Constants in your classes

Another approach, which is more inline with OOP, is to keep your constants in the specific context (i.e. classes) they belong.

In the case of user role constants, for example, we could keep everything under the User model class:

class User extends Model {
  const ADMIN_ROLE = 1;
  const EDITOR_ROLE = 2;
  const PUBLISHER_ROLE = 3;

  public function isAdmin(){
    return $this->role === static::ADMIN_ROLE;
  }
}
Enter fullscreen mode Exit fullscreen mode

To take this one step further, you may consider creating a dedicated Role class that contains all values/logic related to roles:

class Role {
  const ADMIN = 1;
  const EDITOR = 2;
  const PUBLISHER = 3;
}
Enter fullscreen mode Exit fullscreen mode

This would enable a more expressive syntax when you're trying to access your constants:

class User extends Model {  
  public function isAdmin(){
    return $this->role === Role::ADMIN;
  }
}
Enter fullscreen mode Exit fullscreen mode

Enum values

Another alternative to constants that are related to a specific model is to use enumerated (ENUM) types at your database level.

When using your migration for your users table, you may use the enum function to create your column:

public function up()
{
  Schema::create('users', function (Blueprint $table) 
  {
    $table->enum('role', ['admin', 'editor', 'publisher']);
    // Other columns...
  });
}
Enter fullscreen mode Exit fullscreen mode

While ENUMs are compact values (e.g. in MySQL they are automatically encoded as numbers behind the scenes), they allow you to deal with readable string values in your application:

class User extends Model {
  public function isAdmin(){
    return $this->role === 'admin';
  }
}
Enter fullscreen mode Exit fullscreen mode

If you are going for this approach, you should note that not every database engine supports ENUMs (e.g. MySQL and PostgreSQL both support ENUMs, but SQLite doesn't).

What do you think?

What are some other values you store in a constants.php file? Where else do you place your constants?

Top comments (8)

Collapse
 
plweil profile image
Peter Weil

I really like the class-based constants idea, as opposed to dumping everything into the constants file. Compare, for example,

return $this->status === config('constants.status.published')
Enter fullscreen mode Exit fullscreen mode

or even

return $this->status === config('status.published')
Enter fullscreen mode Exit fullscreen mode

with

return $this->status === Status::PUBLISHED
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mattfletcher profile image
Matt Fletcher

PHP 8.1 Enums one day soon

Collapse
 
tradjick profile image
tradjick

I use all of the above depending on usage and context. Regarding the db, I use both in class constants and enum fields within the model classes and migrations. I will have something like:

class Terminal extends Model
{
const STATE_REGISTERED='registered';
const STATE_UPDATE='update';
const STATE_LOCKED='locked';
const STATES=[self::STATE_REGISTERED,self::STATE_UPDATE,self::STATE_LOCKED];
...

and within the migration:
$table->enum('status',\App\Terminal::STATES);

The above means there is a singular authoritative source, that is detectable by the IDE, is contextual within the model, reduces record size for storage, speeds searches, and is enforced on the data storage level.

The only caveat with this technique is the migration changes if you change the authoritative class constant.

Collapse
 
vladi160 profile image
vladi160

I am sure constants in the database isn't good practice at all

Collapse
 
mattdaneshvar profile image
Matt Daneshvar • Edited

I think it really comes down to the context and the specific reason behind each group of constants in an app. I don't see ENUMs as constants in the database, but rather as alternatives to having certain constants in the first place.

For example, I spoke to a friend whose constants file looked like this:

const ADMIN_ROLE = 1;
const EDITOR_ROLE = 2;
const PUBLISHER_ROLE = 3;
Enter fullscreen mode Exit fullscreen mode

His justification for using these was so that:

  • He could store integers (as a somewhat lighter / quicker type) instead of strings
  • He had a reference so that he didn't mess up his data with arbitrary values

He later removed his constants and used an ENUM database column. In his case:

  • His database still stores a small numeric value behind the scenes
  • He gets data integrity at database level (Arbitrary strings won't be stored... Even though ENUM behaviour is quite crappy in handling invalid values).
  • He deals with simple readable string values (e.g. 'admin' and 'editor') in his app without needing to define a set of constants.

It's hard to say if this is the right way, but I guess it's an alternative approach for him to address his goals.

The biggest drawback about database ENUMs I can think of is the fact that the app (and the IDE) won't have any idea about the ENUMs in your database, which would mean no autocompletion in the IDE and no integrity checks at the application level.

Are there other drawbacks you have mind? I think I should eventually add more details to this article to highlight the benefits and drawbacks of each approach better.

Collapse
 
vladi160 profile image
vladi160

I mean:

  1. a query just for config values. One more request for a data, which is static, but in DB is dynamic. You can store it in any kind of a file like .json, .txt, etc, even you can create your own format and there is .env.

  2. I guess, it will be hard to debug, if u use some DB values and thread them as constants or a config.

Collapse
 
edisao profile image
Edison Ramirez

No considero buena práctica el agregar un ID, preferible agregar un código para luego obtenerlo de la base de datos

Collapse
 
ssianik profile image
Syed Sirajul Islam Anik

Class based constants.