在Laravel中添加标签的解决方案

时间:2018-05-25 12:53:10

标签: php laravel laravel-5

在表单中,我有一个Tags字段,它只是一个标准的文本字段。用户可以键入标记名称,然后将其添加到文章中。

我已经有三个表:tagstaggablesarticles并且它们是通过Eloquent关系方法链接的,考虑到a previous question中的设置,我问过。

这是我ArticleController

中的更新方法
/**
 * Update the specified resource in storage.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  int  $id
 * @return \Illuminate\Http\Response
 */
public function update(Request $request, $id)
{
    $validatedData = $request->validate([
        'title' => 'required',
        'excerpt' => 'required',
    ]);

    $article = Article::find($id);

    $article->title = $request->get('title');
    $article->author = $request->get('author');
    $article->category = $request->get('category');
    $article->excerpt = $request->get('excerpt');
    $article->content = $request->get('content');
    $article->featuredImage = $request->get('featuredImage');
    $article->featuredVideo = $request->get('featuredVideo');
    $article->readingTime = $request->get('readingTime');
    $article->published = $request->get('published');

    $article->save();

    /**
     * Once the article has been saved, we deal with the tag logic.
     * Grab the tag or tags from the field, sync them with the article
     */
    $tags = $request->get('tags');
    $comma = ',';

    if (!empty($tags)) {
        if (strpos($tags, $comma) !== false) {
            $tagList = explode(",", $tags);

            // Loop through the tag array that we just created
            foreach ($tagList as $tags) {

                // Get any existing tags
                $tag = Tag::where('name', '=', $tags)->first();

                // If the tag exists, sync it, otherwise create it
                if ($tag != null) {
                    $article->tags()->sync($tag->id);
                } else {
                    $tag = new Tag();

                    $tag->name = $tags;
                    $tag->slug = str_slug($tags);

                    $tag->save();

                    $article->tags()->sync($tag->id);
                }
            }
        } else {
            // Only one tag
            $tag = Tag::where('name', '=', $tags)->first();

            if ($tag != null) {
                $article->tags()->sync($tag->id);
            } else {
                $tag = new Tag();

                $tag->name = $tags;
                $tag->slug = str_slug($tags);

                $tag->save();

                $article->tags()->sync($tag->id);
            }
        }
    }

    return back();
    return redirect()->back();
}

在查找标签的方法部分中,我执行以下操作:

  1. 检查字段是否为空
  2. 检查发送的字符串是否包含逗号
  3. 如果有逗号,我使用explode()将字符串转换为数组
  4. 遍历数组以查看字符串中的给定标记是否已存在
  5. 如果它不存在,我创建它然后将它与文章同步,否则我只是同步它
  6. 这种方法感觉非常混乱,但是,有什么方法可以让我更干净吗?

    根据提供的答案进行更新

    我选择了以下方法:

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $validatedData = $request->validate([
            'title' => 'required',
            'excerpt' => 'required',
        ]);
    
        $article = new Article();
    
        $article->title = $request->get('title');
        $article->author = $request->get('author');
        $article->category = $request->get('category');
        $article->excerpt = $request->get('excerpt');
        $article->content = $request->get('content');
        $article->featuredImage = $request->get('featuredImage');
        $article->featuredVideo = $request->get('featuredVideo');
        $article->readingTime = $request->get('readingTime');
        $article->published = $request->get('published');
    
        //If no featured image set, automatically create featured image placeholder
        if ($request->get('featuredImage') == null) {
            $article->featuredImage = "http://via.placeholder.com/350x150";
        }
    
        $article->save();
    
        // Handle Tags
        $tags = $request->get('tags');
    
        if (!empty($tags)) {
            $tagList = array_filter(explode(",", $tags));
    
            // Loop through the tag array that we just created
            foreach ($tagList as $tags) {
                $tag = Tag::firstOrCreate(['name' => $tags, 'slug' => str_slug($tags)]);
            }
    
            $tags = Tag::whereIn('name', $tagList)->get()->pluck('id');
    
            $article->tags()->sync($tags);
        }
    
        return redirect('editable/news-and-updates')->with('success', 'Article has been added');
    }
    

    然后,为了在更新时显示标签,我做了以下工作:

    /**
     * Show the form to edit this resource
     */
    public function edit($id)
    {
        $user = auth::user();
        $article = Article::find($id);
    
        // Get the tags associated with this article and convert to a comma seperated string
        if ($article->has('tags')) {
            $tags = $article->tags->pluck('name')->toArray();
    
            $tags = implode(', ', $tags);
        } else {
            $tags = "";
        }
    
        return view('editable.news.edit', compact('article', 'user', 'tags'));
    }
    

    基本上,我只是抓取与文章关联的标签,将它们转换为数组,然后使用implode()

    这为标签字段中的逗号分隔列表提供了标签,如:

    blue, red, orange

    但是,在更新时,如果我尝试使用相同的标签保存,我会得到:

    SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'sauce' for key 'tags_slug_unique' (SQL: insert into代码( {名称{1}} {蛞蝓{1}}的updated_at , {created_at {1}}

    以下是标记迁移以供参考:

    ,

3 个答案:

答案 0 :(得分:3)

确实没有必要检查是否有逗号并且有两条不同的路径。如果没有逗号,则explode将返回一个迭代的元素。你可以直接删除if和else。

lists.stream().max(Comparator.comparing(List::size)).get()

此外,您还可以$tagList = explode(",", $tags); // Loop through the tag array that we just created foreach ($tagList as $tags) { // Get any existing tags $tag = Tag::where('name', '=', $tags)->first(); // If the tag exists, sync it, otherwise create it if ($tag != null) { $article->tags()->sync($tag->id); } else { $tag = new Tag(); $tag->name = $tags; $tag->slug = str_slug($tags); $tag->save(); $article->tags()->sync($tag->id); } } 查看here的文档。

  

firstOrCreate方法将尝试查找数据库记录   使用给定的列/值对。如果找不到该模型   在数据库中,将插入一条带有属性的记录   第一个参数,以及可选的第二个参数中的参数。

这可用于将代码重构为以下内容:

firstOrCreate

答案 1 :(得分:2)

也许像这样的东西,只是从头上输入,没有经过测试,但我希望它有点给你一个想法

public function update(Request $request, Article $article)
{
    $validatedData = $request->validate([
        'title' => 'required',
        'excerpt' => 'required',
        // Make validation for all inputs !
    ]);

    // Fill model with inputs and save (make sure that inputs are in fillable model property)
    $article->fill($request->all())->save();

    // Handle Tags 
    $this->handleTags($request, $article);

    // Return Redirect to previous URL
    return redirect()->back();
}

/**
 * Handle Tags for Article
 * @param  \Illuminate\Http\Request  $request
 * @param \App\Article $article
 * @return void
 */
public function handleTags(Request $request, Article $article){
    /**
     * Once the article has been saved, we deal with the tag logic.
     * Grab the tag or tags from the field, sync them with the article
     */
    $tagsNames = explode(',', $request->get('tags'));

    // Create all tags (unassociet)
    foreach($tagsNames as $tagName){
        Tag::firstOrCreate(['name' => $tagName, 'slug' => str_slug($tagName)])->save();
    }

    // Once All tags are created we can query them
    $tags = Tag::whereIn('name', $tagNames)->get()->pluck('id')->get();
    $article->tags()->sync($tags);
}

答案 2 :(得分:1)

我认为最简单的标记方法是使用多对多多态关系... morphedByMany()morphToMany()。 请参阅此示例代码...

  

在迁移中,它们有3个表articlestagstaggables

# --- for Article Table ---
public function up()
{
    Schema::create('articles', function (Blueprint $table) {
        $table->increments('id');
        $table->string('title');
        // ---
    });
}
# --- for Tags Table ---
public function up()
{
    Schema::create('tags', function (Blueprint $table) {
        $table->increments('id');
        $table->string('tagname');
    });
}

# --- for Taggables Table ---
public function up()
{
    Schema::create('taggables', function (Blueprint $table) {
        $table->integer('tag_id');
        $table->integer('taggable_id'); // for storing Article ID's
        $table->string('taggable_type'); // Aside from Article, if you decide to use tags on other model eg. Videos, ... 
    });
}
  

在模型中

# Tag.php Model
class Tag extends Model
{
     protected $fillable = [

            'tagname',
    ];

    public function article()
    {
        return $this->morphedByMany('Yourapp\Article', 'taggable');
    }

}


# Article.php Model
class Article extends Model
{
     protected $fillable = [
        'title',
        # and more...
    ];
    public function tags()
    {
        return $this->morphToMany('Yourapp\Tag', 'taggable');
    }

}
  

在AppServiceProvide.php中〜Yourapp / app / Providers / AppServiceProvider.php

public function boot()
{
    //... by creating this map you don't need to store the "Yourapp\Post" to the "taggable_type" on taggable table
    Relation::morphMap([
        'article' => 'Yourapp\Article',
        'videos' => 'Yourapp\Videos', // <--- other models may have tags
    ]);

}
  

现在使用Elequent,您可以轻松访问数据

   $article->tags; # retrieve related tags for the article
   $tags->articles; # or $tags->articles->get()  retrieve all article that has specific tag
  

用于存储和更新文章

# SAVING article with tags
public function store(Request $request) 
{
    $validatedData = $request->validate([
        'title' => 'required',
        //----
        // Validate tags and also it should be an Array but its up to you
        'tag' => 'required|array|exists:tags,id' # < (exist:tags,id) This will check if tag exist on the Tag table
    ]);

    $article = Article::create([
        'title' => $request->input('title'),
        //----
    ]);

    //Adding tags to article, Sync() the easy way
    $article->tags()->sync($request->input('tag'));

    return "Return anywhare";
}

# UPDATE tags article
public function update(Request $request, $id) 
{   
    // Validate first and ...
    $article = Article::find($id)->first();
    $article->title = $request->input('title');
    $article->save();
    //Updating tags of the article, Sync() the easy way
    $article->tags()->sync($request->input('tag'));

    return "Return anywhare";
}

有关多对多多态关系的更多details信息