Add Tags to Articles: Laravel Many-to-Many Relationships with Select2 (ok)

https://blog.quickadminpanel.com/add-tags-to-articles-laravel-many-to-many-relationships-with-select2/

C:\xampp\htdocs\datvietcoconut\database\migrations\2022_12_09_015715_create_articles_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateArticlesTable extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('articles', function (Blueprint $table) {
      $table->increments('id');
      $table->string('title')->nullable();
      $table->text('article_text')->nullable();
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('articles');
  }
}

C:\xampp\htdocs\datvietcoconut\database\migrations\2022_12_09_015735_create_tags_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTagsTable extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('tags', function (Blueprint $table) {
      $table->increments('id');
      $table->string('name')->nullable();
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('tags');
  }
}

C:\xampp\htdocs\datvietcoconut\database\migrations\2022_12_09_015752_create_article_tag_table.php

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateArticleTagTable extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('article_tag', function (Blueprint $table) {
      $table->integer('article_id')->unsigned()->nullable();
      $table->integer('tag_id')->unsigned()->nullable();
      $table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade');
      $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::dropIfExists('article_tag');
  }
}

C:\xampp\htdocs\datvietcoconut\app\Models\Tag.php

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
  use HasFactory;
  protected $fillable = ['name'];
}

C:\xampp\htdocs\datvietcoconut\app\Models\Article.php

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
  use HasFactory;
  protected $fillable = ['title', 'article_text'];
  public function tag()
  {
    return $this->belongsToMany(Tag::class, 'article_tag');
  }
}

C:\xampp\htdocs\datvietcoconut\routes\web.php

<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Admin\ArticlesController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
    return view('welcome');
});
Route::group(['middleware' => ['auth'], 'prefix' => 'admin', 'as' => 'admin.'], function () {
  Route::resource('articles', ArticlesController::class);
});
Auth::routes();
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');

C:\xampp\htdocs\datvietcoconut\app\Http\Controllers\Admin\ArticlesController.php

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Article;
use App\Models\Tag;
use Illuminate\Http\Request;
use App\Http\Requests\Admin\StoreArticlesRequest;
use App\Http\Requests\Admin\UpdateArticlesRequest;
class ArticlesController extends Controller
{
  public function index()
  {
    $articles = Article::all();
    return view('admin.articles.index', compact('articles'));
  }
  public function create()
  {
    $tags = Tag::get()->pluck('name', 'id');
    return view('admin.articles.create', compact('tags'));
  }
  public function store(StoreArticlesRequest $request)
  {
    $article = Article::create($request->all());
    $article->tag()->sync((array) $request->input('tag'));
    return redirect()->route('admin.articles.index');
  }
  public function edit($id)
  {
    $tags = Tag::get()->pluck('name', 'id');
    $article = Article::findOrFail($id);
    return view('admin.articles.edit', compact('article', 'tags'));
  }
  public function update(UpdateArticlesRequest $request, $id)
  {
    $article = Article::findOrFail($id);
    $article->update($request->all());
    $article->tag()->sync((array) $request->input('tag'));
    return redirect()->route('admin.articles.index');
  }
  public function destroy($id)
  {
    $article = Article::findOrFail($id);
    $article->delete();
    return redirect()->route('admin.articles.index');
  }
}

C:\xampp\htdocs\datvietcoconut\resources\views\admin\articles\index.blade.php

<div class="panel-body table-responsive">
  <table class="table table-bordered table-striped datatable">
    <thead>
      <tr>
        <th>Title</th>
        <th>Tags</th>
        <th> </th>
      </tr>
    </thead>
    <tbody>
      @if (count($articles) > 0)
        @foreach ($articles as $article)
        <tr data-entry-id="{{ $article->id }}">
          <td field-key='title'>{{ $article->title }}</td>
          <td field-key='tag'>
            @foreach ($article->tag as $singleTag)
            <span class="label label-info label-many">{{ $singleTag->name }}</span>
            @endforeach
          </td>
          <td>
            @can('article_view')
            <a href="{{ route('admin.articles.show',[$article->id]) }}" class="btn btn-xs btn-primary">View</a>
            @endcan
            @can('article_edit')
            <a href="{{ route('admin.articles.edit',[$article->id]) }}" class="btn btn-xs btn-info">Edit</a>
            @endcan
            @can('article_delete')
            {!! Form::open(array(
            'style' => 'display: inline-block;',
            'method' => 'DELETE',
            'onsubmit' => "return confirm('Are you sure?');",
            'route' => ['admin.articles.destroy', $article->id])) !!}
            {!! Form::submit('Delete', array('class' => 'btn btn-xs btn-danger')) !!}
            {!! Form::close() !!}
            @endcan
          </td>
        </tr>
        @endforeach
      @else
      <tr>
        <td colspan="8">@lang('global.app_no_entries_in_table')</td>
      </tr>
      @endif
    </tbody>
  </table>
</div>

C:\xampp\htdocs\datvietcoconut\resources\views\admin\articles\edit.blade.php

{!! Form::label('tag', trans('global.articles.fields.tag').'', ['class' => 'control-label']) !!}
<button type="button" class="btn btn-primary btn-xs" id="selectbtn-tag">
  {{ trans('global.app_select_all') }}
</button>
<button type="button" class="btn btn-primary btn-xs" id="deselectbtn-tag">
  {{ trans('global.app_deselect_all') }}
</button>
{!! Form::select('tag[]', $tags, old('tag') ? old('tag') : $article->tag->pluck('id')->toArray(), ['class' =>
'form-control select2', 'multiple' => 'multiple', 'id' => 'selectall-tag' ]) !!}
<p class="help-block"></p>
@if($errors->has('tag'))
<p class="help-block">
  {{ $errors->first('tag') }}
</p>
@endifhp

C:\xampp\htdocs\datvietcoconut\resources\views\admin\articles\create.blade.php

@extends('layouts.app')
@section('content')
<h3 class="page-title">Articles</h3>
{!! Form::open(['method' => 'POST', 'route' => ['admin.articles.store']]) !!}
<div class="panel panel-default">
  <div class="panel-heading">
    Create
  </div>
  <div class="panel-body">
    <div class="row">
      <div class="col-xs-12 form-group">
        {!! Form::label('title', 'Title', ['class' => 'control-label']) !!}
        {!! Form::text('title', old('title'), ['class' => 'form-control', 'placeholder' => '']) !!}
        <p class="help-block"></p>
        @if($errors->has('title'))
        <p class="help-block">
          {{ $errors->first('title') }}
        </p>
        @endif
      </div>
    </div>
    <div class="row">
      <div class="col-xs-12 form-group">
        {!! Form::label('article_text', 'Article Text', ['class' => 'control-label']) !!}
        {!! Form::textarea('article_text', old('article_text'), ['class' => 'form-control ', 'placeholder' => '']) !!}
        <p class="help-block"></p>
        @if($errors->has('article_text'))
        <p class="help-block">
          {{ $errors->first('article_text') }}
        </p>
        @endif
      </div>
    </div>
    <div class="row">
      <div class="col-xs-12 form-group">
        {!! Form::label('tag', 'Tags', ['class' => 'control-label']) !!}
        <button type="button" class="btn btn-primary btn-xs" id="selectbtn-tag">
          Select all
        </button>
        <button type="button" class="btn btn-primary btn-xs" id="deselectbtn-tag">
          Deselect all
        </button>
        {!! Form::select('tag[]', $tags, old('tag'), ['class' => 'form-control select2', 'multiple' => 'multiple', 'id'
        => 'selectall-tag' ]) !!}
        <p class="help-block"></p>
        @if($errors->has('tag'))
        <p class="help-block">
          {{ $errors->first('tag') }}
        </p>
        @endif
      </div>
    </div>
  </div>
</div>
{!! Form::submit('Save article', ['class' => 'btn btn-danger']) !!}
{!! Form::close() !!}
@stop
@section('styles')
@parent
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
@stop
@section('javascript')
@parent
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<script>
  $("#selectbtn-tag").click(function(){
      $("#selectall-tag > option").prop("selected","selected");
      $("#selectall-tag").trigger("change");
  });
  $("#deselectbtn-tag").click(function(){
      $("#selectall-tag > option").prop("selected","");
      $("#selectall-tag").trigger("change");
  });
  $(document).ready(function () {
      $('.select2').select2();
  });
</script>
@stophp

C:\xampp\htdocs\datvietcoconut\app\Http\Requests\Admin\StoreArticlesRequest.php

<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class StoreArticlesRequest extends FormRequest
{
  /**
   * Determine if the user is authorized to make this request.
   *
   * @return bool
   */
  public function authorize()
  {
    return true;
  }
  /**
   * Get the validation rules that apply to the request.
   *
   * @return array
   */
  public function rules()
  {
    return [
      'tag.*' => 'exists:tags,id',
    ];
  }
}

C:\xampp\htdocs\datvietcoconut\app\Http\Requests\Admin\UpdateArticlesRequest.php

<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class UpdateArticlesRequest extends FormRequest
{
  /**
   * Determine if the user is authorized to make this request.
   *
   * @return bool
   */
  public function authorize()
  {
    return false;
  }
  /**
   * Get the validation rules that apply to the request.
   *
   * @return array
   */
  public function rules()
  {
    return [
      //
    ];
  }
}

C:\xampp\htdocs\datvietcoconut\composer.json

{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "require": {
        "php": "^7.3|^8.0",
        "fruitcake/laravel-cors": "^2.0",
        "guzzlehttp/guzzle": "^7.0.1",
        "laravel/framework": "^8.75",
        "laravel/sanctum": "^2.11",
        "laravel/tinker": "^2.5",
        "laravel/ui": "^3.4",
        "laravelcollective/html": "^6.3"
    },
    "require-dev": {
        "facade/ignition": "^2.5",
        "fakerphp/faker": "^1.9.1",
        "laravel/sail": "^1.0.1",
        "mockery/mockery": "^1.4.4",
        "nunomaduro/collision": "^5.10",
        "phpunit/phpunit": "^9.5.10",
        "sven/artisan-view": "^3.4"
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi"
        ],
        "post-update-cmd": [
            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
        ],
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate --ansi"
        ]
    },
    "extra": {
        "laravel": {
            "dont-discover": []
        }
    },
    "config": {
        "optimize-autoloader": true,
        "preferred-install": "dist",
        "sort-packages": true
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}

Add Tags to Articles: Laravel Many-to-Many Relationships with Select2


This article is a simple example of belongsToMany() relationships in Laravel, with managing the data in CRUD form and table, using Bootstrap framework and Select2 library.

Here’s what we’re building – a list of articles with their tags.

Notice: This example was mostly generated with our QuickAdminPanel generator, but will be explained step-by-step in plain Laravel, so you can use it without our generator.

Step 1. Database/model structure

Here are the migrations.

Articles:

Schema::create('articles', function (Blueprint $table) {
    $table->increments('id');
    $table->string('title')->nullable();
    $table->text('article_text')->nullable();    
    $table->timestamps();
});

Tags:

Schema::create('tags', function (Blueprint $table) {
    $table->increments('id');
    $table->string('name')->nullable();    
    $table->timestamps();
});

Pivot table:

Schema::create('article_tag', function (Blueprint $table) {
    $table->integer('article_id')->unsigned()->nullable();
    $table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade');
    $table->integer('tag_id')->unsigned()->nullable();
    $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
});

The models are really simple, too.

app/Tag.php:

class Tag extends Model
{
    protected $fillable = ['name'];
}

app/Article.php:

class Article extends Model
{
    protected $fillable = ['title', 'article_text'];
    
    public function tag()
    {
        return $this->belongsToMany(Tag::class, 'article_tag');
    }    
}

Routes, Controller and Create Form

Our routes/web.php, in addition to Route Group and Middleware, will have this main line:

Route::group(['middleware' => ['auth'], 'prefix' => 'admin', 'as' => 'admin.'], function () {
    // ... other routes
    Route::resource('articles', 'Admin\ArticlesController');
});

Now, here’s our (simplified) app/Http/Controllers/Admin/ArticlesController.php:

namespace App\Http\Controllers\Admin;

use App\Article;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\StoreArticlesRequest;
use App\Http\Requests\Admin\UpdateArticlesRequest;

class ArticlesController extends Controller
{

    public function index()
    {
        $articles = Article::all();
        return view('admin.articles.index', compact('articles'));
    }

    public function create()
    {
        $tags = \App\Tag::get()->pluck('name', 'id');
        return view('admin.articles.create', compact('tags'));
    }

    public function store(StoreArticlesRequest $request)
    {
        // ... to be discussed later
    }

    public function edit($id)
    {
        // ... to be discussed later
    }

    public function update(UpdateArticlesRequest $request, $id)
    {
        // ... to be discussed later
    }

    public function destroy($id)
    {
        // ... to be discussed later
    }
}

To add the articles with their tags, we need to have a create form. Here’s our resources/views/admin/articles/create.blade.php:

@extends('layouts.app')

@section('content')
    <h3 class="page-title">Articles</h3>
    {!! Form::open(['method' => 'POST', 'route' => ['admin.articles.store']]) !!}

    <div class="panel panel-default">
        <div class="panel-heading">
            Create
        </div>
        
        <div class="panel-body">
            <div class="row">
                <div class="col-xs-12 form-group">
                    {!! Form::label('title', 'Title', ['class' => 'control-label']) !!}
                    {!! Form::text('title', old('title'), ['class' => 'form-control', 'placeholder' => '']) !!}
                    <p class="help-block"></p>
                    @if($errors->has('title'))
                        <p class="help-block">
                            {{ $errors->first('title') }}
                        </p>
                    @endif
                </div>
            </div>
            <div class="row">
                <div class="col-xs-12 form-group">
                    {!! Form::label('article_text', 'Article Text', ['class' => 'control-label']) !!}
                    {!! Form::textarea('article_text', old('article_text'), ['class' => 'form-control ', 'placeholder' => '']) !!}
                    <p class="help-block"></p>
                    @if($errors->has('article_text'))
                        <p class="help-block">
                            {{ $errors->first('article_text') }}
                        </p>
                    @endif
                </div>
            </div>
            <div class="row">
                <div class="col-xs-12 form-group">
                    {!! Form::label('tag', 'Tags', ['class' => 'control-label']) !!}
                    <button type="button" class="btn btn-primary btn-xs" id="selectbtn-tag">
                        Select all
                    </button>
                    <button type="button" class="btn btn-primary btn-xs" id="deselectbtn-tag">
                        Deselect all
                    </button>
                    {!! Form::select('tag[]', $tags, old('tag'), ['class' => 'form-control select2', 'multiple' => 'multiple', 'id' => 'selectall-tag' ]) !!}
                    <p class="help-block"></p>
                    @if($errors->has('tag'))
                        <p class="help-block">
                            {{ $errors->first('tag') }}
                        </p>
                    @endif
                </div>
            </div>
            
        </div>
    </div>

    {!! Form::submit('Save article', ['class' => 'btn btn-danger']) !!}
    {!! Form::close() !!}
@stop

@section('styles')
    @parent

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
@stop

@section('javascript')
    @parent

    <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
    <script src="https://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
    <script>
        $("#selectbtn-tag").click(function(){
            $("#selectall-tag > option").prop("selected","selected");
            $("#selectall-tag").trigger("change");
        });
        $("#deselectbtn-tag").click(function(){
            $("#selectall-tag > option").prop("selected","");
            $("#selectall-tag").trigger("change");
        });

        $(document).ready(function () {
            $('.select2').select2();
        });
    </script>
@stop

This is our visual result:

Now, there are quite a few places to explain here:

  1. In this article, I won’t discuss the main layout and code like @extends(‘layouts.app’) and @section(‘content’) because you may have different design and structure, it’s not the subject of this article;

  2. We use LaravelCollective/html package to build the forms with {!! Form::open() !!} and other methods;

  3. See how $tags are passed into the form, we’re getting those values from the Controller, mentioned above;

  4. Above the tags field, we have two buttons: Select all and Deselect all – their behavior in jQuery is implemented in @section(‘javascript’);

  5. We use Select2 library to make the tags searchable and visually compelling.

So here we have our create form.


Saving the data

This part is pretty simple, here’s a method from app/Http/Controllers/Admin/ArticlesController.php:

public function store(StoreArticlesRequest $request)
{
    $article = Article::create($request->all());
    $article->tag()->sync((array)$request->input('tag'));
    return redirect()->route('admin.articles.index');
}

To validate the data, we use app/Http/Requests/StoreArticlesRequest.php class:

namespace App\Http\Requests\Admin;

use Illuminate\Foundation\Http\FormRequest;

class StoreArticlesRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'tag.*' => 'exists:tags,id',
        ];
    }
}

And in the Controller, we just store the article and then use sync() method of many-to-many relationships to store the tags.


Viewing tags in the table

In the screenshot on the top you can see how tags are structured in the table. Here’s our Blade file for this. resources/views/admin/articles/index.blade.php:

<div class="panel-body table-responsive">
    <table class="table table-bordered table-striped datatable">
        <thead>
            <tr>
                <th>Title</th>
                <th>Tags</th>
                <th> </th>
            </tr>
        </thead>
        
        <tbody>
            @if (count($articles) > 0)
                @foreach ($articles as $article)
                    <tr data-entry-id="{{ $article->id }}">
                        <td field-key='title'>{{ $article->title }}</td>
                        <td field-key='tag'>
                            @foreach ($article->tag as $singleTag)
                                <span class="label label-info label-many">{{ $singleTag->name }}</span>
                            @endforeach
                        </td>
                        <td>
                            @can('article_view')
                            <a href="{{ route('admin.articles.show',[$article->id]) }}" class="btn btn-xs btn-primary">View</a>
                            @endcan
                            @can('article_edit')
                            <a href="{{ route('admin.articles.edit',[$article->id]) }}" class="btn btn-xs btn-info">Edit</a>
                            @endcan
                            @can('article_delete')
                            {!! Form::open(array(
                                'style' => 'display: inline-block;',
                                'method' => 'DELETE',
                                'onsubmit' => "return confirm('Are you sure?');",
                                'route' => ['admin.articles.destroy', $article->id])) !!}
                            {!! Form::submit('Delete', array('class' => 'btn btn-xs btn-danger')) !!}
                            {!! Form::close() !!}
                            @endcan
                        </td>
                        @endif
                    </tr>
                @endforeach
            @else
                <tr>
                    <td colspan="8">@lang('global.app_no_entries_in_table')</td>
                </tr>
            @endif
        </tbody>
    </table>
</div>

This is the most important line – we’re using Bootstrap label classes for styling:

<span class="label label-info label-many">{{ $singleTag->name }}</span>

Editing the Article

Here’s Controller code:

public function edit($id)
{
    $tags = \App\Tag::get()->pluck('name', 'id');
    $article = Article::findOrFail($id);
    return view('admin.articles.edit', compact('article', 'tags'));
}

And in resources/views/admin/articles/edit.blade.php we show tags this way:

{!! Form::label('tag', trans('global.articles.fields.tag').'', ['class' => 'control-label']) !!}
<button type="button" class="btn btn-primary btn-xs" id="selectbtn-tag">
    {{ trans('global.app_select_all') }}
</button>
<button type="button" class="btn btn-primary btn-xs" id="deselectbtn-tag">
    {{ trans('global.app_deselect_all') }}
</button>
{!! Form::select('tag[]', $tags, old('tag') ? old('tag') : $article->tag->pluck('id')->toArray(), ['class' => 'form-control select2', 'multiple' => 'multiple', 'id' => 'selectall-tag' ]) !!}
<p class="help-block"></p>
@if($errors->has('tag'))
    <p class="help-block">
        {{ $errors->first('tag') }}
    </p>
@endif

As you can see, we pull in the current value as $article->tag->pluck(‘id’)->toArray().

Updating the data is straightforward, here’s the controller method, really similar to store():

public function update(CreateArticlesRequest $request, $id)
{
    $article = Article::findOrFail($id);
    $article->update($request->all());
    $article->tag()->sync((array)$request->input('tag'));
    return redirect()->route('admin.articles.index');
}

Deleting the data

This is probably the most simple code – here’s Controller method:

public function destroy($id)
{
    $article = Article::findOrFail($id);
    $article->delete();
    return redirect()->route('admin.articles.index');
}

But keep in mind that in our migrations files we specified that we need to delete tags on deleting the article, with this rule onDelete(‘cascade’):

Schema::create('article_tag', function (Blueprint $table) {
    $table->integer('article_id')->unsigned()->nullable();
    $table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade');
    $table->integer('tag_id')->unsigned()->nullable();
    $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
});

Basically, that’s it – a simple demo of many-to-many with Select2 and Bootstrap. If you want it to be generated automatically, try our QuickAdminPanel!

Last updated