actengage / media
Laravel的一个简单媒体管理包。
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.4
- intervention/image: ^2.7
- ksubileau/color-thief-php: ^2.0
- laravel/framework: ^11.0
- psr/http-message: ^1.0
Requires (Dev)
- mockery/mockery: ^1.5
- orchestra/testbench: ^9.0
- phpunit/phpunit: ^10.0
README
此包提供了一组单元测试的API,用于管理与Eloquent模型(多对多关系)相关的不同类型的文件。每种类型的文件都可以关联到自己的资源,以便进行额外的处理和操作。例如,图像使用Image
资源进行处理,并使用Intervention Image进行操作。API提供用于创建自己的资源。
特性
Media
Eloquent模型和迁移。Mediable
特性,可以将任何Eloquent模型关联到Media
模型。Resource
API,以支持更多类型的媒体。Plugin
API,添加可选功能,如图像的颜色提取。- 使用文件存储管理文件。
- 使用Intervention Image管理和操作图像。
- 单元测试
要求
- Laravel 9.x+
- PHP 8.x+
- Intervention Image 2.x+
- GD或Imagick
入门指南
通过Composer安装
composer require actengage/media
发布配置文件
php artisan vendor:publish --tag=config
可选,发布迁移文件
php artisan vendor:publish --tag=config
资源工厂
资源工厂提供了一个创建各种资源的统一接口。您甚至可以创建自己的资源类,并在config/media.php
中注册它们。默认情况下,有两种类型的资源:Image
和File
。例如,您可以定义Audio
或Video
资源。资源按照它们定义的顺序进行处理。
use Actengage\Media\Facades\Resource; use Illuminate\Http\UploadedFile; // Create a resource from a path. $resource = Resource::path('some/file/path/image.jpeg'); // Create a resource from request() instance. $resource = Resource::request('image'); // Create a resource from an \Illuminate\Http\UploadedFile instance. $resource = Resource::make( new UploadedFile('some/file/path/image.jpeg', 'image.jpeg') );
资源方法
每个资源共享一组标准方法。每种类型的资源都可以定义自己的方法。方法是可链接的。
use Actengage\Media\Facades\Resource; // Chain methods on the Resource facade to build the resource. // This will save the resource to the `public` disk in the `images` // directory. This resource is assumed to be an image and will be // filtered using `greyscale()`. $resource = Resource::request('image') ->disk('public') ->directory('images') ->filename('renamed.jpeg') ->title('Hello World!') ->caption('This is my first resource.') ->greyscale(); // Save the resource to the disk and return the `Media` model. $media = $resource->save();
条件资源方法
有时您可能不知道您正在创建什么类型的资源。您可以使用is()
方法有条件地将方法链接到特定的资源类型。使用config/media.php
中的键配置进行匹配或使用完全限定类名。
use Actengage\Media\Facades\Resource; use Actengage\Media\Resources\Image; $resource = Resource::request('image') // When the resource is an image, make it greyscale. ->is('image', function($resource) { $resource->greyscale(); }) // You may also use the literal class as a match... ->is(Image::class, function($resource) { $resource->greyscale(); }) // When the resource is a file, do something else... ->is('file', function($resource) { // Do something here... });
在资源上执行代码之前,您可能需要检查true
和false
值。对于这些场景,您可以使用when()
和not()
方法有条件地将方法链接到资源。
use Actengage\Media\Facades\Resource; use Actengage\Media\Resources\Image; $resource = Resource::request('image') // When the value is `true` execute the callback. ->when(true, function($resource) { // This will only be called when `true` is passed to the first argument. }) // You may also use a callback to check for a `true` value. ->when(function($resource) { return true; }, function($resource) { // This will only be called when `true` is returned from the first callback. }) // When the value is `false` execute the callback. ->not(false, function($resource) { // This will only be called when `false` is passed to the first argument. }) // You may also use a callback to check for a `false` value. ->not(function($resource) { return false; }, function($resource) { // This will only be called when `false` is returned from the first callback. }); ## Resource Context, Meta, Tags Contextual data can be used to search and filter `Media` models. ```php use Actengage\Media\Facades\Resource; $resource = Resource::request('image'); // Context allows you to give a simple string to assign from context to // resources. For example, if you want to notate a resource is an image. $resource->context('image'); // Meta data is a key/value store that is saved as JSON in the database. // Similar to context, but this allows you to associate custom meta data // with a resource instance. $resource->meta([ 'some_key' => 'Some value goes here.' ]); // Meta can be also added using individual arguments. $resource->meta('another_key', 'Another key goes here.'); // Tags add an array of keys as context to a resource. $resource->tags(['a', 'b', 'c']); // Tags can be added using individual arguments or an array. $resource->tags('d', 'e', 'f');
事件
与Eloquent事件类似,Resource
事件处理器以相同的方式工作。有两种绑定事件的方式,一种是全局绑定到Resource
类,另一种是在资源实例上绑定。区别在于全局事件绑定适用于所有资源,而实例方法仅针对该实例。
全局方法
use Actengage\Resources\Image; Image::creating(function($resource, $model) { // This method is fired for the every Image resource before it // has been saved, similar to the `creating` Eloquent event. }); Image::created(function($resource, $model) { // This method is fired for the every Image resource after it // has been saved, similar to the `creating` Eloquent event. });
实例方法
$resource = Resource::request('image') ->creating(function($resource, $model) { // This method is fired for the resource instance before it // has been saved, similar to the `creating` Eloquent event. }) ->created(function($resource, $model) { // This method is fired for the resource instance after it // has been saved, similar to the `created` Eloquent event. });
查询作用域
Media
模型提供了一些方便的作用域用于搜索。
use Actengage\Media\Media; // Search by one or more captions Media::caption('Some Caption'); Media::caption('Some Caption', 'Another Caption'); Media::caption(['Some Caption', 'Another Caption']); // Search by one or more contexts Media::context('Some Context'); Media::context('Some Context', 'Another Context'); Media::context(['Some Context', 'Another Context']); // Search by one or more disks Media::disk('public'); Media::disk('public', 's3'); Media::disk(['public', 's3']); // Search by one or more extensions Media::extension('jpeg'); Media::extension('jpeg', 'jpg'); Media::extension(['jpeg', 'jpg']); // Search by one or more filenames Media::filename('a.jpeg'); Media::filename('a.jpeg', 'b.jpeg'); Media::filename(['a.jpeg', 'b.jpeg']); // Search by one or more filesizes Media::filesize(2500); Media::filesize(2500, 3500); Media::filesize([2500, 3500]); // Search by meta key/values Media::meta([ 'a' => 1, 'b' => 2, 'c' => 3 ]); // Search by one or more mime types Media::mime('text/plain'); Media::mime('text/plain', 'text/html'); Media::mime(['text/plain', 'text/html']); // Search by one or more tags Media::tag('a'); Media::tag('a', 'b'); Media::tag(['a', 'b', 'c']); // Alias to tag() is tags() Media::tags('a', 'b'); // Search by one or more titles Media::title('Some Title'); Media::title('Some Title', 'Another Title'); Media::title(['Some Title', 'Another Title']); // Search records without one or more tags Media::withoutTag('a'); Media::withoutTag('a', 'b'); Media::withoutTag(['a', 'b', 'c']); // Alias to withoutTag() is withoutTags() Media::withoutTags('a', 'b');
Mediable特性
使用Mediable
特性将Media
模型关联到您的自定义模型。Media
模型使用morphToMany
关系相关联。
Document.php
<?php namespace App; use Actengage\Media\Mediable; use Illuminate\Database\Eloquent\Model; class Document extends Model { use Mediable; }
基本用法
// Create a new `Media` model using the `request()` instance. $media = Resource::request('image') ->disk('public') ->save(); // Create a new Document $document = new Document(); $document->content = 'Hello World!'; $document->save(); // Sync the model to the document $document->media()->sync($media); // Many to Many usage dd($document->media); // One to Many usage. This method will give the last `Media` model // associated with the document model. This method is for convenience. dd($document->medium);
插件
插件用于向核心API未提供的资源添加额外功能。这是一个示例插件ExtractImageColors
,它从图像中提取常见颜色并将它们存储在模型中。插件具有适用于特定资源的实例方法或作为事件处理器。
插件类可以使用两种方式之一。第一种方式是将它静态地绑定到它应该使用的资源上。第二种方式是在 config/media.php
文件中定义它。在配置文件中,插件可以绑定到特定资源或全局应用于所有资源。
静态绑定
use Actengage\Media\Resources\Image; // These plugins will only fire on Image resources. Image::plugins([ // This is a plugin without any options defined. HashFilename::class, // This is a plugin with options defined. [ExtractImageColors::class, [ 'colorCount' => 3, 'quality' => 10 ]], ]);
config/media.php
<?php use Actengage\Media\Media; use Actengage\Media\Plugins\ExtractImageColors; use Actengage\Media\Plugins\HashDirectory; use Actengage\Media\Plugins\HashFilename; use Actengage\Media\Resources\File; use Actengage\Media\Resources\Image; return [ // Resources are defined in key/value pairs. The key is the common name // and the value is the class. Plugins are matched to their common name. 'resources' => [ 'image' => Image::class, 'file' => File::class ], 'plugins' => [ // This plugins will apply to all resources and has no options. HashFilename::class, // These plugins will only apply to image resources 'image' => [ // This plugin has some options [ExtractImageColors::class, [ 'colorCount' => 3, 'quality' => 10 ]], ], // These plugins will only apply to file resources 'file' => [ HashDirectory::class ] ] ];
Plugins/ExtractImageColors.php
<?php namespace Actengage\Media\Plugins; use Actengage\Media\Media; use Actengage\Media\Resources\Image; use ColorThief\ColorThief; use Illuminate\Support\Collection; class ExtractImageColors extends Plugin { /** * Boot the plugin. * * @param Collection $options * @return void */ public static function boot(Collection $options): void { /** * Get the color palette of the image. * * @param integer $colorCount * @param integer $quality * @param array|null $area * @param string $outputFormat * @param \ColorThief\Image\Adapter\AdapterInterface|string|null $adapter * @return \Illuminate\Support\Collection */ Image::macro('palette', function( int $colorCount = 10, int $quality = 10, ?array $area = null, string $outputFormat = 'obj', $adapter = null ): Collection { return collect(ColorThief::getPalette( $this->image->getCore(), $colorCount, $quality, $area, $outputFormat, $adapter )); }); Image::creating(function(Image $resource, Media $model) use ($options) { $model->colors = $resource->palette( (int) $options->get('colorCount', 10), (int) $options->get('quality', 10) ); }); } }