附件

附件是与特定记录相关的各种格式和扩展名的文件。通过在模型中添加 Attachable trait 并使用 attachments() 关系,可以将它们附加到应用程序中的任何模型上。

例如,要将文件附加到 Hostel 模型:

namespace App;

use Illuminate\Database\Eloquent\Model;
use Orchid\Attachment\Attachable;

class Hostel extends Model
{
    use Attachable;
    //
}

要检索特定宿舍的附件,可以使用 attachments() 关系:

$item = Hostel::find(42);
$item->attachments()->get();

通过 HTTP 上传文件

要通过 HTTP 上传文件,可以使用 File 类和 load() 方法。以下是一个处理文件上传的控制器方法示例:

use Orchid\Attachment\File;

public function upload(Request $request)
{
    $file = new File($request->file('photo'));
    $attachment = $file->load();

    return response()->json($attachment);
}

这将自动将文件上传到默认存储库(通常为 public)并在数据库中创建一个条目。

要检索附件的 URL,可以使用 url() 方法:

$image = $item->attachments()->first();

// 获取文件的 URL
$image->url();

注意。 url() 方法会首先检查路径是否存在,然后获取 URL。当使用像 s3 这样的外部存储时,这将进行两次调用。为了提高性能,可以使用 Laravel 推荐的缓存适配器来提高性能。您也可以简单地重写此方法并根据需要进行调整。

通过控制台上传文件

有时,必要的文件已经在服务器上,您可以使用以下代码将它们上传到所需的存储中:

use Illuminate\Http\UploadedFile;
use Orchid\Attachment\File;

$file = new UploadedFile($path, $originalName);

$attachment = (new File($file))->load();

重复上传文件

为了防止不必要的重复并节省资源,Laravel Orchid 使用哈希算法在上传新附件之前检查现有文件。如果存储中已经存在具有相同哈希的文件,则在数据库中创建指向现有文件的链接,而不是上传重复的副本。这允许高效的附件存储和管理。

文件只有在所有链接被销毁时才会从存储中删除。这意味着如果多个记录与同一附件相关联,文件将不会被删除,直到所有记录被删除或附件从所有记录中移除。

此功能不仅提高了性能并减少了存储空间,还消除了存储中存在多个相同文件副本的可能性。

允许重复文件

虽然 Laravel Orchid 中的哈希算法旨在防止文件重复,但在某些情况下,您可能希望保留重复文件并生成不同的链接以请求不同的物理文件。为此,可以使用 allowDuplicates() 方法。

use Orchid\Attachment\File;

public function upload(Request $request)
{
    $file = new File($request->file('photo'));
    $attachment = $file->allowDuplicates()->load();

    return response()->json();
}

请记住,允许重复文件可能会增加存储使用量并影响应用程序的性能。因此,应谨慎使用,并仅在必要时使用。

自定义上传路径

默认情况下,Laravel Orchid 使用 Y/m/d 的默认上传路径,例如:2022/06/11。此路径用于在存储中组织和结构化上传的文件。但是,您可能希望自定义路径以满足您的特定需求。

可以使用 path(string $path) 方法更改默认路径:

use Orchid\Attachment\File;

public function upload(Request $request)
{
    $path = "photos";
    $file = new File($request->file('photo'));
    $attachment = $file->path($path)->load();

    return response()->json();
}

在此示例中,path() 方法用于在加载文件之前将路径设置为 photos。这将更改默认路径,所有文件将上传到 photos 文件夹。

您还可以使用动态参数,如 path('photos/'.$user->id)path('photos/'.$file->name) 来为每个用户或文件类型创建特定文件夹。

移除

附件不会在模型移除后自动删除。如果您的附件不能在没有模型的情况下存在,则应在模型 deleting 事件中手动删除它们。如果从 attachments 表中删除一行,文件将不会被删除。要清除附件,需要在 Attachment 模型上使用 delete() 函数。在这种情况下,将进行额外的检查。如果没有文件的链接 – 它将被删除。可以使用关系观察者来实现。

让我们回到我们的示例,使用 “管理文件附件” 中的 hero 关系

// app/Post.php

use Orchid\Attachment\Models\Attachment;

public function hero()
{
    return $this->hasOne(Attachment::class, 'id', 'hero')
        ->withDefault();
}

如果像函数一样调用关系 $post->hero(),它将返回 Illuminate\Database\Eloquent\Builder 类,该类也有 delete() 函数。但它将执行 SQL 查询。如果像属性一样调用关系 $post->hero,它将返回模型类。Attachment 模型类。

$post->hero()->delete();

注意。 应该使用 withDefault() 函数构建关系以避免空指针异常。

让我们为示例模型生成观察者

php artisan make:observer PostObserver

在 PostObserver 中创建 deleting 函数

public function deleting(Post $post)
{
    $post->hero()->delete();
}

当有多个附件时,应使用 Attachable trait 中的 attachments 关系。

public function deleting(Post $post)
{
    // 加载附件为集合而不是查询 attachments()
    $post->attachments->each->delete();
}

注意。 经验丰富的 Laravel 开发人员会看到这里存在 N+1 问题。这是为了访问文件系统以删除文件而故意这样做的(数据库不会为我们这样做)。

AppServiceProvider 中订阅示例模型到观察者

public function boot()
{
    // ...
    
    Post::observe(PostObserver::class);
}

有时,用户可能会上传图像文件而没有正确建立与它们的关系。这些模型和文件可能会在系统中无限期地存在,占用宝贵的存储空间。为了解决这个问题,可以创建一个控制台命令,定期清除任何未关联的图像文件。例如,可以根据上传频率和可用存储空间的多少,安排命令每周或每月运行一次。

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Orchid\Attachment\Models\Attachment;

class AttachmentClear extends Command
{
    /**
     * 控制台命令的名称和签名。
     */
    protected $signature = 'attachment:clear';

    /**
     * 控制台命令的描述。
     */
    protected $description = '移除没有关系的附件';

    /**
     * 执行控制台命令。
     *
     * @return int
     */
    public function handle()
    {
        $unrelatedAttachments = Attachment::doesntHave('relationships')
            ->whereDate('created_at', '<', now()->subDays(2))
            ->get();

        $unrelatedAttachments->each->delete();

        return Command::SUCCESS;
    }
}

默认配置

使用 Laravel Orchid 上传文件时,包使用在 config/platform.php 文件中定义的默认配置。 此配置指定将用于处理文件的磁盘和生成器。

/*
|------------------------------------------------------------------
| 附件的默认配置。
|------------------------------------------------------------------
|
| 文件和存储使用的策略属性。
|
*/

'attachment' => [
    'disk'      => 'public',
    'generator' => \Orchid\Attachment\Engines\Generator::class,
],
  • disk – 用于存储文件的存储磁盘的名称。磁盘应在 /config/filesystems.php 文件中定义。

  • generator – 定义文件如何命名、在存储中的位置以及如何避免重复的生成器类。默认情况下,Orchid 使用 \Orchid\Attachment\Engines\Generator 类,该类使用文件内容的哈希来避免文件重复。

可以根据需要修改这些设置,例如,可以更改磁盘和生成器以自定义文件的处理和存储方式。

优化图像

优化图像可以是提高应用程序性能和用户体验的重要步骤。然而,重要的是要注意如何以及何时优化图像。修改原始图像可能会导致质量损失,尤其是在使用不同分辨率或质量设置多次修改图像时。

一种按需优化图像的方法是使用第三方包,如 https://github.com/Intervention/image 或其替代品。这种方法允许您仅在必要时优化图像,并保持原始图像的原始质量,这在图像需求随时间变化或您希望保留原始图像用于其他目的时很有用。

事件订阅

Orchid 允许您订阅文件上传时触发的事件。此功能使您能够执行其他任务,如视频压缩、图像优化或其他文件处理操作。

要订阅事件,可以使用 Laravel 的事件监听机制。在下面的示例中,注册了一个匿名函数来监听 UploadFileEvent

use Orchid\Platform\Events\UploadFileEvent;
use Illuminate\Support\Facades\Event;

/**
 * 启动任何应用程序服务。
 */
public function boot(): void
{
    Event::listen(function (UploadFileEvent $event) {
        // 在此处处理事件的逻辑
        // $event->attachment
        // $event->time
    });
}

UploadFileEvent 被触发时,使用 Event::listen 注册的匿名函数将被调用。在此函数中,可以定义处理事件的逻辑,例如处理上传的文件、执行验证或触发其他操作。

我们的朋友