How to reuse models in Laravel
伊谢尔伦
伊谢尔伦 2017-05-16 16:53:44
0
5
760

Our company is now using Laravel to develop projects, and also added the Biz layer and the Repositories layer to implement business logic encapsulation, but There is no code in model.
When writing code in Controller, the problem that bothers me is how to reuse Biz objects, Repositories objects and ModelObject.
When I used Yii to develop projects before, there was a factory mode, so when calling Model, basically no new was used, all bytes were used XXX::model() To call, it is enough for an object to be new once, which can effectively save memory.
Controller code:

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

How simple, is there any?

In

Laravel, Model seems to have no factory. To call it, an instance is required. If Repositories encapsulates 5 methods, each one is used. Model, then I called these 5 methods in Controller, and Model was new 5 times.
Currently I see a way on the Internet, which is to inject the Model object into the constructor of Repositories and put it in the private member variable of Repositories, so that all five methods call the private variable of the current class. That's it. But it is troublesome to use. When writing code in Controller, you need to write like this:

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

You need to write this in Repositories:

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

When creating a new Biz, the Repositories object must also be passed in, and the namespace is still long. I basically spell strings, and the writing code is very efficient. Low, I still need to open this file to spell out the name.

I would like to ask how you solve the problem of reuse of logical layer classes such as Model when developing projects with Laravel?

伊谢尔伦
伊谢尔伦

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

reply all(5)
漂亮男人

0x0 Preface

Interesting question, Yii is also recognized in the industry as a framework with higher performance than Laravel. So I wanted to look at the specific implementation of the two major frameworks just from the structure of ActiveRecord.

0x1 Eloquent framework for Laravel

It’s easy to use relational queries in Laravel:

    $user = User::find(1);

I didn’t find the find method in the User class, WTF, what happened! ?

The base class of User is Model, which uses static calling, so the __callStatic magic method of Model will be called:

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

In fact, it is to call the __call magic method again:

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

        $query = $this->newQuery();

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

Tracing back to the source, we found that the find method actually comes from IlluminateDatabaseEloquentBuilder, and this class internally uses the implementation of IlluminateDatabaseQueryBuilder.

Wait a minute, what’s the difference between IlluminateDatabaseEloquentBuilder and IlluminateDatabaseQueryBuilder?

In fact, EloquentBuilder is a further encapsulation of QueryBuilder to better implement relational object query.

So, the actual process is:

In other words, every time you statically call the Model method, the Model will be instantiated and the process will be completed.

0x2 CActiveRecord in Yii 1.1

Since the questioner uses the model method, it should be version 1.1. The module inherits from CActiveRecord (in Yii2, it inherits from YiidbActiveRecord).

Okay, guys, now use Yii to implement relational query, first define:

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

Enquiry:

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

Obviously the query object comes from model, let’s see how the parent class implements this function:

    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 method is directly encapsulated inside CActiveRecord:

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

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

So the process is:

0x3 Dependency injection using Laravel

Under normal circumstances (parameterless constructor or injected parameters have been configured), Laravel will automatically instantiate it for you:

<?php namespace App\Repositories;

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

So you can reuse the same object easily:

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

After implementing the warehouse, do you need to instantiate it manually:

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

No, this is not in line with Laravel's philosophy, you can do it as simple as:

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

Yes, that’s right, you don’t need to manually construct, pass in the User instance, etc., everything is just a simple automatic injection. And the questioner noticed that a namespace is used here, so you only need to use it once. (Of course, if you don’t want to spell such a long namespace, then it’s time for you to change to an IDE. You can use Alt + Enter to quickly import in PhpStorm

0x4 Finally

For the issue of static and non-static overhead, there is a discussion on StackOverflow: http://stackoverflow.com/questions/14727...

So in the final analysis, it still depends on business needs 23333

阿神

Through dependency injection
It can be injected directly into the Controller
You can read this article
http://slides.com/howtomakeaturn/model#/

小葫芦

I assume you don’t know much about Laravel yet.

First, Laravel’s Model is also a model, which does not require explicit instantiation. The calling method is as follows (extracted from official documentation):

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

Second, your description is wrong. What you are looking for is not the factory pattern, but the singleton pattern. The object only needs to be instantiated once during the life cycle of a request. In Laravel, you need to use an IOC (inversion of control) container or service container. Like this:

// 先绑定需要实例化的对象或者服务
$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']);
});

The above is just a simple excerpt. For specific usage, please refer to Laravel’s official excellent documentation. The link is as follows:
Service Container (IOC container/service container)

洪涛

Our company inherits a BaseRepository, which is defined in 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);
}

Similar in Biz, inherit a BaseBiz, and then write the method like this

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

Controller

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

Controller ---> Biz ---> Repository

淡淡烟草味

This is what I did, create this function in the underlying model
Modify bootstrap/app.php and AppServiceProvider.php
For details, please refer to Service Provider

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

Just call Foo::load() in the controller

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template