【问题标题】:How to override database connection for failed job insertion in laravel 5?如何覆盖 laravel 5 中作业插入失败的数据库连接?
【发布时间】:2016-03-23 21:07:27
【问题描述】:

我正在尝试开发一个多租户多数据库应用程序,这基本上意味着每个租户都有自己的数据库、自己的用户、资源等。

当请求进来时,Laravel 需要知道使用哪个数据库连接,所以我编写了一个中间件,它基本上解析请求中的 JWT 并查找租户 ID 或用户名,然后简单地连接到租户的数据库。

p>

但是现在我正在处理队列,并且我正在尝试覆盖 laravel 5 的默认行为,该行为连接到主数据库并插入失败的作业记录。

当我挖掘供应商文件时,我发现了一个FailedJobProvider接口:

<?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();
}

还有一个实现该接口的 DatabaseFailedJobProvider 类:

<?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);
    }
}

所以我想如果我编写自己的提供程序或者可以覆盖这个提供程序,我将能够在插入失败的作业之前告诉 laravel 连接到哪个数据库。但我对 SOLID 或 OOP 不太熟悉,并且对在这种情况下该怎么做感到困惑。

如何编写我自己的提供程序或覆盖这个提供程序以随时更改数据库连接?

【问题讨论】:

    标签: php laravel laravel-5 queue


    【解决方案1】:

    我知道这已经很晚了,但我遇到了同样的问题。我想到了。因此,对于遇到此问题的其他任何人,这是如何完成的:

    首先,您需要创建自己的失败作业提供程序类来实现FailedJobProviderInterface 接口。我建议将代码从Illuminate\Queue\Failed\DatabaseFailedJobProvider 复制到您的自定义类中,然后简单地更改它以使其工作,但您需要它。 Laravel 使用这个类中的其余函数做多项事情,并且类本身需要匹配实现的接口。

    我只是更改了日志方法,将附加数据记录到我的数据库中的附加列中。

    在服务提供者(您自己的或默认服务提供者)中完成此操作后,您需要包含刚刚创建的新的失败作业提供者类。

    然后在服务提供者的boot方法中放入如下代码:

    // 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']);
    
    });
    

    该代码正在覆盖this registration done by Laravel

    为了代码清晰,您可以将该代码作为一个函数放在服务提供者中,并在启动方法中运行该方法。

    如果您想将不同的数据记录到数据库中,请确保同时更新失败的作业迁移和/或数据库表。

    现在,当作业失败时,您的自定义代码将按照您的意愿运行记录失败的作业,即使您更新 Laravel 版本,它也应该可以正常工作。

    【讨论】:

      【解决方案2】:

      我什至迟到了,但在研究这个主题时发现了这篇文章,所以我想我也会给出自己的解决方案。

      我发现处理这个问题最简洁的方法是使用(取消)序列化。

      我总是将SerializesModels trait 分配给我的工作,我以这种方式“扩展”了它(为清晰起见进行了简化):

      <?php
      
      namespace App\Jobs\Traits;
      
      use Illuminate\Queue\SerializesModels;
      
      trait RestoresTenant
      {
          use SerializesModels {
              SerializesModels::__serialize as serialize;
              SerializesModels::__unserialize as unserialize;
          }
      
          /**
           * The tenant ID.
           *
           * @var string
           */
          protected $tenantId;
      
          /**
           * Save the current tenant before serialization.
           *
           * @return array
           */
          public function __serialize()
          {
              // SET THE CURRENT TENANT ID HERE, OR WHATEVER YOU USE TO IDENTIFY IT
              // $this->tenantId = ...
      
              return $this->serialize();
          }
      
          /**
           * Restore the tenant upon unserialization.
           *
           * @param  array $values
           * @return array
           */
          public function __unserialize(array $values)
          {
              // RETRIEVE THE TENANT ID AND RESTORE THE DATABASE CONNECTION HERE
              // $tenantId = $values["\0*\0tenantId"];
              // ...
      
              return $this->unserialize($values);
          }
      }
      

      我的作业使用RestoresTenant trait,这意味着租户数据库连接在其余作业的属性被反序列化之前恢复,甚至在作业中间件执行之前。

      基本上,除非出现问题在反序列化开始之前,失败的作业将记录在正确租户的数据库中。

      【讨论】:

        猜你喜欢
        • 2015-01-13
        • 1970-01-01
        • 2021-11-07
        • 2020-11-26
        • 2016-09-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多