首先,這是一個略感奇葩的需求,不要問我為什麼,如果有遇到過相同需求,握爪先。
本文的重點是,如何實現擴充套件laravel-admin
原有form
元件的部分功能。
在使用laravel-admin
後臺擴充套件包時,根據客戶需求,希望其原有form
元件中的 檔案/圖片上傳 功能可以實現 上傳新圖但保留原圖。(<<== 吐槽:你這是要弄啥嘞!!)
追根溯源
透過審查程式碼,可知專案中用到的 $form->image('photo', '照片')
是基於 Encore\Admin\Form
類中的魔術方法 __call()
實現的,其本質是將 Encore\Admin\Form\Field\Image
類例項化,使之渲染並繫結當前 Form
表單中 type
為 file
的 input
框,如下:
<input type="file" name="photo" ...>
之所以原有 image
元件會在上傳新圖的同時刪除原圖,原因在於 Encore\Admin\Form\Field\Image
中的 prepare()
方法:
public function prepare($image)
{
if (request()->has(static::FILE_DELETE_FLAG)) {
return $this->destroy();
}
$this->name = $this->getStoreName($image);
$this->callInterventionMethods($image->getRealPath());
return $this->uploadAndDeleteOriginal($image); // <<== 看這裡!!
}
此處,呼叫了 Encore\Admin\Form\Field\File
中的 uploadAndDeleteOriginal()
方法:
protected function uploadAndDeleteOriginal(UploadedFile $file)
{
$this->renameIfExists($file);
$path = $this->storage->putFileAs($this->getDirectory(), $file, $this->name);
$this->destroy(); // <<== 看到了吧,重點在這裡 。。。
return $path;
}
對,這裡關鍵的一句 $this->destroy();
,正是刪除原圖的癥結所在。
解決方案
找到這一步,解決思路應該比較清晰了,有的童鞋講:“把那一行程式碼註釋掉不就ok了麼”。
NO,NO,NO,圖樣圖森破!修改擴充套件包原始碼,乃是下下策!
正確的解題姿勢應該是醬紫滴:
根據官方文件推薦的 Form
元件擴充套件方法(參見:Form
元件管理),我們在後臺部分的程式碼目錄下建立一個目錄用於元件擴充套件,比如:app/Admin/Extensions/Form
,對應的名稱空間隨意,比如:App\Admin\Extensions\Form
。
藍後,建立一個自己喜歡的 Image
元件類,命名隨意,比如:ExtraImage
,具體程式碼:
<?php
namespace App\Admin\Extensions\Form;
use Encore\Admin\Form\Field\Image;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class ExtraImage extends Image
{
/**
* Upload file and delete original file.
*
* @param UploadedFile $file
*
* @return mixed
*/
protected function uploadAndDeleteOriginal(UploadedFile $file)
{
$this->renameIfExists($file);
$path = $this->storage->putFileAs($this->getDirectory(), $file, $this->name);
// $this->destroy(); // <<== 重點!!
return $path;
}
}
再藍後,找到 app/Admin/bootstrap.php
,重新註冊 Image
元件:
use App\Admin\Extensions\Form\ExtraImage;
...
Encore\Admin\Form::forget([..., 'image']); // 刪除原有註冊的 Image 元件
Encore\Admin\Form::extend('image', ExtraImage::class); // 重新註冊新的 Image 元件
至此,上傳新圖但保留原圖 功能實現。
$form->image('photo', '照片')
->uniqueName()
->removable()
->move('original/' . date('Ym', now()->timestamp))
->help('Photo尺寸:420 * 380')
->rules('image');
功能最佳化
然鵝 ~~ 這樣做,有一個問題:我們更改了原有 Image
元件的功能性狀。怎樣可以保留框架原有的 Image
元件功能性狀呢?求相容,求相容!!
其實,相容性的實現是很簡單滴~~
將 App\Admin\Extensions\Form\ExtraImage
中程式碼稍作修改,如下:
<?php
namespace App\Admin\Extensions\Form;
use Encore\Admin\Form\Field\Image;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class ExtraImage extends Image
{
protected $isDeletable = false; // <<== 立了一個Flag,標識是否支援上傳新圖同時可刪除原圖
/**
* Upload file and delete original file.
*
* @param UploadedFile $file
*
* @return mixed
*/
protected function uploadAndDeleteOriginal(UploadedFile $file)
{
$this->renameIfExists($file);
$path = $this->storage->putFileAs($this->getDirectory(), $file, $this->name);
if (!$this->isDeletable){ // <<== 論 Flag 的正確用法 。。。
$this->destroy();
}
return $path;
}
public function deletable($bool = false)
{
$this->isDeletable = $bool;
return $this;
}
}
改造後的 Image
元件的用法如下:
// 上傳新圖不保留原圖
$form->image('photo', '照片')
->uniqueName()
->removable()
->move('original/' . date('Ym', now()->timestamp))
->help('Photo尺寸:420 * 380')
->rules('image');
// 上傳新圖但保留原圖
$form->image('photo', '照片')
->deletable(true) // <<== 重點!!
->uniqueName()
->removable()
->move('original/' . date('Ym', now()->timestamp))
->help('Photo尺寸:420 * 380')
->rules('image');
至此,新的 Image
元件實現 上傳新圖但保留原圖,且相容原有 Image
元件,功能改造完美收官。。。
泉涸,魚相與處於陸,
相呴以溼,相濡以沫,不如相忘於江湖。
與其譽堯而非桀也,不如兩忘而化其道。
本作品採用《CC 協議》,轉載必須註明作者和本文連結