Creating Dynamic Tag
#Dynamic Tags
When using tags element we might come across a case where we have an initial list of options as possible tags but the user is allowed to create a new one. In this guide we're going to demonstrate a solution how this can be achieved.
#Database Tables
Let's say we have 3 data tables with the following fields:
user
id - integer
email - string
password - string
skill
id - integer
name - string
public - integer
skill_user
user_id - integer
skill_id - integer
#User Model
The User
model is connected to Skill
with belongsToMany
:
<?php
// app/User.php
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
// ...
class User extends Authenticatable
{
// ...
public function skills() {
return $this->belongsToMany(Skill::class);
}
}
<?php
// app/Skill.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Skill extends Model
{
//
}
#Creating Form
Now let's create a form where we'll assign skills to a user:
<?php
namespace App\Forms;
use App\Skill;
use App\User;
class UserForm extends \Laraform
{
public $model = User::class;
public function schema() {
return [
'id' => [
'type' => 'key'
],
'skills' => [
'type' => 'tags',
'label' => 'Skills',
'create' => true,
'items' => $this->skills()
]
]
}
protected function skills() {
return Skill::where('public', 1)->mapWithKeys(function($skill) {
return [
$skill->id => $skill->name
]
})
}
}
If we render the form we can see that all the skills are available as tags. We also enabled create
which means if we type a new string and hit enter a new tag will be added which does not exist in the database.
#Saving New Tag
The next step is to save a new skill tag when added. Let's create a before
hook to save that before the entity gets inserted into the database:
class UserForm extends \Laraform
{
// ...
public function before() {
$skills = collect($this->data['skills']);
foreach ($skills->filter(function($value){ return !is_numeric($value); }) as $key => $name) {
$skill = new Skill;
$skill->name = $name;
$skill->public = 0;
$skill->save();
$this->data['skills'][$key] = $skill->id;
}
}
// ...
}
Now a new tag, which does not have a numeric value (meaning it's added manually) will be inserted to the database and its value will be replaced in the data. If you try saving the form new while having a new tag, you'll notice it will be created in the database and will be assigned to the user.
So far so good, but what if we try to load this form for the currently authenticated user for example? If that user has skills which aren't public they will not appear in the tags list.
#Appending Non-Public Skills
We need to make those ones available on a per user basis, meaning each authenticated user will have a unique list of tags which will include public tags plus the tags created by them.
Let's implement that by updating our skills
method:
class UserForm extends \Laraform
{
// ...
protected function skills() {
$existingSkills = Auth::user()->skills->keyBy('id')->keys();
return Skill::where('public', 1)->orWhereIn('id', $existingSkills)->mapWithKeys(function($skill) {
return [
$skill->id => $skill->name
]
})
}
}
Now every skill that the user is assigned will surely among the tags list.
#Complete Form
Let's see the whole class in one extended with a Submit button:
<?php
namespace App\Forms;
use App\Skill;
use App\User;
class UserForm extends \Laraform
{
public $model = User::class;
public function schema() {
return [
'id' => [
'type' => 'key'
],
'skills' => [
'type' => 'tags',
'label' => 'Skills',
'create' => true,
'items' => $this->skills()
]
]
}
public function buttons() {
return [[
'label' => 'Submit'
]];
}
public function before() {
$skills = collect($this->data['skills']);
foreach ($skills->filter(function($value){ return !is_numeric($value); }) as $key => $name) {
$skill = new Skill;
$skill->name = $name;
$skill->public = 0;
$skill->save();
$this->data['skills'][$key] = $skill->id;
}
}
protected function skills() {
$existingSkills = Auth::user()->skills->keyBy('id')->keys();
return Skill::where('public', 1)->orWhereIn('id', $existingSkills)->mapWithKeys(function($skill) {
return [
$skill->id => $skill->name
]
})
}
}