使用 FilePond 在 Laravel 中上传文件

发布日期:作者:

Uploading Files in Laravel Using FilePond image

在构建 Web 应用程序时,您通常需要添加一个允许用户上传文件的功能。例如,您可能希望允许用户更新他们的个人资料图片或彼此共享文件。

在本文中,我们将探讨如何使用一个名为 "FilePond" 的 JavaScript 库在您的 Laravel 应用程序中上传文件。我们还将简要讨论 Laravel 中文件上传的替代方法。然后我们将看看如何使用 FilePond 一次上传多个文件以及上传图像(带有图像预览)。最后,我们将看看如何验证您上传的文件,如何删除临时文件以及如何为您的文件上传代码编写测试。

什么是 FilePond?

FilePond 是一个 JavaScript 库,允许您在 Web 应用程序中上传文件。

它提供了一个简单、易用且视觉吸引人的文件上传界面。它可以为您构建自己的文件上传功能提供一个很好的起点,而无需过多担心界面的样式和可访问性。

FilePond 允许您同步或异步上传。这意味着您可以在提交表单时以单个请求上传文件(同步),也可以在提交表单之前在单独的请求中上传文件(异步)。使用异步方法通常可以提供更好的用户体验,因为用户可以在文件上传时继续填写表单中的其他字段。出于本文的目的,我们将重点关注异步方法。

还有一些插件可以与 FilePond 一起使用以添加额外的功能。例如,您可以使用 FilePondPluginImagePreview 插件来显示正在上传的图像的预览。事实上,我们将在本文后面看看这个插件。

FilePond 还提供了分块上传文件的能力。如果您想上传可能太大而无法在一个请求中上传的大文件,这很有用。但是,为了本教程的目的,我们只关注在一个请求中上传文件。如果您想了解更多关于如何分块上传文件的信息,您可以查看 FilePond 文档

FilePond 异步文件上传流程如何工作?

为了解释用户如何使用 FilePond 异步地在表单中上传文件,让我们看一个例子。我们将想象用户正在更新他们的个人资料,使用一个允许他们更新他们的姓名和个人资料图片的表单。我们假设用户想上传一个 avatar.png 文件作为他们的新个人资料图片。

流程可能像这样工作

  1. 用户单击表单上 FilePond 组件中的“浏览”。
  2. 传统的上传文件对话框出现,以便用户可以从他们的设备中选择他们想要上传的 avatar.png 文件。
  3. 一旦文件被选中,FilePond 使用 POST 请求将 avatar.png 文件作为 multipart/form-data 发送到服务器。
  4. 服务器(我们的 Laravel 应用程序)然后将文件保存到一个临时、唯一的地址。例如,它可能会将文件保存到 tmp/12345abcdef/avatar.png
  5. 然后服务器在 text/plain 响应中将唯一地址(在本例中为 12345abcdef/avatar.png)返回给 FilePond。
  6. FilePond 在表单上的 hidden 输入字段中添加此唯一地址。
  7. 虽然步骤 3-6 正在运行,用户本可以继续填写表单的其余部分,同时文件正在上传。一旦文件上传完毕,用户就可以提交表单(现在包括 hidden 输入字段)。
  8. 服务器(我们的 Laravel 应用程序)使用唯一地址将文件从临时存储地址移动到其目标地址。例如,它可能会将文件从 tmp/12345abcdef/avatar.png 移动到 avatars/user-1.png

现在我们已经对异步文件上传是如何工作的有了大致的了解,让我们看看它们相对于表单中的同步文件上传的优势。

同步文件上传阻塞 UI

通常,在使用同步方法上传文件的 Web 应用程序中,用户可能会点击表单中的“文件上传”字段。然后,他们可能会选择他们想要上传的文件。一旦他们选择了他们的文件,文件实际上并没有上传到服务器,直到用户提交表单(不像上面我们看到的异步方法)。这意味着当表单提交时,文件在一个请求中(与表单的其余字段一起)上传。

使用这种同步方法有时会阻止用户与 UI 交互。如果文件很大并且需要很长时间才能上传,尤其如此,因为用户不会对正在发生的事情有任何反馈。

这与异步文件上传的工作方式不同。在异步方法中,文件在表单提交之前已经上传到服务器(或正在上传过程中)在一个单独的请求中。

同步文件上传与无服务器平台的问题

如果您在无服务器平台上运行您的应用程序,例如 AWS Lambda,同步文件上传会很快变得很麻烦。在撰写本文时,根据 AWS Lambda 文档,请求的最大大小为 6MB。这意味着您需要确保表单中数据的(包括上传的文件)大小不超过此限制。

这意味着如果您打算在无服务器平台上运行您的应用程序,您需要采用异步方法上传文件。根据您的应用程序,您可能希望直接从浏览器将其上传到您的存储提供商(例如 AWS S3)。这样做的好处是,您可以完全避免文件触及您的服务器。这样做不仅可以更安全(因为您可以避免潜在的恶意文件在您的服务器上被处理),而且还可以提高性能(因为您不需要先将文件上传到您的服务器),并允许您避免 6MB 的限制。

虽然本文中介绍的一般原则可以应用于直接将文件上传到您的存储提供商,但我们将重点关注先将文件上传到您的服务器,然后将其移动到您的存储提供商。但是,如果您使用的是 Laravel Vapor,您可以查看 文档,以了解有关如何直接将文件上传到您的 AWS S3 存储桶的更多信息。

在前端设置 FilePond

现在我们已经了解了异步文件上传流程,让我们来看看如何在 Laravel 应用程序的前端设置 FilePond。

FilePond 提供了多种适配器,可用于不同的框架,例如 Vue、React 和 Angular。但是,在这篇文章中,我们将只使用原生 JavaScript 适配器。

我们将假设我们正在使用 Vite 编译资产的全新 Laravel 安装上工作。

让我们来看一个基本的例子。假设我们正在构建一个 CSV 导入功能,允许用户上传包含产品详细信息的 CSV 文件,这些文件将在我们的 Web 应用程序中创建。

首先,让我们创建一个非常基础的 Blade 视图,其中包含一个包含单个 "file" input 字段的表单。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
 
<title>FilePond Tutorial</title>
 
<meta name="csrf-token" content="{{ csrf_token() }}">
 
@vite('resources/js/app.js')
</head>
 
<body>
<form action="{{ route('products.import') }}" method="POST">
@csrf
 
<input type="file" name="csv" class="filepond"/>
 
<button type="submit">Import Products</button>
</form>
</body>
</html>

现在,让我们通过运行以下命令通过 NPM 安装 FilePond。

npm i filepond --save

然后,我们可以打开我们的 resources/js/app.js 文件,并在我们的 input 字段上添加功能以启用 FilePond。

import * as FilePond from 'filepond';
import 'filepond/dist/filepond.min.css';
 
const inputElement = document.querySelector('input[type="file"].filepond');
 
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
 
FilePond.create(inputElement).setOptions({
server: {
process: './uploads/process',
headers: {
'X-CSRF-TOKEN': csrfToken,
}
}
});

让我们快速看一下上面代码中做了什么。首先,我们导入了提供所需功能和样式的 FilePond JavaScript 和 CSS 文件。

然后,我们继续找到要转换为 FilePond 字段的 input 字段。注意我们如何在查询选择器中添加了 filepond 类。这样,我们就可以区分要转换为 FilePond 字段的 input 字段和可能不希望转换的字段。

然后,我们从添加到 Blade 视图的 meta 标签中获取 CSRF 令牌。这样做是为了将其传递给 FilePond,以便它在尝试上传文件时将其发送到我们的服务器。如果没有添加它,在尝试上传文件时将收到 HTTP 419 错误响应。

然后,我们创建了 FilePond 实例,并指定当我们想要上传新文件时,它应该发送到我们服务器上的 /uploads/process URL。FilePond 还提供了指定用于删除临时上传文件的 URL 的功能,但我们不会在本教程中使用此功能。

前端现在应该准备就绪。如果用户选择了一个 CSV 文件,它将被发送到 /uploads/process URL 并临时存储。表单中的隐藏 csv 字段将填充我们临时存储文件的路径。

在后端设置 FilePond

我们现在可以设置 Laravel 应用程序的后端来处理来自 FilePond 的文件上传。为此,我们需要创建一个负责临时存储上传文件的路由和控制器。

正如我之前提到的,FilePond 确实提供了以块上传文件的能力。但出于本教程的目的,我们将保持简单,只关注单个请求中的文件上传。

我们首先通过运行以下命令创建一个新的 FileUploadController

php artisan make:controller FileUploadController

然后,我们可以向控制器添加一个 process 方法,该方法处理文件上传并将文件存储在存储的 tmp 目录中。

declare(strict_types=1);
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;
 
final class FileUploadController extends Controller
{
public function process(Request $request): string
{
// We don't know the name of the file input, so we need to grab
// all the files from the request and grab the first file.
/** @var UploadedFile[] $files */
$files = $request->allFiles();
 
if (empty($files)) {
abort(422, 'No files were uploaded.');
}
 
if (count($files) > 1) {
abort(422, 'Only 1 file can be uploaded at a time.');
}
 
// Now that we know there's only one key, we can grab it to get
// the file from the request.
$requestKey = array_key_first($files);
 
// If we are allowing multiple files to be uploaded, the field in the
// request will be an array with a single file rather than just a
// single file (e.g. - `csv[]` rather than `csv`). So we need to
// grab the first file from the array. Otherwise, we can assume
// the uploaded file is for a single file input and we can
// grab it directly from the request.
$file = is_array($request->input($requestKey))
? $request->file($requestKey)[0]
: $request->file($requestKey);
 
// Store the file in a temporary location and return the location
// for FilePond to use.
return $file->store(
path: 'tmp/'.now()->timestamp.'-'.Str::random(20)
);
}
}

您可能已经注意到,我们还添加了对接受多个文件上传的表单字段的支持。我们将在本文后面介绍如何在前端设置 FilePond 以支持多个文件上传。

如果用户将文件上传到此控制器,将返回类似以下内容的字符串。

tmp/1678198256-88eXsQV7XB2RU5zXdw0S/9A4eK5mRLAtayW78jhRo3Lc3WdSSrsihpVHhMvzr.png

然后,我们可以在我们的 web.php 文件中注册 /uploads/process 路由,如下所示。

use App\Http\Controllers\FileUploadController;
use Illuminate\Support\Facades\Route;
 
Route::post('uploads/process', [FileUploadController::class, 'process'])->name('uploads.process');

您的应用程序现在应该成功上传文件并将其存储在临时目录中。

在控制器中访问上传的文件

现在我们已经在前端设置了 FilePond,并添加了在后端临时存储文件的功能,现在我们可以来看看在提交表单时如何在控制器中访问上传的文件。

我们首先创建一个新的控制器,负责从 CSV 文件中导入产品。我们可以通过运行以下命令来实现。

php artisan make:controller ImportProductController -i

然后,我们可以更新新创建的 ImportProductController 以处理文件导入。

declare(strict_types=1);
 
namespace App\Http\Controllers;
 
use App\Services\ProductImportService;
use Illuminate\Http\File;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
 
final class ImportProductController extends Controller
{
public function __invoke(
Request $request,
ProductImportService $productImportService
): RedirectResponse {
$validated = $request->validate([
'csv' => 'required|string',
]);
 
// Copy the file from a temporary location to a permanent location.
$fileLocation = Storage::putFile(
path: 'imports',
file: new File(Storage::path($validated['csv']))
);
 
$productImportService->import(
csvLocation: $fileLocation
);
 
return redirect()
->route('products.index')
->with('success', 'Products imported successfully');
}
}

让我们来看看上面的控制器方法中做了什么。

首先,我们添加了 ProductImportService 类的类型提示,以便它将从服务容器中解析出来供我们在控制器方法中使用。这不是我们将在这篇文章中讨论的类,但我们可以假设它负责从 CSV 文件中导入产品。

我们还验证了请求是否包含 csv 字符串字段。我们将在本文后面介绍如何改进此验证。

接下来,我们将文件从其临时位置复制到永久位置,以便我们可以将其传递给 ProductImportService 对象。

完成所有这些操作后,我们将返回一个重定向响应到产品索引页面,并显示成功消息。

我们现在可以在我们的 web.php 文件中注册 ImportProductController 的路由,如下所示。

use App\Http\Controllers\ImportProductController;
 
Route::post('products/import', ImportProductController::class)->name('products.import');

上传图片

FilePond 提供了一个非常方便的 FilePondPluginImagePreview 插件,允许我们显示用户选择上传的图片的预览。我认为这是一个非常好的功能,看起来很棒。它还向用户提供了关于他们选择上传的文件的反馈,以便他们可以确认它是正确的文件。

要使用 FilePondPluginImagePreview 插件,我们可以通过运行以下命令通过 NPM 安装它。

npm i filepond-plugin-image-preview --save

安装完毕后,我们就可以将以下几行导入到我们的 app.js 文件中。

import FilePondPluginImagePreview from 'filepond-plugin-image-preview';
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css';

接下来,我们可以使用 registerPlugin 方法将插件注册到 FilePond。

FilePond.registerPlugin(FilePondPluginImagePreview);

添加这些行后,您的代码可能看起来像这样。

import * as FilePond from 'filepond';
import 'filepond/dist/filepond.min.css';
import FilePondPluginImagePreview from 'filepond-plugin-image-preview';
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css';
 
const inputElement = document.querySelector('input[type="file"].filepond');
 
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
 
FilePond.registerPlugin(FilePondPluginImagePreview);
 
FilePond.create(inputElement).setOptions({
server: {
process: './uploads/process',
headers: {
'X-CSRF-TOKEN': csrfToken,
}
},
allowMultiple: true,
});

就是这样!您现在应该有一个可以正常工作的 FilePond 组件,允许您上传图片并预览它们。

上传多个文件

可能会有时您希望一次在一次表单提交中上传多个文件。例如,您可能希望为单个产品上传多张图片。

为此,我们可以向输入元素添加 multiple 属性。

<input type="file" name="csv[]" class="filepond" multiple/>

然后,我们可以将 allowMultiple: true 传递给 setOptions 方法。

FilePond.create(inputElement).setOptions({
server: {
process: './uploads/process',
fetch: null,
revert: null,
headers: {
'X-CSRF-TOKEN': csrfToken,
}
},
allowMultiple: true,
});

就是这样!我们已经确保我们的 FileUploadController 可以处理多个文件,因此我们不需要对其进行任何更改。

如果用户尝试上传两个文件,将向服务器发出两个单独的请求以存储这些文件。然后,将向表单添加两个 csv[] 隐藏字段,其中包含上传文件的名称。

注意我们如何需要使用 csv[] 而不是 csv。这是因为如果我们使用 csv,我们每次提交表单时只能发送一个文件路径。通过使用 csv[],我们可以发送多个文件路径,然后在控制器中将其访问为字符串数组。

更进一步

现在我们已经了解了如何在 Laravel 应用程序中使用 FilePond 上传文件,让我们来看看您可能还想做的一些其他事情。

验证

Filepond 提供了一些助手,您可以使用它们向文件上传组件添加验证,例如 data-max-file-size。您可以将这些验证助手添加到您的输入元素,如下所示。

<input type="file" name="csv" class="filepond" data-max-file-size="3MB"/>

但是,请记住,客户端验证主要用于 UI/UX 目的,而不是安全性。您还应该始终在服务器端验证您的数据,以确保数据有效。

因此,在尝试处理文件之前,在提交表单后验证文件非常重要。

例如,假设我们提供了允许用户更新其个人资料图片的功能。您不希望此字段接受 CSV 文件。相反,我们希望确保文件是图片。

因此,让我们看看如何编写验证规则以确保上传的文件有效。我们将通过运行以下命令创建一个新的验证规则。

art make:rule ValidFileUpload

我们可以将我们的 ValidFileUpload 规则更新为如下所示。

declare(strict_types=1);
 
namespace App\Rules;
 
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\Storage;
 
final class ValidFileUpload implements ValidationRule
{
public function __construct(
private readonly array $validMimeTypes
) {
//
}
 
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (!Storage::exists($value)) {
$fail('The file does not exist.');
}
 
if (!in_array(Storage::mimeType($value), $this->validMimeTypes, true)) {
$fail('The file is not a valid mime type.');
}
}
}

ValidFileUpload 类中,我们定义了一个接受有效 MIME 类型数组的构造函数。

validate 方法中,我们添加了两个检查。

  1. 检查文件是否存在于存储中。
  2. 检查文件的 MIME 类型是否在有效 MIME 类型数组中。

然后,我们可以像这样使用此规则进行验证。

use App\Rules\ValidFileUpload;
 
$validated = $request->validate([
'csv' => ['required', 'string', new ValidFileUpload(['text/csv'])],
]);

您甚至可以进一步进行此验证,并添加额外的断言,例如检查文件大小是否不超过特定大小。

清理临时文件

随着时间的推移,您的 tmp 文件夹中会累积大量临时文件。因此,您可能希望编写一个 Artisan 命令,您可以将其安排为定期运行以删除 tmp 文件夹中比某个时间更旧的文件夹。

让我们看看如何做到这一点。我们将通过运行以下命令创建一个新的 DeleteTempUploadedFiles 命令。

art make:command DeleteTempUploadedFiles

然后,我们可以将我们的 DeleteTempUploadedFiles 命令更新为类似如下所示。

declare(strict_types=1);
 
namespace App\Console\Commands;
 
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
 
final class DeleteTempUploadedFiles extends Command
{
protected $signature = 'app:delete-temp-uploaded-files';
 
protected $description = 'Delete temporary uploaded files older than 24 hours.';
 
public function handle(): void
{
foreach (Storage::directories('tmp') as $directory) {
$directoryLastModified = Carbon::createFromTimestamp(Storage::lastModified($directory));
 
if (now()->diffInHours($directoryLastModified) > 24) {
Storage::deleteDirectory($directory);
}
}
}
}

在上面的命令中,我们遍历存储的 tmp 文件夹中的所有目录,并检查目录是否比 24 小时更旧。如果是,我们将删除该目录。

然后,我们可以通过将其添加到 app/Console/Kernel.php 类中的 schedule 方法中,将此命令安排为每小时运行一次。

namespace App\Console;
 
use App\Console\Commands\DeleteTempUploadedFiles;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
 
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
$schedule->command(DeleteTempUploadedFiles::class)->hourly();
}
 
// ...
}

假设您已运行应用程序的调度程序,这意味着每小时,您的应用程序将删除所有比 24 小时更旧的临时目录。这意味着您的 tmp 文件夹应该只包含可能最近使用过或当前正在使用的文件。

根据您的应用程序,您可能希望更改目录可以存在的时间长度或删除它们的频率。

测试您的代码

如果您之前阅读过我的任何文章,您就会知道我非常喜欢测试。为您的代码编写测试非常重要,尤其是在将代码用于生产环境时。这有助于您对代码的正确性充满信心,并使将来更容易进行更改。

让我们看看如何为我们的 `FileUploadController` 中的文件上传功能编写一些基本测试。从总体上讲,我们要测试:

  • 如果表单字段支持单个文件,则文件可以存储在 `tmp` 文件夹中。
  • 如果表单字段支持多个文件,则文件可以存储在 `tmp` 文件夹中。
  • 如果请求中没有传递文件,则会返回错误。
  • 如果请求中传递了多个文件,则会返回错误。

我们可以编写一些基本测试来涵盖这些场景,如下所示:

declare(strict_types=1);
 
namespace Tests\Feature\Controllers;
 
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Tests\TestCase;
 
final class FileUploadControllerTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
 
// Use a fake storage driver so we don't store files on the real disk.
Storage::fake();
 
// Freeze time and define how `Str::random` should work. This allows us
// to explicitly check that the file is stored in the correct location
// and is being named correctly.
$this->freezeTime();
Str::createRandomStringsUsing(static fn (): string => 'random-string');
}
 
/** @test */
public function file_can_be_temporarily_uploaded_for_a_single_file_field(): void
{
$file = UploadedFile::fake()->image('avatar.png');
 
$expectedFilePath = 'tmp/'.now()->timestamp.'-random-string';
 
$this->post(route('uploads.process'), [
'avatar' => $file,
])
->assertOk()
->assertSee($expectedFilePath);
 
Storage::assertExists($expectedFilePath);
}
 
/** @test */
public function file_can_be_temporarily_uploaded_for_a_multiple_file_field(): void
{
$file = UploadedFile::fake()->image('avatar.png');
 
$expectedFilePath = 'tmp/'.now()->timestamp.'-random-string';
 
$this->post(route('uploads.process'), [
'avatar' => [
$file
],
])
->assertOk()
->assertSee($expectedFilePath);
 
Storage::assertExists($expectedFilePath);
}
 
/** @test */
public function error_is_returned_if_no_file_is_passed_in_the_request(): void
{
$this->post(route('uploads.process'))
->assertStatus(422);
}
 
/** @test */
public function error_is_returned_if_more_than_one_file_is_passed_in_the_request(): void
{
$file = UploadedFile::fake()->image('avatar.png');
 
$this->post(route('uploads.process'), [
'avatar' => $file,
'invalid' => $file,
])
->assertStatus(422);
}
}

虽然这些测试相当基础,但它们应该为您编写自己的文件上传功能测试提供一个良好的起点。您可能希望扩展这些测试以检查是否返回了正确的错误消息。或者,如果您在文件上传流程中添加了身份验证和授权,您可能希望检查只有某些用户才能上传文件。

您可能还想为您的验证规则添加测试。这可以帮助您在将来更自信地添加更多断言,如果您决定使您的验证更严格。

结论

在本文中,我们介绍了如何使用 FilePond 在您的 Laravel 应用程序中异步上传文件。我们还介绍了如何删除临时文件、验证上传的文件以及编写测试以确保文件上传正常工作。

希望现在您应该对在自己的 Laravel 项目中实施这种相同的方法来为您的应用程序添加文件上传功能充满信心。

Ashley Allen photo

我是一名自由职业的 Laravel 网站开发人员,热衷于为开源项目做出贡献、构建激动人心的系统并帮助其他人学习 Web 开发。

归档于
Cube

Laravel 新闻稿

加入 40,000 多名其他开发人员,绝不错过新的提示、教程等。

Laravel Forge logo

Laravel Forge

轻松创建和管理您的服务器,并在几秒钟内部署您的 Laravel 应用程序。

Laravel Forge
Tinkerwell logo

Tinkerwell

Laravel 开发人员必备的代码运行器。使用 AI、自动完成和本地和生产环境的即时反馈进行调试。

Tinkerwell
No Compromises logo

无妥协

Joel 和 Aaron,来自 No Compromises 播客的两名经验丰富的开发者,现在可以为您的 Laravel 项目聘用。 ⬧ 固定费率为每月 7500 美元。 ⬧ 没有漫长的销售流程。 ⬧ 没有合同。 ⬧ 100% 返款保证。

无妥协
Kirschbaum logo

Kirschbaum

提供创新和稳定性,以确保您的 Web 应用程序成功。

Kirschbaum
Shift logo

Shift

运行旧版本的 Laravel?即时、自动化的 Laravel 升级和代码现代化,让您的应用程序保持新鲜。

Shift
Bacancy logo

Bacancy

以每月仅 2500 美元的价格,为您的项目配备一名经验丰富的 Laravel 开发人员,拥有 4-6 年的经验。获得 160 小时的专业知识和 15 天的无风险试用。立即安排通话!

Bacancy
Lucky Media logo

Lucky Media

立即获得 Lucky - Laravel 开发的理想选择,拥有十多年的经验!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel 电子商务

Laravel 的电子商务。一个开源软件包,将现代无头电子商务功能的强大功能带入 Laravel。

Lunar: Laravel 电子商务
LaraJobs logo

LaraJobs

官方 Laravel 职位发布平台

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS 启动套件

SaaSykit 是一个 Laravel SaaS 启动套件,包含运行现代 SaaS 所需的所有功能。支付、精美结账、管理面板、用户仪表板、身份验证、就绪组件、统计数据、博客、文档等等。

SaaSykit: Laravel SaaS 启动套件
Rector logo

Rector

您无缝 Laravel 升级、降低成本和加速创新的合作伙伴,为成功企业服务

Rector
MongoDB logo

MongoDB

使用 MongoDB 和 Laravel 的强大集成来增强您的 PHP 应用程序,使开发人员能够轻松高效地构建应用程序。支持事务性、搜索、分析和移动用例,同时使用熟悉的 Eloquent API。了解 MongoDB 的灵活、现代数据库如何改变您的 Laravel 应用程序。

MongoDB
Maska is a Simple Zero-dependency Input Mask Library image

Maska 是一个简单的无依赖输入掩码库

阅读文章
Add Swagger UI to Your Laravel Application image

将 Swagger UI 添加到您的 Laravel 应用程序

阅读文章
Assert the Exact JSON Structure of a Response in Laravel 11.19 image

在 Laravel 11.19 中断言响应的精确 JSON 结构

阅读文章
Build SSH Apps with PHP and Laravel Prompts image

使用 PHP 和 Laravel Prompts 构建 SSH 应用程序

阅读文章
Building fast, fuzzy site search with Laravel and Typesense image

使用 Laravel 和 Typesense 构建快速、模糊的网站搜索

阅读文章
Add Comments to your Laravel Application with the Commenter Package image

使用 Commenter 包在您的 Laravel 应用程序中添加评论

阅读文章