帶你解密laravel數據庫動靜態混合調用的祕密

laravel orm

laravel 使用 orm 調用數據庫查詢的時候很方便,我們只需要配置完數據庫連接後創建model。

比如我們查詢用戶,我們首先要有一個user model

可以使用 artisan創建

php artisan make:model userModel

如果我們需要查詢一個用戶信息,只需要這樣

userModel::where('id', 1)->first();
(new userModel)->where('id', 1)->first();

上面的兩種方法都可以實現查詢用戶id爲1的用戶信息。那他們有什麼區別呢?第一種方法更爲優雅。

那這個是怎麼實現的呢,一般我們要麼定義一個靜態方法用來靜態調用,要麼定義一個對象方法需要使用對象調用。

其實很簡單,他只是用到了php魔術方法

來看一下下面兩個魔術方法:

  • __call()
  • __callStatic()

看一下php文檔中的介紹

__call()

在對象中調用一個不可訪問方法時,__call() 會被調用。

public __call ( string $name , array $arguments ) : mixed

__callStatic()

在靜態上下文中調用一個不可訪問方法時,__callStatic() 會被調用。

public static __callStatic ( string $name , array $arguments ) : mixed

name調name 參數是要調用的方法名稱。arguments 參數是一個枚舉數組,包含着要傳遞給方法 $name 的參數。

我們要讓一個對象方法可以靜態調用就要通過__callStatic()魔術方法了。


public static function __callStatic( string $name , array $arguments) {
    return (new static)->$name(...$arguments);
}

這樣的話當我們調用userModel::where()的時候實際上它內部會創建一個對象然後再對象調用where

但是這樣會產生一個問題,如果當我們調用的方法不存在的時候怎麼辦呢。

可以通過try catch來捕獲錯誤進行錯誤處理。laravel內部就是這麼實現的。在laravel的model文件下,是這麼運用__call()的。


/**
     * Handle dynamic method calls into the model.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        if (in_array($method, ['increment', 'decrement'])) {
            return $this->$method(...$parameters);
        }

        return $this->forwardCallTo($this->newQuery(), $method, $parameters);
    }

他裏面調用了forwardCallTo方法,我們來看一下這個方法。

這個方法存在ForwardsCalls這個trait中。


/**
     * Forward a method call to the given object.
     *
     * @param  mixed  $object
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     *
     * @throws \BadMethodCallException
     */
    protected function forwardCallTo($object, $method, $parameters)
    {
        try {
            return $object->{$method}(...$parameters);
        } catch (Error | BadMethodCallException $e) {
            $pattern = '~^Call to undefined method (?P<class>[^:]+)::(?P<method>[^\(]+)\(\)$~';

            if (!preg_match($pattern, $e->getMessage(), $matches)) {
                throw $e;
            }

            if ($matches['class'] != get_class($object) ||
                $matches['method'] != $method) {
                throw $e;
            }

            static::throwBadMethodCallException($method);
        }
    }

他在調用的時候通過try catch來捕獲錯誤。

還有另外一種方法同樣可以判斷這個類有沒有這個方法,那就是先通過get_class_methods($className)這個函數獲取到類的所有方法,然後我們就可以自己判斷了。


    public function __call($method, $parameters)
    {
        $classFuns = get_class_methods($this);
        if (!in_array($method, $classFuns)) {
            return "沒有這個方法"; //返回錯誤
        }

        return $this->$method(...$parameters);
    }

這樣也不失爲一種方法啊,大家還有其他好方法的話歡迎交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章