php Laravel 自定义模型方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23658479/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 16:51:45  来源:igfitidea点击:

Laravel Custom Model Methods

phplaravellaravel-4eloquentfacade

提问by Jeremy Harris

Whenever I add additional logic to Eloquent models, I end up having to make it a staticmethod (i.e. less than ideal) in order to call it from the model's facade. I've tried searching a lot on how to do this the proper way and pretty much all results talk about creating methods that return portions of a Query Builder interface. I'm trying to figure out how to add methods that can return anything and be called using the model's facade.

每当我向 Eloquent 模型添加额外的逻辑时,我最终不得不使其成为一种static方法(即不太理想),以便从模型的外观中调用它。我已经尝试了很多关于如何以正确的方式执行此操作的搜索,并且几乎所有结果都在讨论创建返回查询生成器界面部分的方法。我试图弄清楚如何添加可以返回任何内容并使用模型的外观调用的方法。

For example, lets say I have a model called Carand want to get them all:

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

$cars = Car::all();

Great, except for now, let's say I want to sort the result into a multidimensional array by make so my result may look like this:

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

$cars = array(
  'Ford' => array(
     'F-150' => '...',
     'Escape' => '...',
  ),
  'Honda' => array(
     'Accord' => '...',
     'Civic' => '...',
  ),
);

Taking that theoretical example, I am tempted to create a method that can be called like:

以那个理论示例为例,我很想创建一个可以这样调用的方法:

$cars = Car::getAllSortedByMake();

For a moment, lets forget the terrible method name and the fact that it is tightly coupled to the data structure. If I make a method like this in the model:

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

public function getAllSortedByMake()
{
   // Process and return resulting array
   return array('...');
}

And finally call it in my controller, I will get this Exception thrown:

最后在我的控制器中调用它,我会抛出这个异常:

Non-static method Car::getAllSortedByMake() should not be called statically, assuming $this from incompatible context

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

TL;DR: How can I add custom functionality that makes sense to be in the model without making it a static method and call it using the model's facade?

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



Edit:

编辑:

This is a theoretical example. Perhaps a rephrase of the question would make more sense. Why are certain non-static methods such as all()or which()available on the facade of an Eloquent model, but not additional methods added into the model? This means that the __callmagic method is being used, but how can I make it recognize my own functions in the model?

这是一个理论上的例子。或许重新表述这个问题会更有意义。为什么某些非静态方法(例如Eloquent 模型的外观上的all()which()可用),而不是将其他方法添加到模型中?这意味着__call正在使用魔术方法,但是我怎样才能让它在模型中识别我自己的函数呢?

Probably a better example over the "sorting" is if I needed to run an calculation or algorithm on a piece of data:

关于“排序”的一个更好的例子可能是我是否需要对一段数据运行计算或算法:

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

To me, it makes sense for something like that to be in the model as it is domain specific.

对我来说,在模型中加入类似的东西是有意义的,因为它是特定于领域的。

回答by Laurence

My question is at more of a fundamental level such as why is all() accessible via the facade?

我的问题更像是一个基本层面,例如为什么 all() 可以通过门面访问?

If you look at the Laravel Core - all() is actually a static function

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

public static function all($columns = array('*'))

You have two options:

您有两个选择:

public static function getAllSortedByMake()
{
    return Car::where('....')->get();
}

or

或者

public function scopeGetAllSortedByMake($query)
{
    return $query->where('...')->get();
}

Both will allow you to do

两者都可以让你做

Car::getAllSortedByMake();

回答by fico7489

Actually you can extend Eloquent Builder and put custom methods there.

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

Steps to extend builder :

扩展构建器的步骤:

1.Create custom builder

1.创建自定义构建器

<?php

namespace App;

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

        return $this;
    }
}

2.Add this method to your base model :

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

public function newEloquentBuilder($query)
{
    return new CustomBuilder($query);
}

3.Run query with methods inside your custom builder :

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

User::where('first_name', 'like', 'a')
    ->test()
    ->get();

for above code generated mysql query will be :

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

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

PS:

PS:

First Laurenceexample is code more suitable for you repository not for model, but also you can't pipe more methods with this approach :

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

public static function getAllSortedByMake()
{
    return Car::where('....')->get();
}

Second Laurenceexample is event worst.

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

public function scopeGetAllSortedByMake($query)
{
    return $query->where('...')->get();
}

Many people suggest using scopes for extend laravel builder but that is actually bad solution because scopes are isolated by eloquent builder and you won't get the same query with same commands inside vs outside scope. I proposed PR for change whether scopes should be isolated but Taylor ignored me.

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

More explanation : For example if you have scopes like this one :

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

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

and two eloquent queries :

和两个雄辩的查询:

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

Generated queries would be :

生成的查询将是:

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

on first sight queries look the same but there are not. For this simple query maybe it does not matter but for complicated queries it does, so please don't use scopes for extending builder :)

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

回答by jalmatari

for better dynamic code, rather than using Model class name "Car",

为了更好的动态代码,而不是使用模型类名“Car”,

just use "static" or "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();
}