如何在Laravel Eloquent ORM中使用数据库特定功能来实现模块化使用

时间:2015-01-15 09:57:09

标签: mysql postgresql laravel orm eloquent

我正在开发一个项目,它使用ORM尽可能地在每个数据库系统上运行项目。 Project现在使用postgresql。我想知道如何在不损失ORM模块性的情况下使用数据库特定功能。

例如: 我必须对一个查询使用“extract”函数,如此;

DELETE FROM tokens AS t WHERE (extract(epoch from t.created_at) + t.expires) < extract(epoch from NOW())

如果我想使用模型类来实现这一点。不久或者我需要以原始格式编写提取函数where子句

Tokens::whereRaw('(extract(epoch from t.created_at) + t.expires) < extract(epoch from NOW())')->get();

如果我使用查询构建器

DB::table('tokens')->whereRaw('(extract(epoch from t.created_at) + t.expires) < extract(epoch from NOW())')->select()->get();

同样的事情发生

我需要的东西就像我使用postgresql时ORM需要使用EXTRACT()函数或当我使用mysql时ORM需要使用UNIX_TIMESTAMP()函数

我可以用什么方法来实现这个目标?

3 个答案:

答案 0 :(得分:1)

从模型中取出这个逻辑。

为Postgres创建一个存储库,让它称之为PostgresTokenRepository。该存储库的构造函数应该看起来像......

<?php

class PostgresTokenRepository implements TokenRepositoryInterface
{
    protected $token;

    public function __construct(Token $token)
    {
        $this->token = $token;
    }

    public function getTokens()
    {
        return $this->token->whereRaw('(extract(epoch from t.created_at) + t.expires) < extract(epoch from NOW())')->get();
    }
}

您需要一个界面...... TokenRepositoryInterface

interface TokenRepositoryInterface
{
    public function getTokens();
}

现在你应该尽可能地设置存储库。如果您需要执行MySQL实现,只需创建一个看起来类似的MysqlTokenRepository,但getTokens()函数将使用UNIX_TIMESTAMP()

现在你需要告诉Laravel,当你在寻找TokenRepositoryInterface的实现时,它应该return PostgresTokenRepository。为此,我们需要创建一个服务提供商。

<?php

class UserServiceProvider extends \Illuminate\Support\ServiceProvider
{
    public function register()
    {
        $this->app->bind('TokenRepositoryInterface', 'PostgresTokenRepository');
    }
}

现在唯一要做的就是将此服务提供商添加到config/app.php中的服务提供商数组中。

现在,只要您在控制器中需要此存储库,就可以自动注入它们。这是一个例子......

class TokenController extends BaseController
{

    protected $token;

    public function __construct(TokenRepositoryInterface $token)
    {
        $this->token = $token;
    }

    public function index()
    {
        $tokens = $this->token->getTokens();

        return View::make('token.index')->with('tokens', $tokens);
    }
}

这样做的目的是当你想开始使用MySQL实现时,你所要做的就是修改服务提供者以返回MysqlTokenRepository而不是PostgresTokenRepository。或者,如果您想要一起编写新的实现,那么无需更改生产代码就可以实现。如果某些内容无效,只需将该行更改回PostgresTokenRepository

卖给我的另一个好处是,它使您能够保持模型和控制器非常轻便且非常可测试。

答案 1 :(得分:1)

这可以在相应的驱动程序中进行,但Taylor Otwell关于驱动程序特定函数的观点是,您应该像使用raw语句一样使用// BaseModel / trait / builder macro or whatever you like public function scopeWhereUnix($query, $col, $operator = null, $value = null) { if (func_num_args() == 3) { list($value, $operator) = array($operator, '='); } switch (class_basename($query->getQuery()->getConnection())) { case 'MySqlConnection': $col = DB::raw("unix_timestamp({$col})"); break; case 'PostgresConnection': $col = DB::raw("extract(epoch from {$col})"); break; } $query->where($col, $operator, $value); } 语句。

然而,在Eloquent中你可以很容易地自己做:

Tokens::whereUnix('created_at', 'value')->toSql();
// select * from tokens where unix_timestamp(created_at) = 'value'
// or
// select * from tokens where extract(epoch from created_at) = 'value'

现在你可以这样做:

Tokens::whereUnix(DB::raw('created_at) + (expires', '<', time())->toSql();
// select * from tokens where unix_timestamp(created_at) + (expires) < 12345678
// or
// select * from tokens where extract(epoch from created_at) + (expires) < 12345678

你有一些更复杂的条件,但你仍然可以通过一点点黑客来实现这一点:

Query\Builder

不幸的是DB::table(..)({{1}})并不容易扩展 - 事实上它根本不可扩展,所以你需要将它与你自己的Builder类交换,这是相当麻烦的。

答案 2 :(得分:0)

我最终创建了一个全球范围。创建了像 ExpiresWithTimestampsTrait 这样的特征,其中包含whereExpires范围的逻辑。范围确实添加了特定于数据库驱动程序的where子句。

public function scopeWhereExpired($query)
{
    // Eloquent class is my helper for getting connection type based on the @jarek's answer
    $type = Eloquent::getConnectionType($this);
    switch ($type) {
        case Eloquent::CONNECTION_POSTGRESS:
            return $query->whereRaw("(round(extract(epoch from (created_at)) + expires)) < round(extract(epoch from LOCALTIMESTAMP))");
            break;
        default:
            break;
    }
}

所以我只需要在模型上使用该特性。我需要添加一个&#34; case&#34;当我开始使用mysql

时,whereExpires范围用于支持mysql和where子句

感谢大家!