So verwenden Sie Modelle in Laravel wieder
伊谢尔伦
伊谢尔伦 2017-05-16 16:53:44
0
5
761

Unser Unternehmen verwendet derzeit Laravel开发项目,同时还增加了Biz层和Repositories层,来实现业务逻辑封装,反而model里面什么代码都没有。
Controller里写代码的时候,尝尝困扰我的问题是如果复用Biz对象,Repositories对象和Model对象。
以前用Yii开发项目的时候,有一个工厂模式,所以调用Model的时候,基本都不new, das als Bytes mit XXX::model() bezeichnet wird. Es reicht aus, wenn ein Objekt einmal neu ist, was effektiv Speicher sparen kann. Controller-Code:

$productModel = Product::model()->getList('xxxxxxxxx');

Wie einfach, oder?

In Laravel scheint Model keine Factory zu haben. Um es aufzurufen, ist eine Instanz erforderlich. Wenn Repositories 5 Methoden kapselt, ist jede davon vorhanden habe Model verwendet, dann habe ich diese 5 Methoden in Controller aufgerufen und Model war fünfmal neu.

Ich sehe derzeit im Internet eine Möglichkeit, das Modellobjekt in den Konstruktor von Repositories einzufügen und in die privaten Mitgliedsvariablen von Repositories einzufügen. Auf diese Weise können alle fünf Methoden aufrufen private Variablen der aktuellen Klasse. Die Verwendung ist jedoch umständlich. Wenn Sie Code in Controller schreiben, müssen Sie so schreiben: Laravel里,Model好像没有工厂,要调用,都需要实例,假如Repositories里面封装了5个方法,每个都使用了Model,那么我在Controller里调用了这5个方法,Model就被new了5次。
目前在网上看到一种办法,就是在Repositories

$xxxBiz = new XXXBiz(\xxx\xxx\Repositories);

Sie müssen dies in Repositories schreiben:

$xxxRepositories = new XXXRepositories(\xxx\xx\xxxModel);

Es gibt ein

-Objekt in new, und der Namespace ist im Grunde immer noch lang, und die Effizienz beim Schreiben von Code ist extrem gering. Außerdem muss ich die Datei öffnen, um den Namen zu buchstabieren. Biz的时候,还必须传入Repositories

Ich würde gerne fragen, wie Sie das Problem der Wiederverwendung logischer Schichtklassen wie Model bei der Entwicklung von Projekten mit Laravel lösen?

伊谢尔伦
伊谢尔伦

小伙看你根骨奇佳,潜力无限,来学PHP伐。

Antworte allen(5)
漂亮男人

0x0 前言

有趣的问题,Yii 在业界也是被公认为性能比 Laravel 高的一个框架。于是我想单从 ActiveRecord 的构造,看看两大框架的具体实现。

0x1 Laravel 的 Eloquent 框架

在 Laravel 中可以很轻松地使用关系查询:

    $user = User::find(1);

我在 User 类里并没有找到 find 方法,WTF,发生了什么!?

User 的基类是 Model,使用静态调用,所以会调用 Model 的 __callStatic 魔术方法:

    public static function __callStatic($method, $parameters)
    {
        // $method = 'find';
        // $parameters = [1];
        
        // 实例化当前类,即 User 模块
        $instance = new static;
        // 调用当前实例的 find 方法
        return call_user_func_array([$instance, $method], $parameters);
    }

其实就是再调用 __call 魔术方法:

    public function __call($method, $parameters)
    {
        //...

        $query = $this->newQuery();

        return call_user_func_array([$query, $method], $parameters);
    }

追根溯源,我们发现其实 find 方法来自 Illuminate\Database\Eloquent\Builder,而该类内部使用 Illuminate\Database\Query\Builder 的实现。

等等,Illuminate\Database\Eloquent\BuilderIlluminate\Database\Query\Builder 的区别是啥?

其实 Eloquent\Builder 是对 Query\Builder 的进一步封装,以便更好的实现关系对象查询。

那么,其实过程为:

也就是说,你每次 静态调用 Model 的方法,都会实例化一次 Model,走一次过程。

0x2 Yii 1.1 中的 CActiveRecord

题主既然用到 model 方法,那应该是 1.1 的版本,模块继承自 CActiveRecord(Yii2 中则继承自 Yii\db\ActiveRecord)。

好了,伙计们,现在使用 Yii 实现关系查询,先定义:

    class User extends CActiveRecord
    {
        public static function model($className=__CLASS__)
        {
            return parent::model($className);
        }
    }

查询:

    $user = User::model()->findAllByPk(1);

明显查询对象来自 model,看看父类怎么实现这个函数:

    public static function model($className=__CLASS__)
    {
        // 如果已经实例化了则直接返回该实例
        if(isset(self::$_models[$className]))
            return self::$_models[$className];
        else
        {
            // 初始化,并将保存当前实例
            $model=self::$_models[$className]=new $className(null);
            $model->attachBehaviors($model->behaviors());
            return $model;
        }
    }

findAllByPk 方法直接封装于 CActiveRecord 内部:

    public function findByPk($pk,$condition='',$params=array())
    {
        // ...

        $criteria = $this->getCommandBuilder()->createPkCriteria($this->getTableSchema(),
            $pk, $condition, $params, $prefix);
            
        return $this->query($criteria);
    }

所以其过程为:

0x3 使用 Laravel 的依赖注入

通常情况下(无参数的构造函数或注入参数已配置),Laravel 会自动帮你实例化:

<?php namespace App\Repositories;

use App\User;    
    
class Repository {
    
    protected $user;
    
    public __construct(User $user) {
        $this->user = $user;
    }
}

所以你可以很轻松复用同一个对象:

class Repository {
    
    protected $user;
    
    public __construct(User $user) {
        $this->user = $user;
    }
    
    public function first() {
        $this->user->first();
    }
    
    public function find(Request $request, $id) {
        if (Gate::denies('view', $request->user())) {
            abort(403);
        }
        
        return $this->user->find($id);
    }
    
    public function excited() {
        return $this->user->where('name', 'bigNews')->get();
    }
}

实现完仓库后,你需要手动实例化吗:

    $repo = new App\Repositories\Repository(new App\User());

不,这不符合 Laravel 的哲♂学,你可以如此简单:

<?php
    use App\Repositories\Repository;
    
    public function index(Repository $repo) {
        return $repo->first();
    }

是的,没错,你无需手动构造,传入 User 实例等等,一切只是一个简单的自动注入。而且题主注意到这里使用了命名空间,所以你只需 use 一次。(当然如果你不想拼写这么长的命名空间,那你是时候换一款 IDE 了,PhpStorm 中可以使用 Alt + Enter 快速导入

0x4 最后

对于静态和非静态开销的问题,在 StackOverflow 上有一篇讨论:http://stackoverflow.com/questions/14727...

所以说到底还是看业务需求嘛23333

阿神

通过依赖注入呀
直接可以注入到Controller中
可以看看这个文章
http://slides.com/howtomakeaturn/model#/

小葫芦

我假定你对Laravel还不是很了解。

第一,Laravel的Model也就是模型,不需要显式的实例化,调用方式像下面这样(摘自官方文档):

$flights = App\Flight::where('active', 1)
               ->orderBy('name', 'desc')
               ->take(10)
               ->get();

第二,你的描述有错误。你寻找的不是工厂模式(factory pattern),而是单例模式(singleton pattern),对象在一次请求生命周期之内,最多只需要实例化一次。在Laravel当中,需要用到IOC(inversion of control)容器或者说是服务容器(service container)。像下面这样:

// 先绑定需要实例化的对象或者服务
$this->app->singleton('FooBar', function ($app) {
    return new FooBar($app['SomethingElse']);
});
// 调用对象或服务有多种方式,比如以下两种:
// 第一种
$fooBar = $this->app->make('FooBar'); // 显式解析
$fooBar = $this->app['FooBar']; // 像访问数组一样调用之前被显式解析(实例化)过的对象或服务
// 第二种
// 通过类型声明(type hinting)来自动解析,无需显式解析(实例化),直接调用,请参考最后附上的文档
// 除了单例模式外,当然还支持工厂模式,即每次调用,返回一个新的实例,像下面这样:
$this->app->bind('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app['HttpClient']);
});

以上只是简单的摘录,具体用法,请参考Laravel官方优秀的文档,链接如下:
Service Container(IOC容器/服务容器)

洪涛

我司是继承一个BaseRepository,BaseRepository中定义

protected function getUserCouponModel($new = false)
{
    if (is_null($this->userCouponModel) || true === $new) {
        $this->userCouponModel = new UserCouponModel();
    }
    return $this->userCouponModel;
}

CouponRepository中

public function create($couponID)
{
    $attributes = [
        'couponID' => $couponID,
    ];

    return $this->getUserCouponModel(true)->create($attributes);
}

Biz中类似,继承一个BaseBiz,然后方法这么写

public function create($fields)
{
    return $this->getCouponRepository()->create($fields);
}

Controller中调用

$biz = new Biz();
$biz->create($fields);

Controller ---> Biz ---> Repository

淡淡烟草味

我是这么做的,在底层 model 里建立这个函数
修改bootstrap/app.php 和 AppServiceProvider.php
具体参考 Service Provider

    static public function load(){
        return app()->make(get_called_class());
    }

在controller 里调用 Foo::load() 就可以了

Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage