Laravel Nova 文件字段

2023-02-16 17:08 更新

Nova 提供了幾種不同類型的字段: FileImage, 和 AvatarFile 字段不僅在文件上傳中最為常見,而且也是  Image 和 Avatar 字段類的基類。在接下來的說明中,我們會探索這些不同的字段以及他們之間的異同.

概述

為了說明 Nova 文件上傳字段的行為,假設(shè)我們的應(yīng)用有上傳「用戶頭像」的功能。因此我們會在 users 表中新建一個叫做 profile_photo 的列。這個列將會記錄用戶頭像在磁盤上的路徑,或者當(dāng)我們使用云存儲(例如 Amazon S3、七牛云)時,記錄用戶頭像的鏈接地址。

字段定義

接下來,我們將文件字段附加到 User 資源上。在這個例子中,我們將會創(chuàng)建該字段并將底層文件存儲在名為 public 的磁盤中。這個磁盤名必須與 config/filesystems.php 配置文件中的磁盤名對應(yīng):

use Laravel\Nova\Fields\File;

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

文件是如何存儲的

當(dāng)使用該字段上傳文件時,Nova 將用 Laravel 的 文件系統(tǒng) 在你選擇的磁盤上存儲該文件,并生成一個隨機(jī)文件名。文件存儲之后,Nova 將在底層數(shù)據(jù)庫列里保存該文件的相對路徑。

為了演示 File 字段的默認(rèn)行為,讓我們來看一個以相同方式存儲文件的等效路由:

use Illuminate\Http\Request;

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

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

當(dāng)然,文件存儲之后,你可以在應(yīng)用中使用 Laravel 的 StorageFacade 檢索出該文件:

use Laravel\Support\Facades\Storage;

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

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

圖片

Image 字段的行為恰好和 File 字段相像;但是,Image 字段并不會在 Nova 面板里顯示一個文件路徑,而是展示一個底層文件的縮略圖預(yù)覽。Image 字段的所有配置和自定義選項都鏡像了 File 字段:

use Laravel\Nova\Fields\Image;

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

頭像

Avatar 字段的行為恰好和 File 字段相像;但是,Avatar 字段并不會在 Nova 面板里顯示一個文件路徑,而是展示一個底層文件的縮略圖預(yù)覽。Avatar 字段的所有配置和自定義選項都鏡像了 File 字段:

use Laravel\Nova\Fields\Avatar;

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

除了展示底層文件的縮略圖預(yù)覽之外,Avatar 字段也會自動顯示在 Nova 的搜索結(jié)果中。Avatar 字段不局限于「用戶」資源,你也可以在 Nova 應(yīng)用程序的任何資源里添加 Avatar 字段:


存儲文件的元數(shù)據(jù)

存儲系統(tǒng)除了能夠存儲文件到指定的路徑以外,你也可以通過 Nova 的指令存儲文件的元數(shù)據(jù)(比如:原客戶端的文件名和文件的大?。?,你可以使用 storeOriginalName 和 storeSize 方法完成這些操作。 這些方法都能夠接收你想要存儲的信息的參數(shù)

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

/**
 * 從上傳資源獲取相應(yīng)的字段用去展示。
 *  
 * @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';
                }),
    ];
}

存儲原始客戶端的文件名有一個好處就是能夠用用原文件名下載文件。例如,你可以在應(yīng)用程序的路由中執(zhí)行下面的操作:

use Laravel\Support\Facades\Storage;

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

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

文件的下載

當(dāng)你使用 storeOriginalName 方法,在 Nova 的控制臺下載文件字段

標(biāo)記刪除

File 字段,Image,Avatar 字段可以標(biāo)記 prunable,當(dāng)與文件的關(guān)聯(lián)模型從數(shù)據(jù)庫刪除的時候,prunable 方法能夠命令 Nova 從存儲庫中刪除文件:

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

Nova 不刪除的情況

Nova 僅僅會自動刪除哪些初始化關(guān)聯(lián)刪除的模型,其他沒有配置的可能需要模型自己實現(xiàn)文件的刪除邏輯了。

自定義

自定義文件存儲

之前我們了解到,默認(rèn)情況下,Nova 使用 Illuminate\Http\UploadedFile 類的 store 方法存儲文件。然而,你可以按照你的程序的需求完全自定義這個行為。

自定義 名字 / 路徑

如果你只需要自定義磁盤上所存儲的文件的名字或路徑,可以使用 File 字段的 path 和 storeAs 方法:

use Illuminate\Http\Request;

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

自定義整個存儲處理進(jìn)程

然而,如果你想要掌控一個字段的上所有的文件存儲邏輯,你可以用 store 方法。store 方法接收一個回調(diào)函數(shù),通過它接收進(jìn)來的 HTTP 請求以及請求所關(guān)聯(lián)的模型實例:

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 回調(diào)函數(shù)返回了一個鍵值對的數(shù)組。這些鍵 / 值對被映射到模型實例上,然后才保存到數(shù)據(jù)庫中,這使得你能夠在存儲文件后更新一個或多個模型的數(shù)據(jù)庫列。

Invokables

當(dāng)然,在一個閉包中執(zhí)行所有的文件存儲邏輯可能會導(dǎo)致你的資源變得臃腫。因為這個原因,Nova 允許你傳遞一個 "可調(diào)用的" 對象到 store 方法:

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

這個可調(diào)用的對象應(yīng)當(dāng)是一個簡單的 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(),
        ];
    }
}

自定義文件刪除操作

當(dāng)文件從 Nova 管理員面板中被刪除,Nova 將自動移除底層存儲中的文件,并在字段相關(guān)聯(lián)列中插入 NULL 值。

如果你想要重寫這個行為并提供你自己的文件刪除的實現(xiàn),你可以使用 delete 方法。類似于上面討論的 store 方法,delete 方法接受一個回調(diào)函數(shù),該函數(shù)接收進(jìn)來的 HTTP 請求和該請求相關(guān)聯(lián)的模型實例:

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 回調(diào)函數(shù)返回一個鍵值對的數(shù)組。這些鍵 / 值對被映射到模型實例上,然后才保存到數(shù)據(jù)庫中,這使得你能夠在存儲文件后更新一個或多個模型的數(shù)據(jù)庫列。通常來說,當(dāng)刪除一個字段時,你將插入 NULL 值到相關(guān)的數(shù)據(jù)庫列中。

Invokables

當(dāng)然,在一個閉包中執(zhí)行所有的文件刪除邏輯可能會導(dǎo)致資源變得臃腫。由于這個原因,Nova 允許你傳遞一個 "可調(diào)用的" 對象到 delete 方法中:

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

這個可調(diào)用對象應(yīng)當(dāng)是一個簡單的 PHP 類,并且有一個 __invoke 方法:

<?php

namespace App\Nova;

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

class DeleteAttachment
{
    /**
     * 刪除字段關(guān)聯(lián)的文件
     *
     * @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,
        ];
    }
}

自定義預(yù)覽

默認(rèn)情況下,Nova 是使用 Storage::url 的方法確定 URL,用來在資源詳細(xì)頁上顯示圖片預(yù)覽。然而你也可以使用 preview 來自定義 URL 的生成。

對于 preview 方法接受一個返回預(yù)覽 URL 的調(diào)用。在調(diào)用中,你可以通過 $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;
    })

預(yù)覽尺寸

默認(rèn)情況系,Nova 的資源詳情頁將顯示寬為 318 像素預(yù)覽圖(為 “視網(wǎng)膜屏幕” 顯示寬為 636 像素) 。

自定義縮略圖

默認(rèn)情況下,Nova 使用 Storage::url 確定 URL。用于在資源索引屏幕上和搜索結(jié)果中(當(dāng)使用 Avatar 字段時),顯示縮略圖預(yù)覽。 然而,你可以使用 thumbnail 方法來定制這個 URL 的生成。

這個 thumbnail 方法接受一個可以返回縮略圖 URL 的調(diào)用。在調(diào)用中,你可以通過 $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;
    })

縮略圖尺寸

默認(rèn)情況下,Nova 顯示的縮略圖寬度為 32 像素(為 “視網(wǎng)膜屏幕” 顯示 64 像素)。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號