Coding snippets for implementation of mini blog with migrations, models, relations, controllers, routes and view files

This post will highlight all the essentials bare bone coding of laravel implementation of mini blog from migrations, models, relations, controllers, routes to view files.

Suppose there will be following tables for this blog project.

1. users
2. articles
3. categories
4. tags
5. article_category
6. article_tag

then if we aim for migration schema of above tablesĀ  following snippets would need to be implemented

1. user

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('email')->unique();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
        $table->softDeletes();
    });
}

2. articles

public function up()
{
    Schema::create('articles', function (Blueprint $table) {
        $table->increments('id');
        $table->foreignId('user_id')->constrained();
        $table->string('title');
        $table->text('article_text');
        $table->timestamps();
        $table->softDeletes();
    });
}

3. categories

public function up()
{
    Schema::create('categories', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->timestamps();
        $table->softDeletes();
    });
}

4. tags

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

5. article_category ( pivot table )

public function up()
{
    Schema::create('article_category', function (Blueprint $table) {
        $table->id();
        $table->foreignId('article_id')->constrained();
        $table->foreignId('category_id')->constrained();
        $table->timestamps();
        $table->softDeletes();        
    });
}

6. article_tag ( pivot table )

public function up()
{
    Schema::create('article_tag', function (Blueprint $table) {
        $table->id();
        $table->foreignId('article_id')->constrained();
        $table->foreignId('tag_id')->constrained();
        $table->timestamps();
        $table->softDeletes();        
    });
}


After migration here are quick snippets for setting up relationship among these tables in respective model files

1. Article.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Article extends Model
{
    use SoftDeletes;

    protected $fillable = ['user_id', 'title', 'article_text'];

    public function author()
    {
        return $this->belongsTo(User::class);
    }

    public function categories()
    {
        return $this->belongsToMany(Category::class);
    }

    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
    
    public function getCategoriesLinksAttribute()
    {
        $categories = $this->categories()->get()->map(function($category) {
            return '.route('articles.index').'?category_id='.$category->id.'">'.$category->name.'';
        })->implode(' | ');

        if ($categories == '') return 'none';

        return $categories;
    }

    public function getTagsLinksAttribute()
    {
        $tags = $this->tags()->get()->map(function($tag) {
            return '.route('articles.index').'?tag_id='.$tag->id.'">'.$tag->name.'';
        })->implode(' | ');

        if ($tags == '') return 'none';

        return $tags;
    }    

}

2. Category.php


namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Category extends Model
{
    use SoftDeletes;

    protected $fillable = ['name'];

    public function articles()
    {
        return $this->belongsToMany(Article::class);
    }

}
?>

3. Tag.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Tag extends Model
{
    use SoftDeletes;

    protected $fillable = ['name'];

    public function articles()
    {
        return $this->belongsToMany(Article::class);
    }

}


After setting up relationship its time to give focus on controller particularly on ArticleController.php with following methods
for listing, creating and showing articles in blade views

public function index()
{
    $articles = Article::with(['categories', 'tags', 'author'])
        ->orderBy('id', 'desc')
        ->paginate(3);
    $all_categories = Category::all();
    $all_tags = Tag::all();
    return view('articles.index', compact('articles', 'all_categories', 'all_tags'));
}

public function create()
{
    $categories = Category::orderBy('name')->get();
    return view('articles.create', compact('categories'));
}

public function store(StoreArticleRequest $request)
{
    $article = Article::create($request->all() + ['user_id' => auth()->id()]);

    if (isset($request->categories)) {
        $article->categories()->attach($request->categories);
    }

    if ($request->tags != '') {
        $tags = explode(',', $request->tags);
        foreach ($tags as $tag_name) {
            $tag = Tag::firstOrCreate(['name' => $tag_name]);
            $article->tags()->attach($tag);
        }
    }

    return redirect()->route('articles.index');
}

public function show(Article $article)
{
    $article->load(['categories', 'tags', 'author']);
    $all_categories = Category::all();
    $all_tags = Tag::all();

    return view('articles.show', compact('article', 'all_categories', 'all_tags'));
}


After controller lets give focus on views index.blade.php, create.blade.php and show.blade.php respectively

1. index.blade.php

@forelse ($articles as $article)
    <a href="{{ route('articles.show', $article->id) }}"><h2>{{ $article->title }}</h2></a>
    <b>Author:</b> {{ $article->author->name }}
    <b>Categories:</b>{!! $article->categories_links !!} // this customly built model attribute created in Article.php model with laravel accessor method getCategoriesLinksAttribute()
    <b>Tags:</b>{!! $article->tags_links !!}     // this is also customly built model attribute created in Article.php model with laravel accessor method getTagsLinksAttribute()
    <p>{{ substr($article->article_text, 0, 200) }}...<a href="{{ route('articles.show', $article->id) }}">Read full article</a></p>    
@empty
    No articles yet.
@endforelse    

2. create.blade.php

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

Categories:
<br />
@foreach ($categories as $category)
    <input type="checkbox" name="categories[]" value="{{ $category->id }}" /> {{ $category->name }}
    <br />
@endforeach

Tags (comma-separated):
<input type="text" name="tags" class="form-control" />
<br />

3. show.blade.php


<b>Author:</b> {{ $article->author->name }}
<b>Categories:</b>{!! $article->categories_links !!} // this customly built model attribute created in Article.php model with laravel accessor method getCategoriesLinksAttribute()
<b>Tags:</b>{!! $article->tags_links !!}     // this is also customly built model attribute created in Article.php model with laravel accessor method getTagsLinksAttribute()
<p>{!! nl2br($article->article_text) !!}</p>
?>


Lastly, setting up routes for ArticleContoller.php

Route::resource('articles', \App\Http\Controllers\ArticleController::class);


For advanced step if we need to search articles with category, tag or text we can modify the index() method of ArticleController.php like below

public function index()
{
    $articles = Article::with(['categories', 'tags', 'author'])
        ->when(request('category_id'), function($query) {
            return $query->whereHas('categories', function($q) {
                return $q->where('id', request('category_id'));
            });
        })
        ->when(request('tag_id'), function($query) {
            return $query->whereHas('tags', function($q) {
                return $q->where('id', request('tag_id'));
            });
        })
        ->when(request('query'), function($query) {
            return $query->where('title', 'like', '%'.request('query').'%');
        })
        ->orderBy('id', 'desc')
        ->paginate(3);
    $all_categories = Category::all();
    $all_tags = Tag::all();
    return view('articles.index', compact('articles', 'all_categories', 'all_tags'));
}

Above eloquent when() method is used instead of if else for checking query string existence and querying Article model.

Related Posts