cviebrock/eloquent-taggable

在 Laravel 5 中轻松为 Eloquent 模型添加标签。

资助包维护!
cviebrock

安装: 554 675

依赖: 4

建议者: 0

安全性: 0

星星: 536

关注者: 17

分支: 72

开放问题: 2

11.0.1 2024-06-06 14:45 UTC

README

轻松为 Laravel 中的 Eloquent 模型添加标签功能。

注意:以下说明适用于 Laravel 最新版本。
如果您使用的是旧版本,请安装与您的 Laravel 版本相对应的包版本。

Build Status Total Downloads Latest Stable Version Latest Unstable Version SensioLabsInsight License

安装

根据您的 Laravel 版本,您应该安装不同版本的包。 注意:从版本 6.0 开始,包的版本应与 Laravel 版本匹配。

† 包的 3.1 版本需要 PHP 7.0 或更高版本,即使 Laravel 5.4 不需要。

Laravel 的旧版本可以使用旧版本的包,尽管它们不再受支持或维护。有关详细信息,请参阅 CHANGELOG.mdUPGRADING.md,并确保您正在阅读正确版本的 README.md(GitHub 默认显示 master 分支的版本,这可能不是您想要的)。

  1. 使用 composer 安装 cviebrock/eloquent-taggable

    $ composer require cviebrock/eloquent-taggable

    该包将自动注册其服务提供者。

  2. 发布配置文件

    php artisan vendor:publish --provider="Cviebrock\EloquentTaggable\ServiceProvider" --tag "config"
  3. 发布迁移

    php artisan vendor:publish --provider="Cviebrock\EloquentTaggable\ServiceProvider" --tag "migrations"

如果您修改了迁移,请注意,您可以添加更多字段,但不应该删除任何现有字段。

此外,请注意,如果您想更改包使用的表名,应在配置文件(在 tables 键下)中执行此操作。

  1. 最后,使用 artisan 运行迁移以创建所需的表

    composer dump-autoload
    php artisan migrate

    (请注意,迁移文件未发布到您的应用程序,但仍会运行。)

更新您的 Eloquent 模型

您的模型应使用可标记特征

use Cviebrock\EloquentTaggable\Taggable;

class MyModel extends Eloquent
{
    use Taggable;
}

注意:请确保您的模型数据库表中没有名为 tags 的属性和/或列;特工会为您添加该属性。

就这样...您的模型现在可以“标记”了!

使用方法

向模型添加和删除标签

使用 tag() 方法标记模型

// Pass in a delimited string:
$model->tag('Apple,Banana,Cherry');

// Or an array:
$model->tag(['Apple', 'Banana', 'Cherry']);

tag() 方法是累加的,因此您可以再次标记模型,这些标签将被添加到之前的标签中

$model->tag('Apple,Banana,Cherry');

$model->tag('Durian');
// $model now has four tags

您可以使用 untag() 逐个删除标签,或使用 detag() 完全删除

$model->tag('Apple,Banana,Cherry');

$model->untag('Banana');
// $model is now just tagged with "Apple" and "Cherry"

$model->detag();
// $model has no tags

您还可以完全重新标记模型(这是删除然后标记的简写形式)

$model->tag('Apple,Banana,Cherry');

$model->retag('Etrog,Fig,Grape');

// $model is now just tagged with "Etrog", "Fig", and "Grape"

如果您已经有了一组标签模型的 ID(主键),则可以使用这些 ID 而不是标签名称来标记模型

// assuming no other tags exist yet ...
$model->tag('Apple','Banana','Cherry','Durian');

$newModel->tagById([1,3]);
// ... $newModel is tagged with "Apple" and "Cherry"

同样,您也可以通过ID取消标签

// assuming no other tags exist yet ...
$model->tag('Apple','Banana','Cherry','Durian');

$model->untagById([1,3]);
// ... $model is now only tagged with "Banana" and "Durian"

通过ID进行标签化/取消标签化/重新标签化非常有用,例如,如果您有一个带有多选下拉框或所有标签的复选框列表的表单,例如:

<select name="tags" multiple>
  <option value="1">Apple</option>
  <option value="2">Banana</option>
  <option value="3">Cherry</option>
  ... etc.
</select>

当表单提交时,发送到您的控制器的是所有选中标签ID的数组。然后,很容易根据选中的标签更新模型

$tags = $request->input('tags');
$model->retagById($tags);

与模型的标签一起工作

您可以获得所有标签的数组(技术上是一个Eloquent集合)

foreach($model->tags as $tag)
{
    echo $tag->name;
}

您还可以获取标签的扁平化数组或分隔列表

$model->tag('Apple,Banana,Cherry');

var_dump($model->tagList);

// string 'Apple,Banana,Cherry' (length=19)

var_dump($model->tagArray);

// array (size=3)
//  1 => string 'Apple' (length=5)
//  2 => string 'Banana' (length=6)
//  3 => string 'Cherry' (length=6)

标签名称已进行归一化(见下文),以确保不会意外创建重复的标签

$model->tag('Apple');
$model->tag('apple');
$model->tag('APPLE');

var_dump($model->tagList);

// string 'Apple' (length=5)

您还可以查看模型是否具有特定的标签

$model->tag('Apple,Banana,Cherry');

// tests use the normalized tag name

var_dump($model->hasTag('apple'));
// bool(true)

var_dump($model->hasTag('Durian'));
// bool(false)

查询作用域

为了参考,想象以下模型已被标记

您可以通过一些查询作用域轻松找到带有标签的模型

// Find models that are tagged with all the given tags
// i.e. everything tagged "Apple AND Banana".
// (returns models with Ids: 3, 4, 8)

Model::withAllTags('Apple,Banana')->get();

// Find models with any one of the given tags
// i.e. everything tagged "Apple OR Banana".
// (returns Ids: 2, 3, 4, 6, 7, 8)

Model::withAnyTags('Apple,Banana')->get();

// Find models that have any tags
// (returns Ids: 2, 3, 4, 5, 6, 7, 8)

Model::isTagged()->get();

相反

// Find models that are not tagged with all the given tags,
// i.e. everything not tagged "Apple AND Banana".
// (returns models with Ids: 2, 5, 6, 7)

Model::withoutAllTags('Apple,Banana')->get();

// To also include untagged models, pass another parameter:
// (returns models with Ids: 1, 2, 5, 6, 7)

Model::withoutAllTags('Apple,Banana', true)->get();

// Find models without any one of the given tags
// i.e. everything not tagged "Apple OR Banana".
// (returns Ids: 5)

Model::withoutAnyTags('Apple,Banana')->get();

// To also include untagged models, pass another parameter:
// (returns models with Ids: 1, 5)

Model::withoutAnyTags('Apple,Banana', true)->get();

// Find models that have no tags
// (returns Ids: 1)

Model::isNotTagged()->get();

一些边缘情况示例

// Passing an empty tag list to a scope either throws an
// exception or returns nothing, depending on the
// "throwEmptyExceptions" configuration option

Model::withAllTags('');
Model::withAnyTags('');

// Returns nothing, because the "Fig" tag doesn't exist
// so no model has that tag

Model::withAllTags('Apple,Fig');

组合作用域

// Find models with any one of the given tags
// i.e. everything tagged "Apple OR Banana"
// but without one of the given tags 
// i.e. everything NOT tagged "Cherry".
// (returns Ids: 2, 3, 6, 7, 8)

Model::withAnyTags('Apple,Banana')::withoutAnyTags('Cherry')->get();

// Find models that are not tagged with all the given tags,
// i.e. everything not tagged "Apple AND Banana".
// and models without any one of the given tags
// i.e. everything not tagged "Cherry OR Durian".
// (returns models with Ids: 2)

Model::withoutAllTags('Apple,Banana')::withoutAnyTags('Cherry,Durian')->get();

// Find models with any one of the given tags
// i.e. everything tagged "Apple OR Banana".
// AND tagged "Cherry OR Durian".
// (returns Ids: 4, 6, 7, 8)

Model::withAnyTags('Apple,Banana')::withAnyTags('Cherry,Durian')->get();

最后,您可以轻松找到模型所有实例中使用的所有标签

// Returns an array of tag names used by all Model instances
// e.g.: ['apple','banana','cherry','durian']

Model::allTags();

// Same as above, but as a delimited list
// e.g. 'apple,banana,cherry,durian'

Model::allTagsList();

// Returns a collection of all the Tag models used by any Model instances

Model::allTagModels();

事件

您可以为模型标记时创建一个监听器

// in your EventServiceProvider 

use Cviebrock\EloquentTaggable\Events\ModelTagged;
...
protected $listen = [
    ...
    ModelTagged::class => [
        ReactModelTagged::class  // your Listener class
    ]
    ...
];

监听器接收带有模型和标签的 Cviebrock\EloquentTaggable\Events\ModelTagged 事件

namespace App\Listeners;

use Cviebrock\EloquentTaggable\Events\ModelTagged;

class ReactModelTagged
{
    /**
     * Handle the event.
     *
     * @param  ModelTagged  $event
     * @return void
     */
    public function handle(ModelTagged $event)
    {
        dd($event->getModel(), $event->getTags());
    }
}

您还可以监听当标签被移除时触发的 Cviebrock\EloquentTaggable\Events\ModelUntagged 事件。

其他方法

您可以为模型重命名标签

Model::rename('Apple', 'Apricot');

这只会影响被标记为 "Apple" 的 Model 实例。如果另一个模型也被标记为 "Apple",这些标签不会被重命名。(要在所有模型中重命名标签,请参阅下面的示例,在 TagService 类 下。)

您还可以获取模型的热门标签列表(包括模型计数)

$tags = Model::popularTags($limit);
$tags = Model::popularTagsNormalized($limit);

// Will return an array like:
//
// [
//     'apple' => 5,
//     'banana' => 3,
//     'durian' => 3,
//     'cherry' => 2,
// ]

您还可以提供最小计数(即,仅返回使用过3次或更多次的标签)

$tags = Model::popularTags($limit, 3);

(同样,上述内容将查询限制为特定的模型。要获取所有模型的热门标签列表,请参阅下面的示例,在 TagService 类 下。)

标签模型

您可以在Tag模型本身上运行一些方法。

Tag::findByName('Apple') 将返回给定名称的Tag模型。然后可以将其链式调用以查找所有相关模型。

在底层,上述内容在Tag模型上使用了 byName() 查询作用域,您也可以自由使用以编写自定义查询。

TagService 类

您也可以直接使用 TagService 类,但是几乎所有功能都通过特性提供的各种方法公开,因此您可能不需要。

// Instantiate the service (can also be done via dependency injection)
$tagService = app(\Cviebrock\EloquentTaggable\Services\TagService::class);

// Return a collection of all the Tag models used by \App\Model instances
// (same as doing \App\Model::allTagModels() ):

$tagService->getAllTags(\App\Model);

// Return a collection of all the Tag models used by all models:

$tagService->getAllTags();

// Rename all tags from "Apple" to "Apricot" for the \App\Model uses
// (same as doing \App\Model::renameTag("Apple", "Apricot") ):

$tagService->renameTags("Apple", "Apricot", \App\Model);

// Rename all tags from "Apple" to "Apricot" across all models:

$tagService->renameTags("Apple", "Apricot");

// Get the most popular tags across all models, or for just one model:

$tagService->getPopularTags();
$tagService->getPopularTags($limit);
$tagService->getPopularTags($limit, \App\Model);
$tagService->getPopularTags($limit, \App\Model, $minimumCount);

// Find all the tags that aren't used by any model:

$tagService->getAllUnusedTags();

一如既往,查看代码以获取服务类的完整文档。

配置

配置通过 /app/config/taggable.php 中的设置来处理。默认值如下

return [
    'delimiters'           => ',;',
    'glue'                 => ',',
    'normalizer'           => 'mb_strtolower',
    'connection'           => null,
    'throwEmptyExceptions' => false,
    'taggedModels'         => [],
    'model'                => \Cviebrock\EloquentTaggable\Models\Tag::class,
    'tables' => [
        'taggable_tags'      => 'taggable_tags',
        'taggable_taggables' => 'taggable_taggables',
    ],
];

分隔符

这些是可以作为分隔符传递给 tag() 方法的单字符字符串。默认情况下,它是逗号,但您可以将它更改为其他字符,或使用多个字符。

例如,如果 delimiters 设置为 ";,/",则这将按预期工作

$model->tag('Apple/Banana;Cherry,Durian');

// $model will have four tags

粘合剂

在构建 tagList 属性的字符串时,这是用于连接标签的 "粘合剂"。在默认值的情况下

var_dump($model->tagList);

// string 'Apple,Banana,Cherry,Durian' (length=26)

规范化器

每个标签在存储到数据库之前都会进行 "归一化"。这是为了确保标签拼写或大小写的变化不会生成重复的标签。例如,我们不想在以下情况下有三个不同的标签

$model->tag('Apple');
$model->tag('APPLE');
$model->tag('apple');

规范化是通过将每个标签名称通过规范化函数来实现的。默认情况下,这是PHP的mb_strtolower()函数,但您可以将其更改为任何接受单个字符串值并返回字符串值的函数或可调用项。一些想法

    // default normalization
    'normalizer' => 'mb_strtolower',

    // same result, but using a closure
    'normalizer' => function($string) {
        return mb_strtolower($string);
    },

    // using a class method
    'normalizer' => ['Illuminate\Support\Str', 'slug'],

您可以通过$model->tagListNormalized$model->tagArrayNormalized访问标签的规范化值,它们与$model->tagList$model->tagArray(如上所述)工作方式相同,只是它们返回规范化值。

当然,您还可以直接从标签访问规范化名称。

echo $tag->normalized;

连接

您可以将此设置指定为Tag模型应使用不同的数据库连接。否则,它将使用默认连接(即来自config('database.default'))。

抛出空异常

将空字符串或数组传递给任何作用域方法是一个有趣的情况。逻辑上,您无法获取具有所有或任何列表标签的模型列表……如果列表为空!

默认情况下,throwEmptyExceptions设置为false。将空值传递给查询作用域将“短路”查询并返回没有模型。这使得您的应用程序代码更干净,因此您不需要在调用作用域之前检查空值。

但是,如果throwEmptyExceptions设置为true,则在这些情况下将空值传递给作用域将抛出Cviebrock\EloquentTaggable\Exceptions\NoTagsSpecifiedException异常。然后您可以在应用程序代码中捕获异常并按您喜欢的任何方式处理它。

标记模型

如果您想要能够找到具有相同标签的所有模型,您将需要在这里定义反向关系。数组键是您将使用的关系名称(例如posts),值是可标记模型的限定类名(例如\App\Post)。例如,使用以下配置

'taggedModels' => [
    'posts' => \App\Post::class
]

您将能够做

$posts = Tag::findByName('Apple')->posts;

这将返回一个包含所有标记为“Apple”的Post的集合。

模型

默认情况下,该包将使用自己的模型类来存储标签。如果您想使用自己的自定义标签模型,那么请使用您的类扩展包的类,并更新配置以引用您的模型。

默认情况下,该包将创建两个表来存储标签信息。如果您想使用不同的表名,则更改这两个值。模型、服务和迁移类都将读取配置值。

错误、建议、贡献和支持

感谢所有为这个项目做出贡献的人,特别感谢Michael Riediger帮助优化SQL。

请使用GitHub来报告错误,以及发表评论或建议。

有关如何贡献更改,请参阅CONTRIBUTING.md

版权和许可

eloquent-taggable是由Colin Viebrock编写的,并使用MIT许可证发布。

版权所有(c)2013 Colin Viebrock