[Laravel Admin] 文件 / 图片上传功能之扩展 -- 上传新图且保留原图
首先,这是一个略感奇葩的需求,不要问我为什么,如果有遇到过相同需求,握爪先。
本文的重点是,如何实现扩展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 协议》,转载必须注明作者和本文链接
推荐文章: