Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override database connection for failed job insertion in laravel 5?

I am trying to work on a multi tenant multi database application which basically means that every tenant has its own database, own users, resources etc.

Naturally when a request comes in, Laravel needs to know which DB connection to use so I wrote a middleware which basically parses the JWT in the requests and looks for the tenant id or username then simply connects to the tenant's database.

But now I am working with queues, and I am trying to over ride the default behaviour of laravel 5 which connects to the main DB and inserts the failed job record.

When I dug into the vendor files, I found a FailedJobProvider interface:

<?php

namespace Illuminate\Queue\Failed;

interface FailedJobProviderInterface
{
    /**
     * Log a failed job into storage.
     *
     * @param  string  $connection
     * @param  string  $queue
     * @param  string  $payload
     * @return void
     */
    public function log($connection, $queue, $payload);

    /**
     * Get a list of all of the failed jobs.
     *
     * @return array
     */
    public function all();

    /**
     * Get a single failed job.
     *
     * @param  mixed  $id
     * @return array
     */
    public function find($id);

    /**
     * Delete a single failed job from storage.
     *
     * @param  mixed  $id
     * @return bool
     */
    public function forget($id);

    /**
     * Flush all of the failed jobs from storage.
     *
     * @return void
     */
    public function flush();
}

And a DatabaseFailedJobProvider class which implements that interface:

<?php

namespace Illuminate\Queue\Failed;

use Carbon\Carbon;
use Illuminate\Database\ConnectionResolverInterface;

class DatabaseFailedJobProvider implements FailedJobProviderInterface
{
    /**
     * The connection resolver implementation.
     *
     * @var \Illuminate\Database\ConnectionResolverInterface
     */
    protected $resolver;

    /**
     * The database connection name.
     *
     * @var string
     */
    protected $database;

    /**
     * The database table.
     *
     * @var string
     */
    protected $table;

    /**
     * Create a new database failed job provider.
     *
     * @param  \Illuminate\Database\ConnectionResolverInterface  $resolver
     * @param  string  $database
     * @param  string  $table
     * @return void
     */
    public function __construct(ConnectionResolverInterface $resolver, $database, $table)
    {
        $this->table = $table;
        $this->resolver = $resolver;
        $this->database = $database;
    }

    /**
     * Log a failed job into storage.
     *
     * @param  string  $connection
     * @param  string  $queue
     * @param  string  $payload
     * @return void
     */
    public function log($connection, $queue, $payload)
    {
        $failed_at = Carbon::now();

        $this->getTable()->insert(compact('connection', 'queue', 'payload', 'failed_at'));
    }

    /**
     * Get a list of all of the failed jobs.
     *
     * @return array
     */
    public function all()
    {
        return $this->getTable()->orderBy('id', 'desc')->get();
    }

    /**
     * Get a single failed job.
     *
     * @param  mixed  $id
     * @return array
     */
    public function find($id)
    {
        return $this->getTable()->find($id);
    }

    /**
     * Delete a single failed job from storage.
     *
     * @param  mixed  $id
     * @return bool
     */
    public function forget($id)
    {
        return $this->getTable()->where('id', $id)->delete() > 0;
    }

    /**
     * Flush all of the failed jobs from storage.
     *
     * @return void
     */
    public function flush()
    {
        $this->getTable()->delete();
    }

    /**
     * Get a new query builder instance for the table.
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function getTable()
    {
        return $this->resolver->connection($this->database)->table($this->table);
    }
}

So I guess if I write my own provider or could over ride this one, I would be able to tell laravel which database to connect to before inserting the failed job. But I am not so seasoned with SOLID or OOP, and get confused what to do in situations like these.

How would write my own provider or over ride this one to change the database connection on the go?

like image 731
Rohan Avatar asked Oct 31 '22 13:10

Rohan


1 Answers

I know this is super late but I ran into this same issue. I figured it out. So for anyone else running into this issue, this is how this is done:

First, you need to create your own failed job provider class implementing the FailedJobProviderInterface interface. I would suggest copying the code from Illuminate\Queue\Failed\DatabaseFailedJobProvider into your custom class and simply change it to work however you need it to. Laravel uses the rest of the functions in this class do multiple things and the class itself needs to match the interface implemented.

I simply changed the log method to log additional data into additional columns in my DB.

Once that's done then in a service provider, your own or a default one, you need to include the new failed job provider class you just created.

Then in the boot method of the service provider put the following code:

// Get a default implementation to trigger a deferred binding
$_ = $this->app['queue.failer'];

//regiter the custom class you created
$this->app->singleton('queue.failer', function ($app) {

    $config = $app['config']['queue.failed'];
    return new NAMEOFYOURCLASS($app['db'], $config['database'], $config['table']);

});

That code is overwriting this registration done by Laravel.

For code clarity, you can put that code as a function inside the service provider and just run the method in the boot method as well.

If you want to log different data into the DB then make sure to also update the failed jobs migration and or DB table as well.

Now when a job fails, your custom code will run logging failed jobs however you want and even if you update your Laravel version, this should keep working just fine.

like image 165
abetwothree Avatar answered Nov 09 '22 09:11

abetwothree