Nova 提供了幾種不同類型的字段: File
, Image
, 和 Avatar
. File
字段不僅在文件上傳中最為常見,而且也是
Image
和 Avatar
字段類的基類。在接下來的說明中,我們會(huì)探索這些不同的字段以及他們之間的異同.
為了說明 Nova 文件上傳字段的行為,假設(shè)我們的應(yīng)用有上傳「用戶頭像」的功能。因此我們會(huì)在 users
表中新建一個(gè)叫做 profile_photo
的列。這個(gè)列將會(huì)記錄用戶頭像在磁盤上的路徑,或者當(dāng)我們使用云存儲(chǔ)(例如 Amazon S3、七牛云)時(shí),記錄用戶頭像的鏈接地址。
接下來,我們將文件字段附加到 User
資源上。在這個(gè)例子中,我們將會(huì)創(chuàng)建該字段并將底層文件存儲(chǔ)在名為 public
的磁盤中。這個(gè)磁盤名必須與 config/filesystems.php
配置文件中的磁盤名對(duì)應(yīng):
use Laravel\Nova\Fields\File;
File::make('Profile Photo')->disk('public')
當(dāng)使用該字段上傳文件時(shí),Nova 將用 Laravel 的 文件系統(tǒng) 在你選擇的磁盤上存儲(chǔ)該文件,并生成一個(gè)隨機(jī)文件名。文件存儲(chǔ)之后,Nova 將在底層數(shù)據(jù)庫(kù)列里保存該文件的相對(duì)路徑。
為了演示 File
字段的默認(rèn)行為,讓我們來看一個(gè)以相同方式存儲(chǔ)文件的等效路由:
use Illuminate\Http\Request;
Route::post('/photo', function (Request $request) {
$path = $request->profile_photo->store('/', 'public');
$request->user()->update([
'profile_photo' => $path,
]);
});
當(dāng)然,文件存儲(chǔ)之后,你可以在應(yīng)用中使用 Laravel 的 Storage
Facade 檢索出該文件:
use Laravel\Support\Facades\Storage;
Storage::get($user->profile_photo);
Storage::url($user->profile_photo);
Image
字段的行為恰好和 File
字段相像;但是,Image
字段并不會(huì)在 Nova 面板里顯示一個(gè)文件路徑,而是展示一個(gè)底層文件的縮略圖預(yù)覽。Image
字段的所有配置和自定義選項(xiàng)都鏡像了 File
字段:
use Laravel\Nova\Fields\Image;
Image::make('Profile Photo')->disk('public')
Avatar
字段的行為恰好和 File
字段相像;但是,Avatar
字段并不會(huì)在 Nova 面板里顯示一個(gè)文件路徑,而是展示一個(gè)底層文件的縮略圖預(yù)覽。Avatar
字段的所有配置和自定義選項(xiàng)都鏡像了 File
字段:
use Laravel\Nova\Fields\Avatar;
Avatar::make('Poster')->disk('public')
除了展示底層文件的縮略圖預(yù)覽之外,Avatar
字段也會(huì)自動(dòng)顯示在 Nova 的搜索結(jié)果中。Avatar
字段不局限于「用戶」資源,你也可以在 Nova 應(yīng)用程序的任何資源里添加 Avatar
字段:
存儲(chǔ)系統(tǒng)除了能夠存儲(chǔ)文件到指定的路徑以外,你也可以通過 Nova 的指令存儲(chǔ)文件的元數(shù)據(jù)(比如:原客戶端的文件名和文件的大小),你可以使用 storeOriginalName
和 storeSize
方法完成這些操作。 這些方法都能夠接收你想要存儲(chǔ)的信息的參數(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';
}),
];
}
存儲(chǔ)原始客戶端的文件名有一個(gè)好處就是能夠用用原文件名下載文件。例如,你可以在應(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 的控制臺(tái)下載文件字段
File
字段,Image
,Avatar
字段可以標(biāo)記 prunable
,當(dāng)與文件的關(guān)聯(lián)模型從數(shù)據(jù)庫(kù)刪除的時(shí)候,prunable 方法能夠命令 Nova 從存儲(chǔ)庫(kù)中刪除文件:
File::make('Profile Photo')->disk('public')->prunable()
Nova 不刪除的情況
Nova 僅僅會(huì)自動(dòng)刪除哪些初始化關(guān)聯(lián)刪除的模型,其他沒有配置的可能需要模型自己實(shí)現(xiàn)文件的刪除邏輯了。
之前我們了解到,默認(rèn)情況下,Nova 使用 Illuminate\Http\UploadedFile
類的 store
方法存儲(chǔ)文件。然而,你可以按照你的程序的需求完全自定義這個(gè)行為。
如果你只需要自定義磁盤上所存儲(chǔ)的文件的名字或路徑,可以使用 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());
})
然而,如果你想要掌控一個(gè)字段的上所有的文件存儲(chǔ)邏輯,你可以用 store
方法。store
方法接收一個(gè)回調(diào)函數(shù),通過它接收進(jìn)來的 HTTP 請(qǐng)求以及請(qǐng)求所關(guān)聯(lián)的模型實(shí)例:
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ù)返回了一個(gè)鍵值對(duì)的數(shù)組。這些鍵 / 值對(duì)被映射到模型實(shí)例上,然后才保存到數(shù)據(jù)庫(kù)中,這使得你能夠在存儲(chǔ)文件后更新一個(gè)或多個(gè)模型的數(shù)據(jù)庫(kù)列。
當(dāng)然,在一個(gè)閉包中執(zhí)行所有的文件存儲(chǔ)邏輯可能會(huì)導(dǎo)致你的資源變得臃腫。因?yàn)檫@個(gè)原因,Nova 允許你傳遞一個(gè) "可調(diào)用的" 對(duì)象到 store
方法:
File::make('Attachment')->store(new StoreAttachment)
這個(gè)可調(diào)用的對(duì)象應(yīng)當(dāng)是一個(gè)簡(jiǎn)單的 PHP 類,并且有一個(gè) __invoke
方法:
<?php
namespace App\Nova;
use Illuminate\Http\Request;
class StoreAttachment
{
/**
* 存儲(chǔ)上傳的文件
*
* @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 將自動(dòng)移除底層存儲(chǔ)中的文件,并在字段相關(guān)聯(lián)列中插入 NULL
值。
如果你想要重寫這個(gè)行為并提供你自己的文件刪除的實(shí)現(xiàn),你可以使用 delete
方法。類似于上面討論的 store
方法,delete
方法接受一個(gè)回調(diào)函數(shù),該函數(shù)接收進(jìn)來的 HTTP 請(qǐng)求和該請(qǐng)求相關(guān)聯(lián)的模型實(shí)例:
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ù)返回一個(gè)鍵值對(duì)的數(shù)組。這些鍵 / 值對(duì)被映射到模型實(shí)例上,然后才保存到數(shù)據(jù)庫(kù)中,這使得你能夠在存儲(chǔ)文件后更新一個(gè)或多個(gè)模型的數(shù)據(jù)庫(kù)列。通常來說,當(dāng)刪除一個(gè)字段時(shí),你將插入 NULL
值到相關(guān)的數(shù)據(jù)庫(kù)列中。
當(dāng)然,在一個(gè)閉包中執(zhí)行所有的文件刪除邏輯可能會(huì)導(dǎo)致資源變得臃腫。由于這個(gè)原因,Nova 允許你傳遞一個(gè) "可調(diào)用的" 對(duì)象到 delete
方法中:
File::make('Attachment')->delete(new DeleteAttachment)
這個(gè)可調(diào)用對(duì)象應(yīng)當(dāng)是一個(gè)簡(jiǎn)單的 PHP 類,并且有一個(gè) __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,
];
}
}
默認(rèn)情況下,Nova 是使用 Storage::url
的方法確定 URL,用來在資源詳細(xì)頁上顯示圖片預(yù)覽。然而你也可以使用 preview
來自定義 URL 的生成。
對(duì)于 preview
方法接受一個(gè)返回預(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
字段時(shí)),顯示縮略圖預(yù)覽。 然而,你可以使用 thumbnail
方法來定制這個(gè) URL 的生成。
這個(gè) thumbnail
方法接受一個(gè)可以返回縮略圖 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 像素)。
更多建議: