每当我向Eloquent模型添加额外的逻辑时,为了从模型的外观调用它,我最终不得不使它成为一个static
方法(即不太理想)。我试着搜索了很多关于如何以正确的方式做到这一点,几乎所有的结果都谈论创建返回Query Builder接口部分的方法。我正试图找出如何添加可以返回任何东西的方法,并使用模型的外观调用。
例如,假设我有一个名为Car
的模型,并希望获得它们:
$cars = Car::all();
很好,除了现在,假设我想通过make将结果排序到多维数组中所以我的结果可能是这样的:
$cars = array(
'Ford' => array(
'F-150' => '...',
'Escape' => '...',
),
'Honda' => array(
'Accord' => '...',
'Civic' => '...',
),
);
在这个理论上的例子中,我想创建一个方法,可以这样调用:
$cars = Car::getAllSortedByMake();
让我们暂时忘记这个可怕的方法名以及它与数据结构紧密耦合的事实。如果我在模型中创建这样一个方法:
public function getAllSortedByMake()
{
// Process and return resulting array
return array('...');
}
最后在我的控制器中调用它,我会得到这个异常:
非静态方法Car::getAllSortedByMake()不应该静态调用,假设$this来自不兼容的上下文
TL;DR:我如何添加在模型中有意义的自定义功能,而不使其成为静态方法并使用模型的facade调用它?
编辑:
这是一个理论上的例子。也许换个说法会更有意义。为什么某些非静态方法(如all()
或which()
)可以在Eloquent模型的facade上使用,而不能在模型中添加其他方法?这意味着正在使用__call
魔术方法,但是我如何使它在模型中识别我自己的函数?
关于"排序"的一个更好的例子是,如果我需要在一段数据上运行一个计算或算法:
$validSPG = Chemical::isValidSpecificGravity(-1.43);
对我来说,像这样的东西在模型中是有意义的,因为它是特定于域的。
我的问题更多的是在一个基本的层面上,比如为什么都是()可通过立面进入?
如果你看一下Laravel Core - all()实际上是一个静态函数
public static function all($columns = array('*'))
你有两个选择:
public static function getAllSortedByMake()
{
return Car::where('....')->get();
}
或
public function scopeGetAllSortedByMake($query)
{
return $query->where('...')->get();
}
都允许您执行
Car::getAllSortedByMake();
实际上您可以扩展Eloquent Builder并在那里放置自定义方法。
扩展生成器的步骤:
1。创建自定义构建器
<?php
namespace App;
class CustomBuilder extends 'Illuminate'Database'Eloquent'Builder
{
public function test()
{
$this->where(['id' => 1]);
return $this;
}
}
2。将这个方法添加到您的基本模型中:
public function newEloquentBuilder($query)
{
return new CustomBuilder($query);
}
3。使用自定义构建器中的方法运行查询:
User::where('first_name', 'like', 'a')
->test()
->get();
以上代码生成的mysql查询将是:
select * from `users` where `first_name` like ? and (`id` = ?) and `users`.`deleted_at` is null
PS: 第一个Laurence示例代码更适合您的存储库而不是模型,但您也不能用这种方法管道更多的方法:
public static function getAllSortedByMake()
{
return Car::where('....')->get();
}
第二个劳伦斯例子是事件最坏的。
public function scopeGetAllSortedByMake($query)
{
return $query->where('...')->get();
}
许多人建议使用作用域来扩展laravel构建器,但这实际上是一个糟糕的解决方案,因为作用域被雄辩的构建器隔离,你不会在作用域内和作用域外使用相同的命令获得相同的查询。我建议PR改变作用域是否应该被隔离,但是Taylor没有理睬我。
更多说明:例如,如果你有这样的作用域:
public function scopeWhereTest($builder, $column, $operator = null, $value = null, $boolean = 'and')
{
$builder->where($column, $operator, $value, $boolean);
}
和两个有说服力的查询:
User::where(function($query){
$query->where('first_name', 'like', 'a');
$query->where('first_name', 'like', 'b');
})->get();
和
User::where(function($query){
$query->where('first_name', 'like', 'a');
$query->whereTest('first_name', 'like', 'b');
})->get();
生成的查询将是:
select * from `users` where (`first_name` like ? and `first_name` like ?) and `users`.`deleted_at` is null
和
select * from `users` where (`first_name` like ? and (`id` = ?)) and `users`.`deleted_at` is null
第一眼看起来是一样的,但实际上不是。对于这个简单的查询,也许它并不重要,但对于复杂的查询,它确实,所以请不要使用扩展生成器的作用域:)
对于更好的动态代码,而不是使用Model类名"Car",
请使用"static"或"self"
public static function getAllSortedByMake()
{
//to return "Illuminate'Database'Query'Builder" class object you can add another where as you want
return static::where('...');
//or return already as collection object
return static::where('...')->get();
}
Laravel模型自定义方法->最好的方法是使用性状
- 步骤#1:创建trait
- 步骤#2:添加特征到模型 步骤3:使用方法
User::first()->confirmEmailNow()
app/模型/User.php
use App'Traits'EmailConfirmation;
class User extends Authenticatable
{
use EmailConfirmation;
//...
}
app/品质/EmailConfirmation.php
<?php
namespace App'Traits;
trait EmailConfirmation
{
/**
* Set email_verified_at to now and save.
*
*/
public function confirmEmailNow()
{
$this->email_verified_at = now();
$this->save();
return $this;
}
}