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;
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
];
This would allow me to access those values throughout my application using:
config('roles.admin'); // Using helper functions
Config::get('roles.admin'); // Using facades
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;
}
}
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;
}
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;
}
}
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...
});
}
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';
}
}
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)
I really like the class-based constants idea, as opposed to dumping everything into the constants file. Compare, for example,
or even
with
PHP 8.1 Enums one day soon
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.
I am sure constants in the database isn't good practice at all
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:
His justification for using these was so that:
He later removed his constants and used an ENUM database column. In his case:
'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.
I mean:
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
.I guess, it will be hard to debug, if u use some DB values and thread them as constants or a config.
No considero buena práctica el agregar un ID, preferible agregar un código para luego obtenerlo de la base de datos
Class based constants.