文件字段

Nova 提供了几种不同类型的字段: File, Image, 和 Avatar. File 字段不仅在文件上传中最为常见, 而且也是 ImageAvatar 字段类的基类. 在接下来的说明中, 我们会探索这些不同的字段以及他们之间的异同.

概述

为了说明 Nova 文件上传字段的行为,假设我们的应用有上传「用户头像」的功能。因此我们会在 users 表中新建一个叫做 profile_photo 的列。这个列将会记录用户头像在磁盘上的路径,或者当我们使用云存储(例如 Amazon S3、七牛云)时,记录用户头像的链接地址。

字段定义

接下来,我们将文件字段附加到 User 资源上。在这个例子中,我们将会创建该字段并将底层文件存储在名为 public 的磁盘中。这个磁盘名必须与 config/filesystems.php 配置文件中的磁盘名对应:

use Laravel\Nova\Fields\File;

File::make('Profile Photo')->disk('public')

文件是如何存储的

当使用该字段上传文件时,Nova 将用 Laravel 的 文件系统 在你选择的磁盘上存储该文件,并生成一个随机文件名。文件存储之后,Nova 将在底层数据库列里保存该文件的相对路径。

为了演示File字段的默认行为,让我们来看一个以相同方式存储文件的等效路由:

use Illuminate\Http\Request;

Route::post('/photo', function (Request $request) {
    $path = $request->profile_photo->store('/', 'public');

    $request->user()->update([
        'profile_photo' => $path,
    ]);
});

当然,文件存储之后,你可以在应用中使用 Laravel 的StorageFacade 检索出该文件:

use Laravel\Support\Facades\Storage;

Storage::get($user->profile_photo);

Storage::url($user->profile_photo);

自定义

以上文档只演示了File字段的默认行为。想了解「自定义行为」的更多信息,请查看 自定义 文档。

图片

Image字段的行为恰好和File字段相像;但是,Image字段并不会在 Nova 面板里显示一个文件路径,而是展示一个底层文件的缩略图预览。Image字段的所有配置和自定义选项都镜像了File字段:

use Laravel\Nova\Fields\Image;

Image::make('Profile Photo')->disk('public')

头像

Avatar字段的行为恰好和File字段相像;但是,Avatar字段并不会在 Nova 面板里显示一个文件路径,而是展示一个底层文件的缩略图预览。Avatar字段的所有配置和自定义选项都镜像了File字段:

use Laravel\Nova\Fields\Avatar;

Avatar::make('Poster')->disk('public')

除了展示底层文件的缩略图预览之外,Avatar字段也会自动显示在 Nova 的搜索结果中。Avatar字段不局限于「用户」资源,你也可以在 Nova 应用程序的任何资源里添加Avatar字段:

Avatar Example

存储文件的元数据

存储系统除了能够存储文件到指定的路径以外,你也可以通过 Nova 的指令存储文件的元数据(比如:原客户端的文件名和文件的大小),你可以使用 storeOriginalNamestoreSize 方法完成这些操作。 这些方法都能够接收你想要存储的信息的参数

use Illuminate\Http\Request;
use Laravel\Nova\Fields\File;
use Laravel\Nova\Fields\Text;

/**
 * 从上传资源获取相应的字段用去展示。
 *  
 * @param  \Illuminate\Http\Request  $request
 * @return array
 */
public function fields(Request $request)
{
    return [
        // ...

        File::make('Attachment')
                ->disk('s3')
                ->storeOriginalName('attachment_name')
                ->storeSize('attachment_size'),

        Text::make('Attachment Name')->exceptOnForms(),

        Text::make('Attachment Size')
                ->exceptOnForms()
                ->displayUsing(function ($value) {
                    return number_format($value / 1024, 2).'kb';
                }),
    ];
}

存储原始客户端的文件名有一个好处就是能够用用原文件名下载文件。例如,你可以在应用程序的路由中执行下面的操作:

use Laravel\Support\Facades\Storage;

Route::get('/download', function () {
    $user = $request->user();

    return Storage::download(
        $user->attachment, $user->attachment_name
    );
})

文件的下载

当你使用 storeOriginalName 方法,在 Nova 的控制台下载文件字段

标记删除

File 字段,ImageAvatar 字段可以标记 prunable,当与文件的关联模型从数据库删除的时候,prunable方法能够命令 Nova 从存储库中删除文件:

File::make('Profile Photo')->disk('public')->prunable()

Nova 不删除的情况

Nova 仅仅会自动删除哪些初始化关联删除的模型,其他没有配置的可能需要模型自己实现文件的删除逻辑了。

自定义

自定义文件存储

之前我们了解到,默认情况下,Nova 使用 Illuminate\Http\UploadedFile 类的 store 方法存储文件。然而,你可以按照你的程序的需求完全自定义这个行为。

自定义 名字 / 路径

如果你只需要自定义磁盘上所存储的文件的名字或路径,可以使用 File 字段的 pathstoreAs 方法:

use Illuminate\Http\Request;

File::make('Attachment')
    ->disk('s3')
    ->path($request->user()->id.'-attachments')
    ->storeAs(function (Request $request) {
        return sha1($request->attachment->getClientOriginalName());
    })

自定义整个存储处理进程

然而,如果你想要掌控一个字段的上所有的文件存储逻辑,你可以用 store 方法。store 方法接收一个回调函数,通过它接收进来的 HTTP 请求以及请求所关联的模型实例:

use Illuminate\Http\Request;

File::make('Attachment')
    ->store(function (Request $request, $model) {
        return [
            'attachment' => $request->attachment->store('/', 's3'),
            'attachment_name' => $request->attachment->getClientOriginalName(),
            'attachment_size' => $request->attachment->getSize(),
        ];
    })

正如你所见,上例中,store 回调函数返回了一个键值对的数组。这些键/值对被映射到模型实例上, 然后才保存到数据库中, 这使得你能够在存储文件后更新一个或多个模型的数据库列。

Invokables

当然,在一个闭包中执行所有的文件存储逻辑可能会导致你的资源变得臃肿。因为这个原因,Nova 允许你传递一个 "可调用的" 对象到 store 方法:

File::make('Attachment')->store(new StoreAttachment)

这个可调用的对象应当是一个简单的 PHP 类,并且有一个 __invoke 方法:

<?php

namespace App\Nova;

use Illuminate\Http\Request;

class StoreAttachment
{
    /**
     * 存储上传的文件
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return array
     */
    public function __invoke(Request $request, $model)
    {
        return [
            'attachment' => $request->attachment->store('/', 's3'),
            'attachment_name' => $request->attachment->getClientOriginalName(),
            'attachment_size' => $request->attachment->getSize(),
        ];
    }
}

自定义文件删除操作

当文件从 Nova 管理员面板中被删除,Nova 将自动移除底层存储中的文件,并在字段相关联列中插入 NULL 值。

如果你想要重写这个行为并提供你自己的文件删除的实现,你可以使用 delete 方法。类似于上面讨论的 store 方法,delete 方法接受一个回调函数,该函数接收进来的 HTTP 请求和该请求相关联的模型实例:

use Illuminate\Http\Request;
use Laravel\Support\Facades\Storage;

File::make('Attachment')
    ->disk('s3')
    ->delete(function (Request $request, $model) {
        if (! $model->attachment) {
            return;
        }

        Storage::disk($this->disk)->delete($model->attachment);

        return [
            'attachment' => null,
            'attachment_name' => null,
            'attachment_size' => null,
        ];
    })

从上例中可以看出,delete 回调函数返回一个键值对的数组。这些键/值对被映射到模型实例上, 然后才保存到数据库中, 这使得你能够在存储文件后更新一个或多个模型的数据库列。通常来说,当删除一个字段时,你将插入 NULL 值到相关的数据库列中。

Invokables

当然,在一个闭包中执行所有的文件删除逻辑可能会导致资源变得臃肿。由于这个原因,Nova 允许你传递一个 "可调用的" 对象到 delete 方法中:

File::make('Attachment')->delete(new DeleteAttachment)

这个可调用对象应当是一个简单的 PHP 类,并且有一个 __invoke 方法:

<?php

namespace App\Nova;

use Illuminate\Http\Request;
use Laravel\Support\Facades\Storage;

class DeleteAttachment
{
    /**
     * 删除字段关联的文件
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return array
     */
    public function __invoke(Request $request, $model)
    {
        if (! $model->attachment) {
            return;
        }

        Storage::disk($this->disk)->delete($model->attachment);

        return [
            'attachment' => null,
            'attachment_name' => null,
            'attachment_size' => null,
        ];
    }
}

自定义预览

默认情况下,Nova 是使用 Storage::url 的方法确定 URL,用来在资源详细页上显示图片预览。然而你也可以使用 preview 来自定义 URL 的生成。
对于 preview 方法接受一个返回预览 URL 的调用。在调用中,你可以通过 $this->value 访问字段的底层列值。如下所示:

use Laravel\Nova\Fields\Image;
use Laravel\Support\Facades\Storage;

Image::make('Profile Photo')
    ->disk('public')
    ->preview(function () {
        return $this->value
                    ? Storage::disk($this->disk)->url($this->value)
                    : null;
    })

预览尺寸

默认情况系,Nova 的资源详情页将显示宽为318像素预览图(为“视网膜屏幕”显示宽为636像素) 。

自定义缩略图

默认情况下,Nova 使用 Storage::url 确定 URL。用于在资源索引屏幕上和搜索结果中(当使用 Avatar 字段时),显示缩略图预览。 然而,你可以使用 thumbnail 方法来定制这个 URL 的生成。

这个 thumbnail 方法接受一个可以返回缩略图 URL 的调用。在调用中,你可以通过 $this->value 访问字段的底层列值。如下所示:

use Laravel\Nova\Fields\Image;
use Laravel\Support\Facades\Storage;

Image::make('Profile Photo')
    ->disk('public')
    ->thumbnail(function () {
        return $this->value
                    ? Storage::disk($this->disk)->url($this->value)
                    : null;
    })

缩略图尺寸

默认情况下,Nova 显示的缩略图宽度为 32 像素(为“视网膜屏幕”显示 64 像素)。

本文章首发在 LearnKu.com 网站上。
上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
贡献者:8
讨论数量: 0
发起讨论 只看当前版本


暂无话题~