簡述
Facade
可以有效幫我實現方法的靜態化。Laravel
大部分的擴充套件包都使用了 Facade
。
下面的簡易 Facade
主要是利用 PHP 的特性 trait
,魔術方法 __callStatic
,反射類 ReflectionClass
。
使用場景
後臺系統大部分都會有類似這樣的操作:
<?php
$user = User::find($id);
if (!$user) {
throw new \Expection("資源不存在");
}
這樣似乎沒有什麼問題,但是還會存在下面這樣的:
$article = Article::find($id);
if (!$article) {
throw new \Expection("資源不存在");
}
$article->delete();
這樣寫法十分不優雅。
上程式碼
1、首先我們應該要有一個 Service
<?php
namespace App\Services;
use App\Traits\ModeServiceTrait;
class ModelService extends BaseService
{
use ModeServiceTrait;
}
2、新建一個 Trait
trait 為了多繼承而存在的,可以去 PHP官網 看文件。
<?php
namespace App\Traits;
use \ReflectionClass;
use \Exception;
use \ReflectionException;
use Illuminate\Database\Eloquent\Model;
use App\Exceptions\ResourceException;
/**
* @method static Model find(string $className, int $id, callable $callback = null)
*
* @see Model
* @package App\Services
*/
trait ModeServiceTrait
{
/**
* 回撥方法
*
* @param Model|null $model
* @param string $method
* @return Model
* @throws ResourceException
*/
public static function callback(Model|null $model, string $method): Model
{
switch ($method)
{
case 'first':
case 'find':
if (!$model) {
throw new ResourceException("資源不存在");
}
break;
default:
break;
}
return $model;
}
/**
* 呼叫不存在的方法時觸發
*
* @param $method
* @param $args
* @return false|mixed
* @throws ReflectionException
* @throws ResourceException
* @throws Exception
*/
public static function __callStatic($method, $args)
{
$className = $args[0];
$arg = $args[1];
// 判斷模型類是否存在
if (!class_exists($className)) {
throw new Exception("The class {$className} could not be found. from:" . __CLASS__);
}
// 利用反射例項化其類
$reflection = new ReflectionClass($className);
$instance = $reflection->newInstanceArgs();
// 呼叫該不存在的方法
$model = call_user_func_array([$instance, $method], [$arg]);
// 如果存在複雜操作交給 callback
return isset($args[2]) ? $args[2]($model) : self::callback($model, $method);
}
}
首先我們關注 __callStatic
這個魔術方法。 當呼叫不存在的靜態方法時會觸發該方法。和他相似的魔術方法是 __call
。這是使用 __callStatic
是為了達到 Facade
的效果。
__callStatic
有兩個回撥引數 $method
是 被呼叫的且不存在的方法
,$args
是 $method
方法中所傳遞的引數(陣列形式)。
這樣一個簡易的 trait
就完成了。
使用
我們新建一個 command
$ php artisan make:command TestCommand
寫入下面的內容
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\ModelService;
use App\Models\Article\Article;
class TestCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'test:test';
/**
* The console command description.
*
* @var string
*/
protected $description = 'a test';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$article = ModelService::find(Article::class, 1);
$article = ModelService::find(Article::class, 1, function ($model) {
return $model->load('author');
});
}
}
其中的 Article
模型需要自己去建立。
接下來就可以看看效果了:
$ php artisan test:test
結語
這樣我們就拋棄了使用 abstract
抽象類,來達到了跟 Facade
一樣的效果。同時也做到了程式碼複用。
這樣使用程式會多走很多步,但是跟優雅比起來,效能什麼的都無所謂了。
表達不是很清楚,需要自己深入體會了。???
本作品採用《CC 協議》,轉載必須註明作者和本文連結