[Laravel tips] How to temporarily change a laravel environment variable without updating the .env file

Imagine the following scenario: you are working on a laravel application that dispatches some jobs to a queue. You notice in your log file that a specific job is throwing an exception, but you can’t debug it directly (with dd() for instance), since the job is consumed by a worker elsewhere.

One way to debug this issue would be to change the QUEUE_DRIVER environment variable in your .env file to sync, debug, and then revert back the change after you finish.

Or imagine that you have a test database and you need to run the migrations on it first, and you’d need to update the .env file to use the test database first, run the migrations and then revert back the change.

As you can see, in both cases, all what we need to do is to update the environment variable temporarily. And usually, it takes some time to update the .env file (you might even need to clear the configuration or restart some workers).

Luckily, it is possible to pass an environment variable to linux commands, and these variables will only be used for the current execution.

In the previous example, all what we have to do is to execute the command as follows:

QUEUE_DRIVER=sync php artisan my-artisan-command-here

You can test it yourself with tinker.

You can even pass multiple environment variables like this:

QUEUE_DRIVER=sync DB_DATABASE=testing php artisan my-artisan-command-here

As you can see, this is not a laravel specific trick, and you can use it whenever you find yourself in a situation where you need to change an environment variable temporarily.

I hope you’ll find this useful.

[PoC] Password-Based user roles and triggers/actions (or how to use different passwords for multiple roles for the same account)

We have all seen a movie or two where a villain forces some innocent people, to give him the password to access her ultra secret account (a nuclear facility, or a Swiss bank account), the villain ends up getting access to the account and transfers all the money to his own account, or launch a nuclear weapon.
We have also all heard about authorities in some countries that force their citizens or even visitors to hand them their social media accounts and passwords. Usually the “victims” don’t have any choice and they end up providing their passwords.

One way to prevent this is maybe to activate multi level authentication on your account (2FA), and leave your phone, where you receive the one time password, at home when you travel, but this might not be a real solution in too many cases.

What if instead of all this, we have systems/websites with multiple roles/password for each account, and instead of using just one password all the time, we can use another one to tell the system “Hey system, when I use this particular password it means that I’m not in a secure place (or I’m forced to hand out my password) so please show just a restricted version of my account”, or even better “Hey system, when I use this particular password just disable my account, and do not accept any request to activate it for 30 days”.

I’ll explain in this article how we would implement such a system , for the sake of simplicity, I’ll focus just on the main concepts, and I’ll be using just a single controller, a single table for the users and their roles, and even use just 3 types of account: master, restricted and a trigger. I’ll be also using PHP/Laravel, but you can implement the same idea with the language of your choice.

Add multiple roles for the user

In order to add multiple roles for the user, we will be creating additional [sub-]account to the user.
To do so, we will create a users table migration that contains:


as you can can see here, the email is nullable, so we can create new accounts without an email (we can prevent the users from creating new “main” accounts without a password with a validation layer).
We are also referencing a master account, and the type of the account.

We can create a main account and two sub-accounts like the following example:

Basic/classical authentication

When we build a classical authentication in a Laravel application (without using the built it authController), we usually check if the password we get corresponds to the email like this:

public function postLogin(Request $request)
   $data = $request->all();
   $email = $data['email'];
   $password = $data['password'];
   if (Auth::attempt(['email' => $email, 'password' => $password]) {
      // redirect the user to the dashboard
   } else {
   // redirect the user back to the login page

in our new authentication system we will change that a little bit

first, we get all the accounts that belongs the email address:

$emailOwner = User::where('email', $email)→first();
$users = User::where('master_account_id', $emailOwner->id)->get();

and then we attempt to login to them one by one with the password we received:

foreach ($users as $user) {
   if (Auth::attempt(['id' => $user->id, 'password' => $password])) {
      return redirect('/home');
// if we finish the loop without finding a match, redirect the user back to the login page

note here that we are attempting the authentication with the user ID (not the email) and the password, and as soon as we find a match, we login with the found account.

As you can see here we created a multi-roles account and we chose the role based on the password the user used.

Add password-based triggers/actions

let’s add one more thing, lets add a way to trigger some actions when we login with one of the roles. To keep the example simple, let’s disable all the accounts associated with an email address if we authenticate with a specific password.

First lets add an additional field to the user account:


and the PostLogin method (the one we use to authenticate the user) will become like this:

function postLogin(Request $request)
    $data       = $request->all();
    $email      = $data['email'];
    $emailOwner = User::where('email', $email)->first();

    if (!$email) {
        return redirect()->route('login')->with('authentication-issue', true);

    $password   = $data['password'];
    $users      = User::where('master_account_id', $emailOwner->id)->get();
    foreach ($users as $user) {
        if (Auth::attempt(['id' => $user->id, 'password' => $password])) {
            if ($user->type == "trigger") {
            if ($user->disabled) {
                return redirect()->route('login')->with('account-disabled', true);
            return redirect('/home');

    return redirect()->route('login')->with('authentication-issue', true);

function trigger($userId)
    $users = User::where('master_account_id', $userId)->get();
    foreach ($users as $user) {
        $user->disabled = true;

when we login to our account with the password associated with the trigger account, the master account and all its sub-accounts get disabled.

You can find the demo in this repo:


How to avoid duplicates when you insert hundreds of thousands of entries into the same MySQL table

let’s assume that you are working on an application that requires all entries on a table (lets call it entries) to be unique.
If we are writing our application with PHP/Laravel the migration (more precisely its up() method) of the table would look like this:

 public function up()
        Schema::create('entries', function (Blueprint $table) {

One way to solve this issue (if not the most obvious one that comes to mind) is the following:

use a simple check, create a method called isDuplicated($entry) that searches for the entry in the table, if it doesn’t exist insert it, otherwise, throw an exception.

This method could look something like this (it should be added to the Entry model class):

public static function isDuplicated($inputs)
        $exists = self::where('parameter_001', $inputs['parameter_001'])
                       ->where('parameter_002', $inputs['parameter_002'])
                       ->where('parameter_003', $inputs['parameter_003'])
                       ->where('parameter_004', $inputs['parameter_004'])

        return $exists ? true : false;

PS: we could add a listener to the insertion, and do the check automatically when we use the Entry::create() method, but for the sake of simplicity we won’t do it in this tutorial.

This solution will work just fine, and it might be a good solution for the majority of cases, but lets assume that your application is big and it inserts millions of entries each day/hour. After a while you’ll definitely notice some duplicate entries.

How would this happen? You might ask, You are already checking the table before inserting. It turns out the problem arise when the application tries to insert the same entry twice on the exact same moment. Since the app checks at the same time whether the entry already exists on the table via the isDuplicated($entry) method, this one will return false for both operations, and we end up inserting the same entry twice.

The first solution that comes to mind now is the following: use some locking mechanism (at MySql level):

either by using transactions:
or Pessimistic Locking

in this case, we will make sure that we can’t insert the same row twice, right?

This seems like a very good solution, but if you think about it, when you try to insert a new entry, there is nothing to lock in the first place, we are not updating an existing row (a situation where locking would work just fine) but we are inserting a brand new one.

So the lock mechanism would be worthless in this case.

Another solution that you might think of, is to add an index to the entire table, a key composed of all the fields.

There is a problem that arise here (remember, we are talking about hundreds of thousands of entries) we might experience some performance issues really quickly, having an index on the whole table will slow down operations on it significantly.

Even if we don’t consider performance issues, we still have another problem.

Let say that we are trying to insert this entry twice:

['parameter_001'=> 'value_001',
'parameter_002'=> 'value_002',
'parameter_003'=> 'value_003',
'parameter_004'=> 'value_004'

as expected, the first one will get inserted, and the second one will get rejected (as expected as well) due to violation of the index/key we are using.

But if we try to insert the following entry twice:

['parameter_001'=> 'value_001',
'parameter_002'=> 'value_002',
'parameter_003'=> 'value_003'

it will get inserted twice without any problem. But why would this happen?

Remember that our fields in this table are nullable, this mean, when we try to insert a new entry, our “global” index will guard against entry that have all sub-keys present and that was already inserted. But if a sub-key is omitted, they check fails, and we end up with a duplicate entry.

even though this solution is not the right one, it gets us close to what we should do, in the sense that we should let MySql handle the issue instead of doing it in the code, we can use the same concept without compromising performance and without failing if a “sub-key” was omitted.

The solution for this problem would be to create an additional row that represents the hash of the entry, and lets call it hash, and this hash would play the role of a unique key, so when we try to insert the entry, we add its hash to it, and try to insert, if the operation goes through, i.e Mysql doesn’t throw any exception, then the entry is indeed unique, otherwise, MySql will throw an exception since it can’t insert two rows with the same unique key.

First we need to add this line to the up() method of the Entry table migration:


now, there is one more thing we should take care of in this situation, we need to make sure that the hash is unique, and there are no collusions. In other words, we can’t use one way hashing like md5 since we will eventually end up with two entries that have the same key, but something like base64 (or even just concatenate all the fields) would work

PS: don’t forget to add the fillable property to model class

protected $fillable = ['parameter_001', 'parameter_002', 'parameter_003', 'parameter_004', $hash];

another thing that we should definitely consider when we create the hash is to add a separator between the fields before we generate the hash, in other words, when we try to insert the following entry:

['parameter_001'=> 'value_001',
'parameter_002'=> 'value_002',
'parameter_003'=> 'value_003'

we should generate the hash for something like value_001-value_002-value_003

PS: do not use a simple implode() method here, since this will ignore fields that are not present in the entry.

the reason behind this, is that if we don’t add any separator, we would have false positives, when we have some missing parameters, and we are using the same value with different fields.
For instance, these following entries are not duplicates (i.e should be inserted without any problem) but MySql will reject them:

['parameter_001'=> 'value_001',
'parameter_002'=> 'value_002',
 'parameter_003'=> 'value_003'


['parameter_001'=> 'value_001',
'parameter_002'=> 'value_002',
'parameter_004'=> 'value_003',

because if we concatenate without any separators, we will end up with the hash of value_001value_002value_003 in the two cases, whilst when we add a separator, we will be generating the hash of value_001-value_002-value_003 for the first entry, and value_001value_002--value_003 (notice the double - sign) for the second one, which will generate a different hash than the first one.

To sum up, each time we want to insert a new entry, we will generate a unique hash for this entry, and we insert the hash with the entry, and since the hash has an index on it, duplicate entry won’t get inserted and Mysql will throw an exception instead.

What about the other exception that mysql might throw and that has nothing to do with the deduplication?

If we want to catch the exception related to this dedpulication mechanism (we might want to log it somewhere, or execute some logic when this happen), then catching all Exception won’t be a good solution, since MySQL might be throwing an exception for a completely different reason other than the duplcation exception.

To solve this issue, we need to analyze the exception thrown and execute the right logic accordingly.
If you take a look at Mysql documentation regarding the exception, you can find information about the duplication exception that we want to handle:

as you can see, when we try to insert a duplicate entry, MySQL throws an exception that has the sql state 23000 and error code 1062, so we can write a method to verify this:

 private function isDuplicateEntryException(QueryException $e)

      $sqlState = $e->errorInfo[0];
      $errorCode  = $e->errorInfo[1];
      if ($sqlState === "23000" && $errorCode === 1062) {

        return true;

      return false;

and then we can add this check in our code as follow:

// insert the entry
    catch (QueryException $e) {
           if ($this->isDuplicateEntryException($e)) {
            throw new DuplicateEntryException('Duplicate Entry');

           throw $e;