A solution for adding tags in Laravel
Maybe something like this, typed just from head, not tested, but i hope it kinda give you an idea
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);
}
There is really no need to check if there are commas and have two different paths. If there are no commas, explode will return one element to iterate over. You can literally just remove the if and the else.
$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);
}
}
Additionally, there is the ability to do firstOrCreate
which you can see the documentation for here.
The firstOrCreate method will attempt to locate a database record using the given column / value pairs. If the model can not be found in the database, a record will be inserted with the attributes from the first parameter, along with those in the optional second parameter.
This can be use to refactor code to the following:
$tagList = explode(",", $tags);
// Loop through the tag array that we just created
foreach ($tagList as $tags) {
$tag = Tag::firstOrCreate(['slug' => $tags];
}
$tags = Tag::whereIn('name', $tagList)->get()->pluck('id')->get();
$article->tags()->sync($tags);
I think the Easiest way to do tagging is by using many-to-many polymorphic relationship... morphedByMany()
and morphToMany()
.
See this example code...
In migration their are 3 tables
articles
,tags
,taggables
# --- 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, ...
});
}
In the Model
# 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');
}
}
In the 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
]);
}
Now using Elequent you can easily access data
$article->tags; # retrieve related tags for the article
$tags->articles; # or $tags->articles->get() retrieve all article that has specific tag
For storing and updating article
# 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";
}
For more details about many-to-many polymorphic relationship