uwla/ltags

Laravel标签系统

v1.5.4 2023-07-09 17:26 UTC

This package is not auto-updated.

Last update: 2024-09-30 00:12:47 UTC


README

Laravel的标签系统。

特性

  • 任意模型:标签可以附加到任何Eloquent模型。
  • 嵌套标签:一个标签也可以被标记,允许层次结构。
  • 上下文感知:可以为不同的上下文创建具有相同标签名称的多个标签。
  • 非侵入式:可以在不修改其类或数据库表的情况下对资源进行标记。
  • 便捷的API:方便的API用于向模型添加/设置/获取/删除标签,以及通过标签获取模型。

安装

使用composer安装

composer require uwla/ltags

发布ACL迁移表

php artisan vendor:publish --provider="Uwla\Ltags\TagServiceProvider"

运行迁移

php artisan migrate

使用方法

本节解释了如何使用此包。还有在uwla/ltags-demo上的一个演示应用程序,以说明用例。

标签

创建标签

<?php
use Uwla\Ltags\Models\Tag;

// create single tag
$tag = Tag::createOne('foo');           // shorter way
$tag = Tag::create(['name' => 'foo']);  // default way to create Eloquent models

// create multiple tags tag
$tags = Tag::createMany(['foo', 'bar', 'zoo']); // Eloquent is way more verbose

获取标签

<?php
$tag = Tag::findByName('foo');  // get single tag
$tags = Tag::findByName(['foo', 'bar', 'zoo']); // get many tags

按名称删除标签或标签集

<?php
// delete a tag by name
Tag::delByName('foo');                  // delete single tag
Tag::delByName(['foo', 'bar', 'zoo']);  // delete multiple tags

// The method above only works for string and string arrays.
// If you have a Eloquent model or collection and want to delete it or them,
// do the following:
$model->delete();
$models->delete();

要更新标签或标签集,请使用Laravel文档中提供的Eloquent的update方法。此包提供通过名称创建、获取和删除标签的替代方法,以使其更方便和简洁,但在更新标签时,Laravel的界面简单明了,我们无法使其更简洁。

被标记的模型

任何模型都可以被标记。所需做的只是添加Taggable特质。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Uwla\Ltags\Trait\Taggable

class Post extends Model
{
    use Taggable;   // just add this

    // more code...
}

我们将使用Post模型作为用户帖子应用程序的示例,但它可以是任何标记的模型,包括用户本身!

向模型添加标签或标签集

<?php
// add single tag, which is Eloquent
$tag = Tag::createOne('public');
$post->addTag($tag);

// add single tag by its name, no need to for you to fetch it first
$post->addTag('public');

// add tags
$tags = Tag::all();
$post->addTag($tags);

// add tags by name
$post->addTag(['php', 'laravel', 'composer']);

// add a tag or tags to multiple models at once
// (the second argument must be an Eloquent Collection of the model)
Post::addTagsTo($tags, $posts);           // tags can be am Eloquent Collection
Post::addTagsTo(['html', 'css'], $posts); // tags can be an array of strings too
Post::addTagTo('php', $posts);            // to pass a single tag, call addTagTo
                                          // instead of addTagsTo (shorter syntax)

获取模型标签

<?php
// get all tags
$tags = $post->getTags();

// you specify the depth of the search to get nested tags
// that is, if a post has a tag and that tag has another tag,
// you will get the parent tag as well.
$depth = 4;
$tags = $post->getTags($depth);

// get tags matching a regex
// (in this case, any tag whose name is made up of two words)
$tags = $post->getTagsMatching('/\W \W/');

// again, the depth of the search can be specified
$tags = $post->getTagsMatching('/\W \W/', $depth);

检查模型是否有标签或标签集

<?php
// check if has a single tag
$post->hasTag($tag);        // Eloquent model
$post->hasTag('public');    // name string

// check if has all provided tags
$post->hasTags($tags);              // Eloquent collection
$post->hasTags(['foo', 'bar']);     // name string

// check if it has any of the provided tags
$post->hasAnyTags($tags);              // Eloquent collection
$post->hasAnyTags(['foo', 'bar']);     // name string

// the depth of the search can also be provided
$depth = 3;
$post->hasTag($tag, $depth);
$post->hasTags($tags, $depth);
$post->hasAnyTags($tags, $depth);

从模型中删除标签或标签集

<?php
// remove single tag, which is Eloquent
$tag = Tag::createByName('public');
$post->delTag($tag);

// remove via tag name
$post->delTag('public');

// remove tags from Eloquent collection
$tags = $post->getTagsMatching('*www*');
$post->delTags($tags);

// remove tags by name
$post->delTags(['php', 'laravel', 'composer']);

// remove all tags
$post->delAllTags();

// remove a tag or tags from multiple models at once
// (the second argument must be an Eloquent Collection of the model)
Post::delTagsFrom($tags, $posts);           // tags can be an Eloquent Collection
Post::delTagsFrom(['html', 'css'], $posts); // tags can be an array of strings
Post::delTagFrom('php', $posts);            // to pass a single tag you can call
                                            // delTagFrom instead of delTagsFrom

// remove all tags from the given models
Post::delAllTagFrom($posts);

设置模型的标签

<?php
// the set method basically removes all tags of the model
// and add the new ones, thus 'setting' its tags.
// It is syntax sugar.
$post->setTags($tags);

获取具有标签或仅标签名称的模型

<?php
// attach the tags to the given posts
$posts = Post::withTags($posts);
$posts = Post::withTagNames($posts);

// all posts
$posts = Post::withTags(Post::all());
$posts = Post::withTagNames(Post::all());

// posts that match a condition
$posts = Post::withTags(Post::where($condition)->get());
$posts = Post::withTagNames(Post::where($condition)->get());

在示例的第二行中,仅附加标签名称到模型,而不是标签本身(标签是一个Tag实例)。

在上面的示例中,每个模型将有一个新的属性名为tags,这是一个包含模型直接标签的Collection(即不包含嵌套标签)。您可以通过使用Laravel的集合便捷方法(如mappluckfilter等)对标签执行其他操作。

获取被标签或标签集标记的模型

<?php
// by a single tag
$posts = Post::taggedBy($tag);      // Eloquent model
$posts = Post::taggedBy('public');  // name string

// posts tagged by at least one of the given tags
$posts = Post::taggedBy($tags);               // Eloquent collection
$posts = Post::taggedBy(['php', 'laravel']);  // array of strings

// you can provide the depth of the search in the second argument
// default depth is 1
$posts = Post::taggedBy($tags, 3);

// it is possible to specify the namespace (explained in the next section)
$posts = Post::taggedBy($tags, 1, 'posts');
$posts = Post::taggedBy($tags, namespace: 'posts'); // named arguments syntax

在上面的示例中,taggedBy将返回被至少一个提供的标签标记的所有模型。如果您想要被所有标签标记的模型,则使用具有与taggedBy相同语法的静态方法taggedByAll

注意:由于taggedByAll需要检查所有标签是否匹配,而taggedBy只需检查一个标签,因此如果使用关系数据库并且尝试通过具有高深度值的许多标签来获取标记的模型,则taggedByAll可能会变慢。在这种情况下,图数据库更合适。但对于大多数应用程序,即使深度值等于五或六,这也可能不是问题。

命名空间

可以有多个具有不同命名空间(即上下文)的标签。

例如,开发者可能希望为帖子添加一个“top”标签,同时也为视频添加一个“top”标签(尽管我个人认为,只要开发者妥善处理,使用一个“top”标签来同时标记帖子和视频就足够了)。

另一个例子,可能存在一个用于支付上下文的“free”标签,以及一个用于自由软件的“free”标签,在这里它意味着“自由”。

最终,决定是否需要使用命名空间的是开发者,因为他们最了解应用的需求。

让我们看看如何使用它们。为了创建、查找或删除特定命名空间下的标签

<?php
// just add the namespace as the second parameter
$namespace = 'posts';

// create the tags
$tag = Tag::createOne($name, $namespace);    // one tag
$tags = Tag::createMany($names, $namespace); // multiple tags

// find the tags
$tag = Tag::findByName($name, $namespace); // one tag
$tags = Tag::findByName($names, $namespace); // multiple tags

// delete the tags
Tag::delByName($name, $namespace);  // one tag
Tag::delByName($names, $namespace); // multiple tags

当调用getTagshasTags方法时,它们将使用通过调用getTagNamespace获取的命名空间,默认为null。您可以为给定的模型使用一个静态命名空间或一个动态命名空间。

以下是一个静态(即不更改)标签命名空间的示例

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Uwla\Ltags\Trait\Taggable

class Post extends Model
{
    use Taggable;   // just add this

    // override method
    public function getTagNamespace()
    {
        return 'posts';
    }
}

现在,一个动态的示例

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Uwla\Ltags\Trait\Taggable

class Post extends Model
{
    use Taggable;   // just add this

    public $tagNamespace = 'posts';

    // override method
    public function getTagNamespace()
    {
        return $this->tagNamespace;
    }
}

您可以在实时中更改它

<?php
$tag1 = Tag::createOne('public', 'bar'); // bar namespace
$tag2 = Tag::createOne('public', 'foo'); // foo namespace

$post->tagNamespace = 'bar';    // set namespace to 'bar'
$post->addTag('public');        // adds $tag1
$post->tagNamespace = 'foo';    // set namespace to 'foo'
$post->hasTag('public');        // returns false, since the namespace is 'foo'
$post->hasTag($tag);            // returns true, since the model was provided
$post->addTag('public');        // adds $tag2
$post->tagNamespace = 'bar';    // set namespace to 'bar'
$post->delTag('public');        // deletes $tag1
$post->tagNamespace = 'foo';    // set namespace to 'foo'
$post->hasTag('public');        // returns true, since the namespace is 'foo'

命名空间只会影响传递给方法的字符串名称作为参数的方法,因为与这些名称关联的标签需要在幕后进行检索。如果传递了一个Eloquent模型或Eloquent集合作为参数,则命名空间将没有任何影响,因为Eloquent模型已经具有唯一标识标签的唯一标签ID。

自定义标签模型

您可以使用自定义的Tag模型代替默认的模型,默认为Uwla\Ltags\Models\Tag

<?php

namespace App\Models;

use Uwla\Ltags\Models\Tag as BaseTag;

class Tag extends BaseTag
{
    // disable timestamps, if you want
    $timestamps = false;

    // maybe you changed the table name..
    $table = 'tag';
}

为了使Taggable特质使用您的标签而不是默认标签,您可以覆盖其getTagClass方法如下

<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Uwla\Ltags\Trait\Taggable;
use App\Models\Tag;

class Post extends Model
{
    use Taggable;

    protected static function getTagClass()
    {
        return Tag::class;
    }
}

不必为每个模型都这样做,您还可以有一个自定义的Taggable特质

<?php

namespace App\Traits;

use Uwla\Ltags\Trait\Taggable as BaseTaggable
use App\Models\Tag;

class Taggable extends BaseTaggable
{
    protected static function getTagClass()
    {
        return Tag::class;
    }
}

然后,使用App\Traits\Taggable而不是Uwla\Ltags\Traits\Taggable

示例

除了使用标签来组织和搜索内容(如视频或文章)外,还有几种方法可以使标签非常实用。让我们来看看。

假设您有一个包含帖子应用程序,其中一些是公开的。在您的Laravel PostPolicy中,您可以这样做以检查用户是否允许查看特定的帖子

<?php

/**
 * Determine whether the user can view the post
 *
 * @param  App\User  $user
 * @param  App\Post  $post
 * @return \Illuminate\Auth\Access\Response|bool
 */
public function view(User $user, Post $post)
{
    // any user can view a public post
    if ($post->hasTag('public'))
        return true;

    // user can view a private post if he is the post owner
    // the "traditional" way is to put a `user_id` column in the posts table
    // but of course there are better ways to do this.
    return $post->user_id == $user->id;
}

在上面的示例中,我们避免了在帖子表中添加is_public列的需要。

另一个示例是使用标签根据用户角色标记用户。一些应用程序只是向用户表添加一个role列,然后他们会检查user->role。但这会创建一个额外的列。如果您不需要复杂的访问控制系统,但需要简单地将用户根据角色进行分类的方法,您可以为用户添加标签。

<?php
// instead of
if ($user->role == 'admin')
{
    // do stuff
}
// or maybe
if ($user->role == 'vip')
{
    // allow vip content
}

// you could do the following
if ($user->hasTag('admin'))
{
    // do stuff
}
// or maybe
if ($user->hasTag('vip'))
{
    // allow vip content
}

另一个示例是一个推广编程竞赛的应用程序,这些竞赛可能有公开或私有的可见性,运行或过期的状态等。开发者可能选择使用标签作为关于竞赛的额外信息来源,而不是在竞赛表中添加多个列。当然,这是一个好决定还是坏决定取决于开发者根据自己的具体情况来判断。

这些只是三个简单的示例。可能性仅受开发者想象力的限制。标签可以适应任何可以按某些标准分组资源的上下文,任何处理集群的上下文。

贡献

欢迎贡献。分叉存储库,进行您的更改,然后提交拉取请求。请检查开发以获取所有开发相关说明。

帮助

在GitHub上为此存储库打开一个问题。我很乐意帮助您。

许可证

MIT。