farhanshares/laravel-mediaman

MediaMan - 为Laravel提供最优雅且功能强大的媒体管理包!

v1.0.0-rc1 2024-04-15 16:07 UTC

README

Github CI Total Downloads Latest Stable Version License

Laravel MediaMan

MediaMan是一个优雅且功能强大的媒体管理包,适用于Laravel应用程序,支持无痛苦的上传器、虚拟集合和自动转换,并提供与您的应用程序模型特定广播频道的按需关联

MediaMan不依赖于UI,提供流畅的API来管理应用程序的媒体,这意味着您对媒体拥有完全控制权,包括外观、感觉和无忧的开发体验。它是您的应用程序或API服务器的完美选择。

着急?这里有一个快速示例

$media = MediaUploader::source($request->file->('file'))
            ->useCollection('Posts')
            ->upload();

$post = Post::find(1);
$post->attachMedia($media, 'featured-image-channel');

为什么选择MediaMan?它有什么独特之处?

虽然许多Laravel应用程序都在处理媒体和文件管理,但我经常在Spatie的Laravel MediaLibrary中找到慰藉。然而,尽管它具有功能,但还有一些方面它没有涉及,这是我渴望的功能。

进入MediaMan:时尚、轻量级且功能丰富。无论您需要简单地将媒体附加或解除附加,还是需要一个强大的媒体管理器后端,MediaMan都能满足您的需求。而且请放心,它的演变将遵循现代应用程序不断变化的需求,无论是单体、API驱动、增强Livewire/InertiaJS集成,还是建立在无服务器架构之上。

概述与关键概念

在继续之前,需要了解一些关键概念

  • 媒体:它可以是指任何类型的文件。在尝试上传文件之前,您应该在应用程序的验证逻辑中指定文件限制。

  • 媒体上传器:媒体项以自己的实体形式上传。在创建时,它不属于系统中的任何其他模型,因此项可以独立管理(这使得它是媒体管理器的完美引擎)。MediaMan提供了“媒体上传器”,用于在数据库中创建记录并存储在文件系统中。

  • 媒体集合:它也可以指一组文件。媒体项可以捆绑到任何“集合”。媒体和集合将形成多对多关系。您可以创建集合/虚拟目录/媒体组,稍后检索一组以检查其内容或执行操作。

  • 关联:媒体项需要附加到模型才能进行关联。MediaMan提供了帮助程序以轻松完成工作。多对多多态关系允许任意数量的媒体与任意数量的其他模型相关联,而无需修改现有的数据库模式。

  • 频道:它也可以指文件的标签。在关联过程中,媒体项绑定到模型的“频道”。因此,您可以轻松地将多种类型的媒体关联到模型。例如,“用户”模型可能有一个“头像”和“文档”媒体频道。如果您的头脑中混乱,只需将“频道”视为特定模型的“标签”即可。频道也用于执行转换。

  • 转换:您可以使用转换来操作图像,转换将在媒体项关联到模型时执行。例如,您可以为模型的“画廊”频道注册“缩略图”转换,以便在图像附加到该频道时运行。

目录

需求

  • PHP v7.3 | v7.4 | v8.0 | v8.1 | v8.2
  • Laravel v7 | v8 | v9 | v10 | v11
  • Composer v1 | v2

安装

您可以通过Composer安装此软件包

composer require farhanshares/laravel-mediaman

除非您已禁用自动发现模式,否则Laravel会自动发现此软件包。在这种情况下,请将服务提供程序添加到您的config/app.php

FarhanShares\MediaMan\MediaManServiceProvider::class

安装完成后,您应发布提供的资产以创建必要的迁移和配置文件。

php artisan mediaman:publish-config
php artisan mediaman:publish-migration

确保存储已连接。

php artisan storage:link

运行迁移,您就设置完成了。

php artisan migrate

配置

MediaMan开箱即用。如果您想调整它,MediaMan附带一个config/mediaman.php配置文件。调整的一个常见需求可能是将媒体存储在专用存储中。

MediaMan支持Laravel支持的所有存储驱动程序(例如:本地、S3、SFTP、FTP、Dropbox等)。

以下是一个示例配置,用于为MediaMan使用专用本地媒体磁盘。

// file: config/filesystems.php
// define a new disk
'disks' => [
    ...
    'media' =>
        'driver' => 'local',
        'root' => storage_path('app/media'),
        'url' => env('APP_URL') . '/media',
        'visibility' => 'public',
    ],
]

// define the symbolic link
'links' => [
    ...
    public_path('media') => storage_path('app/media'),
],


// file: config/mediaman.php
// update the disk config to use our recently created media disk
'disk' => 'media'

现在,运行php artisan storage:link来创建我们新创建的媒体磁盘的符号链接。

媒体

上传媒体

您应使用FarhanShares\MediaMan\MediaUploader类来处理文件上传。您可以一次性上传文件、在数据库中创建记录并将文件存储在文件系统中。

$file  = $request->file('file')
$media = MediaUploader::source($file)->upload();

文件将存储在默认磁盘和默认集合中,该集合在mediaman配置中指定。文件大小将存储在数据库中,文件名将自动清理。

但是,您可以做更多的事情,而不仅仅是坚持默认设置。

$file  = $request->file('file')
$media = MediaUploader::source($file)
            ->useName('Custom name')
            ->useFileName('custom-name.png')
            ->useCollection('Images')
            ->useDisk('media')
            ->useData([
                'additional_data' => 'will be stored as json',
                'use_associative_array' => 'to store any data you want to be with the file',
            ])
            ->upload();

如果集合不存在,它将自动创建。您可以在下面了解更多关于集合的信息。

问题:如果我在上述过程中不提供唯一的文件名,会发生什么?

答案:不要担心,MediaMan以智能和安全的方 式管理上传。文件以冲突几乎不会发生的方式存储在磁盘上。在存储磁盘时,MediaMan将在磁盘上创建一个格式为mediaId-hash的目录并将文件放在其中。与文件相关的任何内容都将拥有自己的小屋。

问题:但是为什么?我会得到一堆目录吗?

答案:是的,你会的。如果您想,可以扩展FarhanShares\MediaMan\Models\Media模型,并按您喜欢的任何方式自定义。最后,将您的自定义模型指向mediaman配置。但我们建议坚持默认设置,这样您就不必担心文件冲突。添加了hash以及mediaId,因此用户无法猜测并检索随机文件。稍后将添加更多有关自定义的信息。

提醒:MediaMan将任何文件(Illuminate\Http\UploadedFile的实例)视为媒体源。如果您想上传特定类型的文件,可以使用Laravel的验证器。

检索媒体

您可以使用任何Eloquent操作来检索媒体,我们还添加了findByName()。

// by id
$media = Media::find(1);

// by name
$media = Media::findByName('media-name');

// with collections
$media = Media::with('collections')->find(1);

Media实例具有以下属性

'id' => int
'name' => string
'file_name' => string
'extension' => string
'type' => string
'mime_type' => string
'size' =>  int // in bytes
'friendly_size' => string // in human readable format
'media_uri' => string  // media URI for the original file. Usage in Blade: {{ asset($media->media_uri) }}.
'media_url' => string  // direct URL for the original file.
'disk' =>  string
'data' => array // casts as array
'created_at' =>  string
'updated_at' => string
'collections' => object // eloquent collection

您可以使用一些方法以及属性

// $media->mime_type => 'image/jpg'
$media->isOfType('image') // true

// get the media url, accepts optional '$conversionName' argument
$media->getUrl('conversion-name')

// get the path to the file on disk, accepts optional '$conversionName' argument
$media->getPath('conversion-name')

// get the directory where the media stored on disk
$media->getDirectory()

更新媒体

使用Media实例,您可以执行各种更新操作

$media = Media::first();
$media->name = 'New name';
$media->data = ['additional_data' => 'additional data']
$media->save()

更新媒体名称

$media = Media::first();
$media->name = 'New Display Name';
$media->save();

更新附加数据

$media->data = ['additional_data' => 'new additional data'];
$media->save();

删除所有附加数据

$media->data = [];
$media->save();

更新媒体文件名

更新媒体文件名也将重命名存储中的实际文件。

$media->file_name = 'new_filename.jpg';
$media->save();

更改媒体存储磁盘

将媒体移动到另一个存储磁盘将实际文件转移到指定的磁盘。

$media->disk = 's3';  // Example disk name, ensure it exists
$media->save();

注意!有关磁盘读写操作访问性检查的配置: check_disk_accessibility

磁盘访问性检查:

  • 优点:早期识别潜在磁盘问题。
  • 缺点:可能会引入性能延迟。

提示:启用此检查可以预先发现存储问题,但可能会增加轻微的操作延迟。请根据您的系统需求相应决定。

删除媒体

您可以通过对Media实例调用delete()方法来删除媒体。

$media = Media::first();
$media->delete()

或者这样删除媒体

Media::destroy(1);
Media::destroy([1, 2, 3]);

注意:当删除Media实例时,文件将从文件系统中删除,与您的App Models和MediaCollection的所有关联也将被删除。

注意!您不应使用查询删除媒体,例如 Media::where('name', 'the-file')->delete(),这不会触发删除事件,文件也不会从文件系统中删除。有关详细信息,请参阅官方文档

媒体 & 模型

关联媒体

MediaMan通过FarhanShares\MediaMan\HasMedia特质提供易于使用的API,用于将媒体项关联到模型。在您的App Model中使用此特质,然后您就可以开始了。

use Illuminate\Database\Eloquent\Model;
use FarhanShares\MediaMan\Traits\HasMedia;

class Post extends Model
{
    use HasMedia;
}

这将建立您的App Model和Media Model之间的关系。

完成设置后,您可以将媒体关联到模型,如下所示。

attachMedia()方法的第一个参数可以是媒体模型/ID或模型/ID的可迭代集合。

$post = Post::first();

// Associate in the default channel
$post->attachMedia($media); // or 1 or [1, 2, 3] or collection of media models

// Associate in a custom channel
$post->attachMedia($media, 'featured-image');

attachMedia()在成功时返回关联的媒体数量(整数),在失败时返回null。

检索模型的媒体

除了这些,HasMedia特质还使您的App Models可以方便地检索媒体。

// All media from the default channel
$post->getMedia();

// All media from the specified channel
$post->getMedia('featured-image');

尽管原始媒体URL已附加到Media模型,但您应该知道有一个getUrl()方法可用。

$media =  $post->getMedia('featured-image');
// getUrl() accepts only one optional argument: name of the conversion
// leave it empty to get the original media URL
$mediaOneUrl = $media[0]->getUrl();

对于大多数Laravel应用程序,使用第一个媒体项的频率较高,因此MediaMan提供了专门的方法来检索所有关联媒体中的第一个项目。

// First media item from the default channel
$post->getFirstMedia();

// First media item from the specified channel
$post->getFirstMedia('featured-image');

// URL of the first media item from the default channel
$post->getFirstMediaUrl();

// URL of the first media item from the specified channel
$post->getFirstMediaUrl('featured-image');

提示:getFirstMediaUrl()接受两个可选参数:频道名称和转换名称

解除媒体关联

您可以使用HasMedia特质中提供的detachMedia()方法来从模型中解除媒体关联。

// Detach the specified media
$post->detachMedia($media); // or 1 or [1, 2, 3] or collection of media models

// Detach all media from all channels
$post->detachMedia();

// Detach all media of the default channel
$post->clearMediaChannel();

// Detach all media of the specific channel
$post->clearMediaChannel('channel-name');

detachMedia()在成功时返回解除关联的媒体数量(整数),在失败时返回null。

同步关联/解除关联

您可以使用syncMedia()方法同步指定频道的媒体。这提供了维护模型和关联媒体记录之间关联的灵活方式。默认的方法签名如下:syncMedia($media, string $channel = 'default', array $conversions = [], $detaching = true)

如果$detaching为真,这将删除提供的列表中不存在的媒体,并添加尚未关联的媒体。

$post = Post::first();
$media = Media::find(1); // model instance or just an media id: 1, or array of id: [1, 2, 3] or a collection of media models

// Sync media in the default channel (the $post will have only $media and others will be removed)
$post->syncMedia($media);

注意!attachMedia、detachMedia或syncMedia方法都不会删除文件,它只是按照其含义执行。有关如何删除媒体,请参阅“删除媒体”部分。

集合

MediaMan提供集合以更好地管理媒体。使用FarhanShares\MediaMan\Models\MediaCollection处理媒体集合。

创建集合

当上传文件时,如果不存在,则会自动创建集合。

$media = MediaUploader::source($request->file('file'))
            ->useCollection('My Collection')
            ->upload();

如果您希望在不上传文件的情况下创建集合,您也可以这样做,毕竟它是一个Eloquent模型。

MediaCollection::create(['name' => 'My Collection']);

检索集合

您可以通过ID或名称检索集合。

MediaCollection::find(1);
MediaCollection::findByName('My Collection');

// Retrieve the bound media as well
MediaCollection::with('media')->find(1);
MediaCollection::with('media')->findByName('My Collection');

更新集合

您可以更新集合名称。实际上没有其他可以更新的内容。

$collection = MediaCollection::findByName('My Collection');
$collection->name = 'Our Collection'
$collection->save();

删除集合

您可以使用MediaCollection实例删除集合。

$collection = MediaCollection::find(1);
$collection->delete()

这将不会从磁盘上删除媒体,但会从数据库中删除绑定。

注意!deleteWithMedia()是一个尚未实现的概念性方法,如果需要此功能,请创建一个功能请求。PR非常受欢迎。

媒体 & 集合

MediaMediaCollection 之间的关系已经配置好了。您可以轻松地进行绑定、解绑以及同步绑定和解绑。对于 Media::**Collections()MediaCollection::**Media() 的方法签名是相似的。

绑定媒体

$collection = MediaCollection::first();
// You can just pass a media model / id / name or an iterable collection of those
// e.g. 1 or [1, 2] or $media or [$mediaOne, $mediaTwo] or 'media-name' or ['media-name', 'another-media-name']
$collection->attachMedia($media);

attachMedia() 在成功时返回已绑定的媒体数量(int),在失败时返回 null。或者,您可以使用 Media::attachCollections() 从媒体模型实例绑定到集合。

注意!HasMedia 特性不同,您不能在媒体集合上拥有频道。

解绑媒体

$collection = MediaCollection::first();
// You can just pass a media model / id / name or an iterable collection of those
// e.g. 1 or [1, 2] or $media or [$mediaOne, $mediaTwo] or 'media-name' or ['media-name', 'another-media-name']
$collection->detachMedia($media);

// Detach all media by passing null / bool / empty-string / empty-array
$collection->detachMedia([]);

detachMedia() 在成功时返回已解绑的媒体数量(int),在失败时返回 null。或者,您可以使用 Media::detachCollections() 从媒体模型实例解绑集合。

同步绑定与解绑

$collection = MediaCollection::first();
// You can just pass media model / id / name
$collection->syncMedia($media);

// You can even pass iterable list / collection
$collection->syncMedia(Media::all())
$collection->syncMedia([1, 2, 3, 4, 5]);
$collection->syncMedia([$mediaSix, $mediaSeven]);
$collection->syncMedia(['media-name', 'another-media-name']);

// Synchronize to having zero media by passing null / bool / empty-string / empty-array
$collection->syncMedia([]);

syncMedia() 总是返回一个包含同步状态的数组。或者,您可以使用 Media::syncCollections() 与媒体模型实例的集合同步。

转换

当媒体绑定到频道时,您可以指定一个模型来执行“转换”。

MediaMan 提供了一个流畅的 API 来操作图像。它底层使用了流行的 intervention/image 库。缩放、添加水印、转换为不同的格式或其他支持的任何操作都可以完成。简而言之,您可以利用库中的所有功能。

转换是全局注册的。这意味着它们可以在您的应用程序中重用,例如,一个文章和一个用户都可以有相同大小的缩略图,而无需两次注册相同的转换。

要开始使用,您首先应该在应用程序的一个服务提供者中注册一个转换。

use Intervention\Image\Image;
use FarhanShares\MediaMan\Facades\Conversion;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Conversion::register('thumb', function (Image $image) {
            // you have access to intervention/image library,
            // perform your desired conversions here
            return $image->fit(64, 64);
        });
    }
}

注册转换后,您应该配置一个媒体频道,以便在媒体绑定到您的模型时执行转换。

class Post extends Model
{
    use HasMedia;

    public function registerMediaChannels()
    {
        $this->addMediaChannel('gallery')
             ->performConversions('thumb');
    }
}

从现在起,每当媒体项绑定到“gallery”频道时,都会生成一个转换后的图像。您可以像下面这样获取转换后图像的 URL

// getFirstMediaUrl() accepts two optional arguments: channel name & conversion name
// you should provide channel name & conversion name to get the url
$post->getFirstMediaUrl('gallery', 'thumb');

提示: 默认频道名称为 default

// if you have multiple media associated & need to retrieve URLs you can do it with getUrl():
$media = $post->getMedia();
// getUrl() accepts only one optional argument: name of the conversion
// you should provide the conversion name to get the url
$mediaOneThumb = $media[0]->getUrl('thumb');

提示: media_urimedia_url 总是附加一个 Media 实例,这些反映了原始文件(而不是转换)。

升级到MediaMan v1.x指南

如果您是从 MediaMan 的早期版本升级,请放心,过渡相当直接。以下是您需要了解的内容

变更

  • 引入 media_uri:在本版本中,我们引入了一个新的属性 media_uri。这提供了原始文件的 URI。当您想要生成用于 Blade 的完整 URL 时,您可以使用它如下

    {{ asset($media->media_uri) }}
  • 修改 media_url:在之前的版本中,media_url 的行为类似于现在的 media_uri。从 v1.0.0 开始,media_url 将直接提供原始文件的绝对 URL。

升级步骤

  1. 更新包:运行 composer update 命令以获取最新版本。

    composer update farhanshares/laravel-mediaman
  2. 审查您的 Blade 文件:如果您之前使用 media_urlasset() 辅助函数一起使用,如下所示

    {{ asset($media->media_url) }}

    将其更新为

    {{ asset($media->media_uri) }}

    如果您在没有 asset() 的情况下使用 media_url,则不需要任何更改。

  3. 运行任何新的迁移(如果适用):始终检查新的迁移并运行它们以确保您的数据库模式是最新的。

  4. 测试您的应用程序:如往常一样,在升级后,确保您彻底测试应用程序,特别是使用媒体文件的部分,以确保一切按预期工作。

感谢您使用 MediaMan,并希望您喜欢 v1.0.0 版本的改进!如果您遇到任何问题,请随时在 GitHub 上提交工单。

贡献和许可

如果您遇到任何错误,请考虑在 GitHub 上提交问题。欢迎提出功能请求和 PR。

麻省理工学院许可证(MIT)。请阅读许可文件以获取更多信息。