尝试从 Laravel 5.2 中的排队作业访问会话数据

Trying to access session data from a queued job in Laravel 5.2

我正在构建一个网络应用程序,允许人们与老师预约时间。按照目前的情况,一旦将时间添加到用户购物车中,其他人就无法看到该时间可用或将其添加到他们的购物车中。

我正在使用 darryldecode's shopping cart,只要会话持续(2 小时),项目就会存储在其中。但是,我希望项目在添加 5 分钟后自动从中删除。为了实现这一点,我设置了一个延迟 5 分钟的排队作业。

我的问题是作业更改了时间以在 5 分钟后再次对其他用户可见,但没有将他们从购物车中删除,因为它似乎无法访问存储购物车数据的会话.谁能建议我如何从排队的作业中访问存储在会话中的购物车数据? 谢谢。

这是作业的代码,除 Cart::remove()

外,一切正常
namespace App\Jobs;

use App\Jobs\Job;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\TimeSlot;
use Cart;

class RemoveTimeSlotFromCart extends Job implements ShouldQueue
{
    use InteractsWithQueue, SerializesModels;

    public $timeSlot;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct(TimeSlot $timeSlot)
    {
        $this->timeSlot = $timeSlot;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $timeSlot = $this->timeSlot;
        if ($timeSlot->booked == 0 && $timeSlot->in_basket == 1) {
            $timeSlot->in_basket = 0;
            $timeSlot->save();
            Cart::remove($timeSlot->id);
        }
    }
}

新会话中的 Cron 作业 运行。您的购物车库 (darryldecode/laravelshoppingcart) 是基于会话的。如果它不是来自当前用户的浏览器启动的会话,那么更改会话数据将非常困难(也许不可能?取决于您的会话驱动程序)。具体来说,这一行:

Cart::remove($timeSlot->id);

购物车模型无法知道要从哪个购物车中移除商品。从用户体验的角度来看——你会想要给用户一些反馈,告诉他们他们的时间段已经被取消了;因此,在每个页面绘制(或通过 js 异步)编写删除检查不应该是意外的工作。

在绘制的每个页面上,遍历访问者购物车中的每个项目。我不知道您是如何构建 cart::add(...) 来处理唯一的购物车商品 ID,因此这假设时间段 ID 与购物车商品 ID 匹配。

$removed = [];
foreach(Cart::getContent() as $item) {
    $timeSlot = timeSlot::findOrFail($item->id);
    if( $timeSlot->in_basket === 0 ) {
        Cart::remove($timeSlot->id);
    }
}
if(count($removed)) {
    //inject a HTML/JS popup message like "your timeslot has been dropped because you're too slow
    //or pass it along into the request
    $request->session()->flash('slots-dropped', $removed);
}

这最好在 cartUpdate 中间件中处理,但也可以在控制器中完成,具体取决于您站点的复杂性。

[编辑] 由于数据库结构的原因,上述解决方案存在缺陷-用户A保留了一个时间段但没有在结帐window内结帐,时间段在几分钟前返回到"not in cart"状态用户A通知;同时,在用户 B 返回到可用时隙池之后和用户 A 查看另一个页面之前(并且购物车使用适当的消息更新)之前,用户 B 看到了相同的时隙。此时时间段在 B 的购物车中。当页面刷新查看 A 的时间段是否在 "any" 购物车中时 - 它显示 "yes, its in a cart because $timeslot->in_basket == 1 "。人 A 继续结帐并获得时间段,即使它在人 B 的购物车中。

这个问题的修复相当简单——将 in_basket 的数据类型从布尔值更改为字符串,并在每次将其添加到购物车时为其分配 session_id 的值。从我在购物车 class 中看到的,可能是 app()->session->get('session')。你 var_dump(app()->session->all()) 在你承诺重构它之前先看看这是否正确。更新后的解决方案将如下所示:

$removed = [];
foreach(Cart::getContent() as $item) {
    $timeSlot = timeSlot::findOrFail($item->id);
    if( $timeSlot->in_basket !== app()->session->get('session') ) {
        Cart::remove($timeSlot->id);
    }
}
if(count($removed)) {
    //inject a HTML/JS popup message like "your timeslot has been dropped because you're too slow
    //or pass it along into the request
    $request->session()->flash('slots-dropped', $removed);
}