首先,這是一個略感奇葩的需求,不要問我為什麼,如果有遇到過相同需求,握爪先。
本文的重點是,如何實現擴充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` 元件管理](https://laravel-admin.org/docs/zh/model-form-field-management)),我們在後臺部分的程式碼目錄下建立一個目錄用於元件擴充套件,比如:`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](https://learnku.com/users/31554) 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` 元件,功能改造完美收官。。。