Laravel 後臺擴充套件包 Laravel-admin 檔案 / 圖片上傳功能之擴充 -- 實現上傳新圖且保留原圖

Elijah_Wang發表於2019-05-16

首先,這是一個略感奇葩的需求,不要問我為什麼,如果有遇到過相同需求,握爪先。
本文的重點是,如何實現擴充 laravel-admin 原有 form 元件的部分功能。
在使用 laravel-admin 後臺擴充套件包時,根據客戶需求,希望其原有 form 元件中的 檔案/圖片上傳 功能可以實現 上傳新圖但保留原圖

追根溯源

通過審查程式碼,可知專案中用到的 $form->image('photo', '照片') 是基於 Encore\Admin\Form 類中的魔術方法 __call() 實現的,其本質是將 Encore\Admin\Form\Field\Image 類例項化,使之渲染並繫結當前 Form 表單中 typefileinput 框,如下:

<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` 元件,功能改造完美收官。。。

夏蟲不語冰

相關文章