Laravel自定义模型方法

Jer*_*ris 35 php facade laravel eloquent laravel-4

每当我向Eloquent模型添加额外的逻辑时,我最终都必须使它成为一种static方法(即不太理想)才能从模型的外观中调用它.我已经尝试了很多关于如何以正确方式执行此操作的内容,并且几乎所有结果都讨论了创建返回Query Builder接口部分的方法.我试图弄清楚如何添加可以返回任何东西的方法,并使用模型的外观进行调用.

例如,假设我有一个名为的模型,Car并希望得到它们:

$cars = Car::all();
Run Code Online (Sandbox Code Playgroud)

好的,除了现在,假设我想通过make将结果排序为多维数组,所以我的结果可能如下所示:

$cars = array(
  'Ford' => array(
     'F-150' => '...',
     'Escape' => '...',
  ),
  'Honda' => array(
     'Accord' => '...',
     'Civic' => '...',
  ),
);
Run Code Online (Sandbox Code Playgroud)

以这个理论为例,我很想创建一个可以被称为的方法:

$cars = Car::getAllSortedByMake();
Run Code Online (Sandbox Code Playgroud)

让我们忘记一下可怕的方法名称以及它与数据结构紧密耦合的事实.如果我在模型中创建这样的方法:

public function getAllSortedByMake()
{
   // Process and return resulting array
   return array('...');
}
Run Code Online (Sandbox Code Playgroud)

最后在我的控制器中调用它,我将抛出此异常:

非静态方法Car :: getAllSortedByMake()不应该静态调用,假设$ this来自不兼容的上下文

TL; DR:我如何添加有意义的自定义功能,使其成为模型而不使其成为静态方法并使用模型的外观调用它?


编辑:

这是一个理论上的例子.也许这个问题的改写更有意义.为什么某些非静态方法在Eloquent模型的外观上是可用的,all()或者which()在模型中添加了其他方法?这意味着__call正在使用魔术方法,但是如何让它在模型中识别我自己的函数?

可能比"排序"更好的例子是,如果我需要对一段数据运行计算或算法:

$validSPG = Chemical::isValidSpecificGravity(-1.43);
Run Code Online (Sandbox Code Playgroud)

对我而言,这样的事情在模型中是有道理的,因为它是特定于域的.

Lau*_*nce 56

我的问题更多的是一个基本层面,例如为什么所有()都可以通过外观进入?

如果你看一下Laravel Core - all()实际上是一个静态函数

public static function all($columns = array('*'))
Run Code Online (Sandbox Code Playgroud)

您有两种选择:

public static function getAllSortedByMake()
{
    return Car::where('....')->get();
}
Run Code Online (Sandbox Code Playgroud)

要么

public function scopeGetAllSortedByMake($query)
{
    return $query->where('...')->get();
}
Run Code Online (Sandbox Code Playgroud)

两者都可以让你做到

Car::getAllSortedByMake();
Run Code Online (Sandbox Code Playgroud)

  • 请注意,["作用域应始终返回查询构建器实例"](https://laravel.com/docs/5.1/eloquent#query-scopes).因此,如果使用` - > get()`,则应使用静态方法而不是范围变量.另请注意,范围可以链接:`Car :: GetAllSortedByMake() - > GetAllSortedByMake() - > otherScopeYouHaveDefined() - > where(...) - > get()`. (5认同)

fic*_*489 7

实际上,您可以扩展 Eloquent Builder 并将自定义方法放在那里。

扩展构建器的步骤:

1.创建自定义构建器

<?php

namespace App;

class CustomBuilder extends \Illuminate\Database\Eloquent\Builder
{
    public function test()
    {
        $this->where(['id' => 1]);

        return $this;
    }
}
Run Code Online (Sandbox Code Playgroud)

2.将此方法添加到您的基本模型:

public function newEloquentBuilder($query)
{
    return new CustomBuilder($query);
}
Run Code Online (Sandbox Code Playgroud)

3.使用自定义构建器中的方法运行查询:

User::where('first_name', 'like', 'a')
    ->test()
    ->get();
Run Code Online (Sandbox Code Playgroud)

对于上面的代码生成的 mysql 查询将是:

select * from `users` where `first_name` like ? and (`id` = ?) and `users`.`deleted_at` is null
Run Code Online (Sandbox Code Playgroud)

PS:

第一个 Laurence示例是更适合您的存储库而不是模型的代码,但您也无法使用这种方法管理更多方法:

public static function getAllSortedByMake()
{
    return Car::where('....')->get();
}
Run Code Online (Sandbox Code Playgroud)

劳伦斯的第二个例子是事件最坏的情况。

public function scopeGetAllSortedByMake($query)
{
    return $query->where('...')->get();
}
Run Code Online (Sandbox Code Playgroud)

许多人建议使用范围来扩展 laravel 构建器,但这实际上是一个糟糕的解决方案,因为范围是由 eloquent builder 隔离的,并且您不会在范围内和范围外使用相同的命令获得相同的查询。我提出 PR 来改变范围是否应该被隔离,但 Taylor 没有理会我。

更多解释:例如,如果您有这样的范围:

public function scopeWhereTest($builder, $column, $operator = null, $value = null, $boolean = 'and')
{
    $builder->where($column, $operator, $value, $boolean);
}
Run Code Online (Sandbox Code Playgroud)

和两个雄辩的查询:

User::where(function($query){
    $query->where('first_name', 'like', 'a');
    $query->where('first_name', 'like', 'b');
})->get();
Run Code Online (Sandbox Code Playgroud)

对比

User::where(function($query){
    $query->where('first_name', 'like', 'a');
    $query->whereTest('first_name', 'like', 'b');
})->get();
Run Code Online (Sandbox Code Playgroud)

生成的查询将是:

select * from `users` where (`first_name` like ? and `first_name` like ?) and `users`.`deleted_at` is null
Run Code Online (Sandbox Code Playgroud)

对比

select * from `users` where (`first_name` like ? and (`id` = ?)) and `users`.`deleted_at` is null
Run Code Online (Sandbox Code Playgroud)

乍一看,查询看起来相同,但没有。对于这个简单的查询,它可能并不重要,但对于复杂的查询,它确实重要,所以请不要使用范围来扩展构建器 :)