To avoid code duplication in Laravel, I want to have a method that is used by multiple Controllers, it inserts some rows in database and also updates some data in another table.

I thought of using Repository, but I read somewhere that Repository is better used for retrieving data and shouldn't be used for inserting.

所以我现在要用trait .但我有点困惑...

Could some one please explain in a simple way what is the best usage for each of these (Repository/Service/Trait) and how are they different?

推荐答案

Traits

是另一种继承方法,它解决了PHP使用的单个类继承的一些限制.这通常用于在模型之间共享类似的逻辑.让我们假设有几个模特与公司有关系.

trait HasCompany {
   public function company() {
       return $this->belongsTo(Company::class);
   }
}

Now the user can easily share code from the trait, by the keyword using. This is an example and most often a more complex use case would be needed for it to make sense.

class User {
   use HasCompany;
}

Repositories

Repositories is a design pattern to abstract data layers from the application. Your logic should not care about how you store data, so if you wanted to change from Mysql to Mongodb, you would only swap out the repository and not have to change business logic.

这里非常固执己见,但这不是适合Laravel人的设计模式.Laravel具有Eloquent 的功能,因此数据库层已经被抽象.存储库有时用于Laravel个应用程序,但不是一个常见的场景,而是一个异常值.存储库的主要原因之一是数据实现不可知,它已经存在,并且可以在SQL服务器之间完美地交换.还有Eloquents个特性,比如::find()scopes等,感觉像是存储库的替代品,同时使用起来很奇怪.

If you use Doctrine as the ORM, which you can in Laravel, it is the core of their architecture and should be used.

Services

Is commonly used for a place to store business logic or the building blocks of your actions in your application. In traditional MVC design, Controllers should only handle input. Normally you would put your logic in Models, but they get "fat" very quickly, when this happens services is a common place to put business logic. Sometimes also named actions or commands, which is similar but a little bit different approaches.

One of the core things it solves, is to make your business logic reusable. Imaging filtering all users by an active flag, when you retrieve it in its controller.

public function all() {
    return User::where('active', true)->get();
}

现在您有了业务逻辑,它强制您只处理活动用户,之后您希望通过使用命令通知所有活动用户,并通过邮箱通知所有活动用户.

class NotifyUsers extends Command {
    public function handle() {
        foreach (User::where('active', true)->get() as $user) {
            $user->notify();
        }
    }
}

现在,您必须手动更新业务逻辑.下次添加第二个条件或更改逻辑时,必须在两个地方更改代码.在经常使用此代码块的大型应用程序中,如果不忘记其中一个位置,则很难维护这些条件.如果使用此逻辑创建服务,则可以轻松地在整个应用程序中使用相同的业务逻辑.虽然只有一个地方可以修改代码,但如果这种逻辑必须改变的话.

class UserService {
    public function all() {
        return User::where('active', true)->get();
    }
}

在您希望使用此业务逻辑获取活动用户的任何地方,您都可以使用该服务.因此,只有一个地方来维持逻辑.一个呼叫可以简单到resolve(UserService::class)->all().服务的更新逻辑示例如下.

// controller
public function all(UserService $userService) {
    return $userService->all();
}

// command
class NotifyUsers extends Command {
    public function handle(UserService $userService) {
        $userService->all()->each->notify();
    }
}

Conclusion

世界不是黑白的,你必须想出自己的方法.我的建议是,不要把时间花在存储库上,Laravel有很多特性来处理与数据相关的操作scopesgetterssetters等,这些都与存储库设计模式相冲突.看看类似服务的设计方法是否适合您,您是否可以利用em.Traits与其说是一种体系 struct 设计模式,不如说是一种类继承替代方案,只是为了在类之间共享逻辑.

Laravel相关问答推荐

我如何通过 laravel 在高图中针对不同的时间范围进行烛台数据分组

Laravel Eloquent:组合列(或自定义属性)上的 Where 子句

调用字符串上的成员函数

Eloquent/Laravel:是否有 object->load('relation') 的对应物,例如.object->unload('relation')?

如何将用户对象绑定到中间件中的请求

指令allow_call_time_pass_reference警告

调用未定义的函数 App\Http\Controllers\ [函数名]

有条件的Eager加载

如何创建具有关系的 Eloquent 模型?

Laravel 判断约束违规

中间件中的 Laravel 依赖注入

laravel 5.4 指定的key太长,为什么数字191

Laravel Blade - 如果满足条件,则添加一个类

扩展 Eloquent 的类的构造函数

如何从 Laravel 中的资源中获取图像?

Laravel hasManyThrough

控制器中的artisan调用输出?

使用 Queue::fake() 测试监听器

Eloquent - 带有字符串值而不是列标题的连接子句

开发中的 Laravel 和视图缓存 - 无法立即看到更改