初生牛犢不怕虎,直接莽上laravels
前因
我們公司一直都是採用 think3.2 ,雖然使用起來確實不錯,但是哪能有 laravel 舒服
於是我才用 laravel ,但是經理覺得後臺太慢了
我做了一部分最佳化還是慢,我太難了,之前他讓我隨便選框架
我就想到了大佬的 [laravels](https://github.com/hhxsv5/laravel-s) 心裡想laravels+laravel-admin豈不是美哉
結果一使用出
大問題
發現問題,問題介紹
前端問題
laravel-s
已提供Cleaner,請參閱文件使用
- 第一個問題就是比較常見的,頂部的重新整理無限增多,如圖
- 第二個問題就是,刪除
這個
卻進入上一個進入的刪除路由,導致各種刪除失敗
- 匯出excel提示
exit
- Pjax中介軟體提示
exit
解決問題,提供思路
授人以魚不如授人以漁
我們先排查第一個問題,這個比較常見,其實就是我們的Admin
例項並沒有被清除,導致無限新增navBar
然後在 admin/bootstrap.php
中每次都呼叫一下
於是我想到第一個辦法,這個方法比較
憨批
,因為當時認為這個例項重新建立應該挺麻煩,所以採用區域性清理的方式
解決問題之後測試刪除功能,又發現新的問題,也就是
問題2
這個問題很明顯是例項沒清理乾淨,導致上一步生成的js下一步仍然存在,排除在控制器生成的可能,
所以初步判斷只有可能存在於\Encore\Admin\Admin
例項中,因為這個例項是存在於容器中
所以直接從中尋找,發現原來是\Encore\Admin\Admin
靜態變數
//因為是表單所以我們先進入 Encore\Admin\Form\Form 尋找Tool 發現renderDelete
protected function renderDelete()
{
$trans = [
'delete_confirm' => trans('admin.delete_confirm'),
'confirm' => trans('admin.confirm'),
'cancel' => trans('admin.cancel'),
'delete' => trans('admin.delete'),
];
.
.
.
Admin::script($script);
return <<<HTML
<div class="btn-group pull-right" style="margin-right: 5px">
<a href="javascript:void(0);" class="btn btn-sm btn-danger {$class}-delete" title="{$trans['delete']}">
<i class="fa fa-trash"></i><span class="hidden-xs"> {$trans['delete']}</span>
</a>\</div>
HTML;
}
看到
Admin::script
這個原來是在Admin 下的HasAssets
/**
* @param string $script
* @param bool $deferred
* @return array|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public static function script($script = '', $deferred = false)
{
if (!empty($script)) {
if ($deferred) {
return self::$deferredScript = array_merge(self::$deferredScript, (array) $script);
}
return self::$script = array_merge(self::$script, (array) $script);
}
$script = array_unique(array_merge(static::$script, static::$deferredScript));
return view('admin::partials.script', compact('script'));
}
那麼解決起來就狠方便了 直接在bootstrap
中初始化一下這些靜態變數即可,儘量少改原始碼
所以自己實現一個
clean
, laravel-s已提供Cleaner
這裡僅供參考
\Encore\Admin\Admin::$script=[];
\Encore\Admin\Admin::$deferredScript=[];
\Encore\Admin\Admin::$headerJs = [];
\Encore\Admin\Admin::$manifestData = [];
\Encore\Admin\Admin::$extensions = [];
.
.
.
$app->forgetInstance(\Encore\Admin\Admin::class);
Facade::clearResolvedInstance(\Encore\Admin\Admin::class);
由此,`問題一`和`問題二`已經基本解決
下面就是解決
exit
問題了,採用丟擲異常
的方式
//csv匯出操作
$res = Response::stream(function () {
$handle = fopen('php://output', 'w');
$titles = [];
$this->chunk(function ($records) use ($handle, &$titles) {
if (empty($titles)) {
$titles = $this->getHeaderRowFromRecords($records);
// Add CSV headers
fputcsv($handle, $titles);
}
foreach ($records as $record) {
fputcsv($handle, $this->getFormattedRecord($record));
}
});
// Close the output stream
fclose($handle);
}, 200, $headers);
swoole_exit($res);
//Pjax 修改後
$next = function () use ($response) {
return $response;
};
swoole_exit((new static())->handle(Request::capture(), $next));
//swoole_exit實際是封裝的丟擲異常
if (!function_exists('swoole_exit')){
function swoole_exit($response)
{
throw new App\Exceptions\SwooleExitException($response);
}
}
我們需要修改異常處理
Handler
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Response;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
class Handler extends ExceptionHandler\
{
/**
* A list of the exception types that are not reported.\ *\ * @var array
*/
protected $dontReport = [
SwooleExitException::class
];
/**
* A list of the inputs that are never flashed for validation exceptions.\ *\ * @var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Report or log an exception@param \Exception $exception\
* @return void\
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.\ *\ * @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
//判斷是否為我們的自定義異常
if ($exception instanceof SwooleExitException) {
//直接呼叫perpare
return $exception->getResponse()->prepare($request);
}
return parent::render($request, $exception);
}
}
自定義異常程式碼
SwooleExitException
<?php
namespace App\Exceptions;
use Exception;
use Throwable;
class SwooleExitException extends Exception\
{
protected $response;
public function __construct($response,$message = "", $code = 0, Throwable $previous = null)
{
$this->response = $response;
parent::__construct($message, $code, $previous);
}
//獲取響應內容
public function getResponse(){
return $this->response;
}
如果你直接把exit去掉,那麼 匯出csv 會提示你 oops … ob_end_clean() 這個報錯
我們可以在Hhxsv5\LaravelS\Illuminate\Laravel
中的 handleDynamic
加一個判斷ob_get_length()
也可以不加,因為後期解決exit退出問題,也就不會有這種報錯
解決思路
因為exit其實就是不執行後續的響應
,所以我們想到異常就是執行到異常丟擲之前,於是我們可以定義一種特定的異常,來提前結束,用來代替exit ,至於 swoole/laravels 為什麼不能使用這些函式我就不在這贅述
使用異常,但是我們也得返回請求,但是我們的請求不能直接send掉,因為laravels需要使用swoole的方式返回,我們直接執行 send 是不起作用的,那我們就可以透過特定的異常返回我們所需要的響應,
public static function toResponse($request, $response)\
{
if ($response instanceof Responsable) {
$response = $response->toResponse($request);
}
if ($response instanceof PsrResponseInterface) {
$response = (new HttpFoundationFactory)->createResponse($response);
} elseif ($response instanceof Model && $response->wasRecentlyCreated) {
$response = new JsonResponse($response, 201);
} elseif (! $response instanceof SymfonyResponse &&
($response instanceof Arrayable ||
$response instanceof Jsonable ||
$response instanceof ArrayObject ||
$response instanceof JsonSerializable ||
is_array($response))) {
$response = new JsonResponse($response);
} elseif (! $response instanceof SymfonyResponse) {
$response = new Response($response);
}
if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) {
$response->setNotModified();
}
return $response->prepare($request);
}
Router::toResponse();
$exception->prepare()
兩種方式都可以,不過推薦使用後者
出現json格式異常/其他異常,主要是因為 其他擴充套件可能有替換Handler/自定義Handler沒有進行處理,需要額外注意
感謝z-song
提供的laravel-admin優質擴充套件
感謝hhxsv5
提供的laravel-s優質擴充套件
具體分析請等下次更新,我們把laravels
大致走一遍,學習一下(?)
本作品採用《CC 協議》,轉載必須註明作者和本文連結