Laravel get logged in user in Observer when event is triggered from queued job

Viewed 435

I'm not sure if this is possible or not, but if it is it will save me lots of duplicate code. I have many queued jobs that handle importing csv data. I need to track the changes that have been made in these imports and who made the changes. I implemented observers for each model and I can log the changes but I can't get the user that imported the data since the changes take place in a queued job. The job itself is aware of the user since I am passing the user to the job, but I can't figure out how to pass the user to the observer. Does anyone have any ideas on how to do this?

I'm using the Laravel Excel package to handle the imports, and this package also integrates queuing the jobs. Here's the code from the import job:

class QueuedImport implements ToCollection, WithChunkReading, ShouldQueue
{
    use Importable;

    private $user;
    private $client;

    public function __construct($user, $client)
    {
        $this->user = $user; // this is the logged in user
        $this->client = $client;
    }

    public function collection(Collection $rows)
    {
        //each row updates a model, which triggers the update event in the observer
        $this->importRows($rows, $this->client, $this->user);

    }

    public function chunkSize(): int
    {
        return 500;
    }

    //....
}

And here's the observer's update event:

public function updated(MyModel $model)
    {
        $original = $model->getOriginal();
        $user = auth()->user();
        foreach ($model->getChanges() as $key => $value) {
            \App\History::create([
                'user_id' => $user->id ?? null,
                'model_id' => $model->id,
                'field' => $key,
                'old_value' => $original[$key],
                'new_value' => $value
            ]);
        }
    }

When I update the model and it's not in a queued job, the user saves fine. I understand that queued jobs are not aware of the authenticated user, but there must be some way to get the user into the observer.

I realize I could just track the change in the Job, but I have many places this model could be updated, and I don't want to worry about finding all the different spots it may be updated. Can this be done, and how?

2 Answers

I am not super happy with this solution, but maybe this is gonna work.

You might want to implement the laravel excel events with the Auth facade.

use Auth;
use Maatwebsite\Excel\Concerns\WithEvents;

class QueuedImport implements ToCollection, WithChunkReading, ShouldQueue, withEvents
{
    use Importable;

    private $user;
    private $client;

    public function __construct($user, $client)
    {
        $this->user = $user; // this is the logged in user
        $this->client = $client;
    }

    public function collection(Collection $rows)
    {
        //each row updates a model, which triggers the update event in the observer
        $this->importRows($rows, $this->client, $this->user);

    }

    public function chunkSize(): int
    {
        return 500;
    }

    public function beforeImport()
    {
        Auth::loginUsingId($this->user->id);
    }

    public function afterImport()
    {
        Auth::logout();
    }

    //....
}

The best I could come up with was not ideal, but I couldn't think of another way to do it. I ended up adding a column to each of the tables I am observing, updated_by. So when the model is updated, I populate that field with the currently logged in user. Then I have access to that field in the observer through the model. So this is what I have now in the observer:

public function updated(MyModel $model)
    {
        $original = $model->getOriginal();
        $user = $model->created_by; // <--- this is the new field I added
        foreach ($model->getChanges() as $key => $value) {
            \App\History::create([
                'user_id' => $user,
                'model_id' => $model->id,
                'field' => $key,
                'old_value' => $original[$key],
                'new_value' => $value
            ]);
        }
    }
Related