actengage/media

Laravel的一个简单媒体管理包。

v1.0.1 2024-07-11 18:02 UTC

This package is auto-updated.

Last update: 2024-09-11 18:24:34 UTC


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中注册它们。默认情况下,有两种类型的资源:ImageFile。例如,您可以定义AudioVideo资源。资源按照它们定义的顺序进行处理。

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...
    });

在资源上执行代码之前,您可能需要检查truefalse值。对于这些场景,您可以使用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)
            );
        });
    }
}