Laravel自定义模型方法

时间:2014-05-14 15:05:07

标签: php laravel laravel-4 eloquent facade

每当我向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 :我如何添加有意义的自定义功能,而不是将其作为静态方法并使用模型的外观调用它?


编辑:

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

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

$validSPG = Chemical::isValidSpecificGravity(-1.43);

对我来说,这样的事情在模型中是有意义的,因为它是特定于域的。

3 个答案:

答案 0 :(得分:51)

  

我的问题更多的是一个基本层面,例如为什么全部()   可通过立面进入吗?

如果你看一下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();

答案 1 :(得分:1)

用于更好的动态代码,而不是使用Model类名" Car",

只需使用"静态"或者"自我"

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();
}

答案 2 :(得分:1)

实际上,您可以扩展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();

vs

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

vs

select * from `users` where (`first_name` like ? and (`id` = ?)) and `users`.`deleted_at` is null

乍一看,查询看起来一样,但是没有。对于这个简单的查询来说,可能并不重要,但是对于复杂的查询来说,则无关紧要,所以请不要使用范围来扩展builder:)