初生牛犢不怕虎,直接莽上laravels
前因
我們公司一直都是採用 think3.2 ,雖然使用起來確實不錯,但是哪能有 laravel 舒服
於是我才用 laravel ,但是經理覺得後臺太慢了
我做了一部分優化還是慢,我太難了,之前他讓我隨便選框架
我就想到了大佬的 laravels 心裡想laravels+laravel-admin豈不是美哉,畢竟有swoole 以前學過一些,覺得應該可以把持住
結果一使用出
大問題
發現問題,問題介紹
- 第一個問題就是比較常見的,頂部的重新整理無限增多,如圖
- 第二個問題就是,刪除
這個
卻進入上一個進入的刪除路由,導致各種刪除失敗
- 匯出excel提示exit
4.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'),
];
$class = uniqid();
$script = <<<SCRIPT
$('.{$class}-delete').unbind('click').click(function() {
swal({ title: "{$trans['delete_confirm']}",
type: "warning",\ showCancelButton: true, confirmButtonColor: "#DD6B55",\ confirmButtonText: "{$trans['confirm']}",
showLoaderOnConfirm: true,\ cancelButtonText: "{$trans['cancel']}",\
preConfirm: function() {\ return new Promise(function(resolve) {\ $.ajax({\ method: 'post',\ url: '{$this->getDeletePath()}',
data: {\ _method:'delete',\_token:LA.token,\ },\ success: function (data) {\ $.pjax({container:'#pjax-container', url: '{$this->getListPath()}' });
resolve(data);\ }\ });\ });\ }\ }).then(function(result) {\ var data = result.value;\ if (typeof data === 'object') {\ if (data.status) {\ swal(data.message, '', 'success');\ } else {\ swal(data.message, '', 'error');\ }\ }\ });\});\
SCRIPT;
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,之前
憨批
的方法我也就換成更加粗暴的方式,最終還是清理掉Admin在容器中的例項
\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問題了,中間複雜心裡鬥爭,最後只能採用丟擲異常的方式
Pjax中的exit 在56行
Export中 這裡就很多,我就不一一指出
//匯出操作
$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) {
return Router::toResponse($request, $exception->getResponse());
}
return parent::render($request, $exception);
}
}
<?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 是不起作用的,那我們就可以通過特定的異常返回我們所需要的響應,
至於Router::toResponse();
其實我還沒有分析,只是猜想是返回響應
結果是猜想正確,具體分析請等下次更新,我們把laravels
大致走一遍,學習一下
laravels可以獲取到最終響應然後返回,目的就達到了