我正在开发一个项目,它使用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()函数
我可以用什么方法来实现这个目标?
答案 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子句感谢大家!