队列作业完成后如何刷新组件?

How to refresh the component after the queue job is finished?

我有一个 table productsjob_statuses 使用这个 package: laravel-job-status

job_statuses 中有一个名为 status 的专栏,一旦队列作业完成,包就创建了这个专栏 finished

所以我在 products table 中创建了一个名为 job_status_id 的列(与 job_statuses 的关系)只是为了保存产品中的工作状态 ID table 看看这个作业是不是完成了!

简单地说,我使用 Livewire 创建了一个组件,用于在作业完成刷新组件时仅刷新单个产品:

class ProductIndex extends Component
{
    public $product;


    public function mount($product)
    {
        $this->product = $product;
    }

    public function render()
    {
        return view('livewire.merchant.product.index');
    }
}

内部 product-index 组件:

@if ($product->job_status->status == 'finished')
  // show real image
 
@else
  // show loader 
@endif

我的blade:

@foreach ($products as $product)
  <livewire:merchant.product.product-index :product="$product" :key="$product->id">
@endforeach

如果状态为完成,如何刷新产品组件?

您可以在组件中添加 event-listener,然后从页面上的任何位置触发事件 - 甚至从 JavaScript - 以刷新组件。

要添加侦听器以使组件自行刷新,只需将以下行添加到您的 ProductIndex 组件。

protected $listeners = ['refreshProducts' => '$refresh'];

Livewire 现在将侦听发出的任何 refreshProducts 事件,一旦发出,它将刷新该组件。

您还可以进一步自定义它,方法是将神奇的 $refresh 操作替换为可以向其传递参数的方法。如果将事件命名为与方法相同,则无需指定 key/value 对(事件名称为键,方法名称为值),仅值就足够了。这是一个例子,

protected $listeners = ['refreshProducts'];

// ...

public function refreshProducts($product_id = null) 
{
    // Refresh if the argument is NULL or is the product ID
    if ($product_id === null || $product_id === $this->product->id) {
        // Do something here that will refresh the event
    }
}

在 Livewire 组件中,您可以使用

发出事件
$this->emit('refreshProducts');` 

// or if you are passing arguments, as with the second example, 
$this->emit('refreshProducts', $productID);

您还可以从 JavaScript 发出事件,方法是

<script>
    Livewire.emit('refreshProducts')
</script>

如果你想让队列在事件完成后触发该事件,你需要实现一些东西来轮询服务器询问“作业是否完成”然后触发事件,或者您可以使用 Laravel Echo 作为 websocket。这将允许您触发和侦听来自 Livewire 生态系统外部的事件。

轮询

When you're polling, you don't have to emit an event for every update, as the component will refresh itself continuously .

轮询是持续更新 Livewire 组件的最简单方法,它不需要像 Laravel Echo 那样的任何 websockets 集成。这意味着每隔 X 秒(默认为 2 秒),您的组件将向您的服务器发出一个 AJAX 请求,以获取其最新数据,并 re-render 自身使用新数据。

这很容易通过使用 wire:poll 属性包装组件来实现 - 这是一个使用 5 秒的示例。

<div wire:poll.5s>
    <!-- The rest of your view here -->
</div>

但是,这意味着该组件的所有实例都将 re-render 自身并向服务器发出它们自己的 AJAX 请求以获取最新数据。您可能想为所有项目制作一个“父”组件,从而只有 1 个单一组件 re-render.

广播和 Websockets

我假设您已经 installed Laravel Echo。现在——要实现广播这个功能,首先需要创建一个事件。 运行 命令

php artisan make:event ProductStatusUpdated

这将在您的 app\Events 文件夹中创建一个活动。根据需要自定义它。 运行命令后看起来像这样,

class ProductStatusUpdated
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

我们可以将频道从 PrivateChannel('channel-name') 更改为 Channel('products'),这样我们就可以在 Livewire 的 products 频道中收听它(您可以命名任何你想要的频道,你也可以收听私人事件 - 在 Livewire 文档中有相关文档,在这个答案的底部引用)。

所以这意味着 broadcastOn 看起来像这样

public function broadcastOn()
{
    return new Channel('products');
}

接下来,在作业完成其工作并设置所有状态后,使用

从Laravel触发该事件
event(new \App\Events\ProductStatusUpdated);

现在我们需要更新 Livewire 组件中的侦听器,以便我们可以通过 Laravel Echo(而不是我们之前执行的 Livewire 事件)实际侦听该频道上的广播。

protected $listeners = ['echo:products,ProductStatusUpdated' => 'refreshProducts'];

大功告成!您现在正在使用 Laravel Echo 广播一个事件,Livewire 截获该事件,然后运行。侦听器现在是一个 key/value 对,其中值仍然是组件中的方法名称(如果您愿意,您仍然可以使用魔法操作 $refresh 代替),键是 channel,eventecho:.

为前缀

资源: