在Laravel 5

时间:2017-09-30 20:09:57

标签: php postgresql laravel session laravel-5

我刚刚开始在Laravel 5.5中使用database会话驱动程序以及PostgreSQL,但我遇到了一些轻微的不便。

我想将last_activity列保留为timestamp with timezone,但是Laravel想要在其中写入一个整数,有时它也会尝试根据整数值从中删除。

我尝试在Session模型中执行以下操作:

public function setLastActivityAttribute($ts){
    $this->attributes['last_activity'] = date('c', $ts);
}

适用于保存,但是当Laravel尝试垃圾收集会话时,它会使用一个整数值来导致PDOException

  

SQLSTATE [22008]:日期时间字段溢出:7错误:日期/时间字段值超出范围:“1506794381”
  提示:也许你需要一个不同的“日期风格”设置。 (SQL:从“sessions”中删除“last_activity”< = 1506794381)

我有什么方法可以指定格式或拦截流程以保留timestamptz列类型吗?

1 个答案:

答案 0 :(得分:2)

我建议通过覆盖DatabaseSessionHandler.php驱动程序的gcexpired方法来创建自定义会话驱动程序,以使用timestamp with timezone而不是整数。

gc目前已通过time() - $lifetime,但您可以将其更改为date('c', time() - $lifetime)。在expired中,您可以致电strtotime上的$session->last_activity转换为与getTimestamp()相同的单位。

<?php

namespace App\Extensions;
use Illuminate\Session\DatabaseSessionHandler;

class MyPostgresHandler extends DatabaseSessionHandler
{   
    public function gc($lifetime) {
        $sessions = $this->getQuery()->where('last_activity', '<=', date('c', time() - $lifetime))->get();
        foreach ($sessions as $session) {
            $this->destroy($session->id);
        }
    }

    protected function expired($session)
    {
        return isset($session->last_activity) &&
            strtotime($session->last_activity) < Carbon::now()->subMinutes($this->minutes)->getTimestamp();
    }
}

然后您可以通过扩展ServiceProvider

来注册新的驱动程序
<?php

namespace App\Providers;

use App\Extensions\MyPostgresHandler;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;

class SessionServiceProvider extends ServiceProvider
{
    /**
     * Perform post-registration booting of services.
     *
     * @return void
     */
    public function boot()
    {
        Session::extend('my-db-driver', function ($app) {
            return new MyPostgresHandler;
        });
    }

    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

现在,您可以在my-db-driver

中使用config/session.php

有关详细信息,请参阅https://laravel.com/docs/5.5/session#implementing-the-driver

从OP更新

FuzzyTree's answer之上添加,需要添加的代码比文档所说的更多,所以我想发布其余的流程。因为我们正在扩展内置类,所以它与框架的其他部分有一些联系,但幸运的是有很多方法可以利用它们。最终的SessionServiceProvider如下所示:

namespace App\Providers;

use App\Extensions\MyPostgresHandler;
use Illuminate\Database\Connectors\PostgresConnector;
use Illuminate\Database\PostgresConnection;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Connection;

class SessionServiceProvider extends ServiceProvider {
    public function boot(){
        Session::extend('postgres', function ($app){
            return new MyPostgresHandler;
        });
        Connection::resolverFor('postgres', function ($connection, $database, $prefix, $config) {
            return new PostgresConnection($connection, $database, $prefix, $config);
        });
        $this->app->bind('db.connector.postgres', function(){
            return new PostgresConnector;
        });
    }
}

我已将我的驱动程序命名为postgres,并且驱动程序的类与原始答案中的相同。由于缺少参数,我的IDE在return new MyPostgresHandler;行显示错误,但似乎代码无论如何都有效。

这些解析器是Laravel其余部分接受我们的新驱动程序所必需的,因为内置类只将pgsql名称映射到相应的类,因为我们创建了一个具有不同名称的驱动程序否则会认为它无效。

最后,服务提供商必须在config/app.php注册:

'providers' => [
    // ...

    /*
     * Application Service Providers...
     */
    App\Providers\AppServiceProvider::class,
    App\Providers\AuthServiceProvider::class,
    App\Providers\EventServiceProvider::class,
    App\Providers\RouteServiceProvider::class,

    App\Providers\SessionServiceProvider::class, // <--
],