首页 > 解决方案 > Laravel 中添加标签的解决方案

问题描述

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

我已经有三个表:tagstaggables并且articles它们通过 Eloquent 关系方法链接,给定我在上一个问题中提出的设置。

这是我的更新方法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. 如果它不存在,我创建它,然后将它与文章同步,否则我只是同步它

这种方法感觉很混乱,但是,有什么办法可以让这个更干净吗?

鉴于提供的答案,更新

我采用了以下方法:

/**
 * 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标签(名称,slug ,updated_at ,created_at) values ( sauce, sauce, 2018-05-26 11:42:17, 2018-05-26 11:42:17))

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

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateTagsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tags', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('slug')->unique();
            $table->timestamps();
        });

        Schema::create('taggables', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('tag_id')->unsigned();
            $table->integer('taggable_id')->unsigned();
            $table->string('taggable_type');

            $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('taggables');
        Schema::dropIfExists('tags');
    }
}

标签: phplaravellaravel-5

解决方案


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

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', $tagsNames)->get()->pluck('id')->get();
    $article->tags()->sync($tags);
}

推荐阅读