Laravel 5 Blog Tutorial
Introduction
Allow me to start this tutorial with a business perspective then jump into the technical aspect of it.Why should you allow me to do this?
Because we develop technical tools that add value to the business of our clients. That is what clients pay us for. Successful web developers develop to add business value and not to show technical expertise when working with a particular technology. This can only happen if you understand the business value of whatever you are developing.
Almost any business that has an online presence needs to engage in content marketing. The contentmarketinginstitute.com defines content marketing as “Content marketing is a strategic marketing approach focused on creating and distributing valuable, relevant, and consistent content to attract and retain a clearly-defined audience — and, ultimately, to drive profitable customer action.”
Let me explain this in a layman’s language, let’s say you developed Larashop to sell dresses online.
How do you stand out and beat your competitors? What strategies can you use to rank in search engine results?
You need to establish yourself as an expert in the fashion industry. You can write content that advises women what to wear on a first date and then recommend your dresses to them. This increases the chances of them buying from you. To the owner of Larashop, this is business value.
How do you go about implementing this for Larashop?
You can use a blog to publish valuable, relevant, and consistent content.
Blogs can also be used to publish video, audio, pictures, or written content etc.
Topics to be covered
We will cover the following topics in this tutorial- Tutorial Pre-requisites
- Features of a success blog
- Post
- Categories
- Tags
- Related Posts
- Comments
- User experience
- Newsletter
- Technical SEO factors
- Post Title
- Meta Description
- Role of Social Media
- Larashop Blog Database Migrations
- Tutorial Project Larashop Blog Implementation
Tutorial pre-requisites
This tutorial assumes;- You understand the basics of Laravel
- You have PHP, MySQL and Apache up and running
- You have composer installed
- You have a text editor or IDE that supports PHP.
- You have a modern web browser that supports HTML5
- You have been following the tutorial series for Larashop. You can still create a new Laravel project and follow the tutorial but you will gain more if you do other tutorials in the series first.
Features of a successful blog
Let’s now discuss the features that we will implement for Larashop blogPost
This is the main content of the blog. The information that will be published. The information will be retrieved from the database and it will be formatted using HTML tags.Categories
Categories are used to group related posts together. For example, you can have a category dedicated to women’s wear, another for men and another for kids.Tags
Tags are similar to categories but they are more specialized. You can create tags for things like dating and tag all properties that related to dating regardless of the category. Users can browse by category and get all the items that match the specified tag i.e. datingRelated Posts
Related posts are used to show posts that are similar to the one that the reader is reading. Related posts can be displayed based on the category or post tags.Comments
Comments allow you to get feedback from the readers. Comments are usually displayed at the end of the post. You can have a custom comment system or you can choose a third party system such as Disqus.User experience
The user experience considers factors such as responsiveness of the design. Can the blog properly display on a mobile device? Is it easy to navigate the contents of the blog? How fast is the blog load time etc.?Newsletter
In content marketing, customer retention of the major factors. Most blogs will have newsletter subscriptions that allow readers to subscribe. The subscription email address is used to send updates to subscribers whenever new content is published. You can either implement a custom newsletter or you can use a third party such as Mail chimp. The popular choice is using third parties. Third parties have a high delivery rate compared to custom ones.Technical SEO factors
We do not want our blog to be number 1,678,901 in search results. We should aim for the first page, anything beyond the fourth page is bad for you.Read the tutorial Laravel SEO Friendly URLs to understand how you can implement technical tools that help with search engine optimization.
Our blog implementation must provide the following database fields / features
- Post Title – This should be limited to about 56 characters. It is the title that will be displayed in search results.
- Meta Description – This should be limited to about 160 characters. It is the description that is displayed in search results.
- Role of Social Media – Social media metrics are used to determine the value of content in search engine algorithms. The more social shares a post gets the better it will be ranked. You need to make it easier for visitors to share your content.
Larashop Blog Database Migrations
Now that we have complete Blog Basics 101, let’s get our hands dirty. The following tables show the database tables that we will need to create for our blogCommon Fields to all Tables
S/N | FIELD | DATA TYPE | DESCRIPTION |
---|---|---|---|
1 | created_at | Timestamp | Timestamp when record was created |
2 | updated_at | Timestamp | Timestamp when record was last updated |
S/N | FIELD | DATA TYPE | DESCRIPTION |
---|---|---|---|
1 | id | INT | Primary key |
2 | category | VARCHAR | Category Name |
S/N | FIELD | DATA TYPE | DESCRIPTION |
---|---|---|---|
1 | id | INT | Primary key |
2 | tag | VARCHAR | Tag Name |
S/N | FIELD | DATA TYPE | DESCRIPTION |
---|---|---|---|
1 | id | INT | Primary key |
2 | post_id | INT | Foreign key |
3 | tag_id | INT | Foreign key |
S/N | FIELD | DATA TYPE | DESCRIPTION |
---|---|---|---|
1 | id | INT | Primary key |
2 | url | Varchar(255) | Page URL |
3 | title | Varchar(140) | Page title |
4 | description | Varchar(170) | Description that shows in search engine results |
5 | content | Text | The content of the page or blog post. |
6 | blog | Tinyint(1) | Determines if a post is a page is blog |
7 | category_id | INT | Foreign key |
8 | image | Varchar(255) | Blog post image |
I have XAMPP installed on windows on drive C and Larashop is in larashop directory
Open the command prompt / terminal
Run the following command to browse to the project root.
cd "C:\xampp\htdocs\larashop"
Run the following commands
php artisan make:migration add_category_id_image_to_posts --table=posts
/database/migrations/xxxx_xx_xx_xxxxxx_ add_category_id_image_to_posts.php
Modify the code to the following
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddCategoryIdImageToPosts extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('posts', function (Blueprint $table) {
$table->string('image')->nullable()->after('content');
$table->unsignedInteger('category_id')->nullable()->after('blog');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('posts', function (Blueprint $table) {
$table->dropColumn('image');
$table->dropColumn('category_id');
});
}
}
$table->string('image')->nullable()->after('content');
adds a string fieldimage
after the columncontent
. The new column can accept null values.$table->unsignedInteger('category_id')->nullable()->after('blog');
adds an unsigned integercategory_id
after the columnblog
. The new column can accept null values.
php artisan migrate
Let’s now create the migration file for
blog_categories
. Our migration file will also seed records to the new database table.Run the following artisan command
php artisan make:migration blog_categories
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class BlogCategories extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('blog_categories', function (Blueprint $table) {
$table->increments('id');
$table->string('category')->unique();
$table->timestamps();
});
DB::table('blog_categories')->insert([
'category' => "WOMEN"
]);
DB::table('blog_categories')->insert([
'category' => "MEN"
]);
DB::table('blog_categories')->insert([
'category' => "KIDS"
]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('blog_categories');
}
}
Run the following artisan command
php artisan make:migration blog_tags
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class BlogTags extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('blog_tags', function (Blueprint $table) {
$table->increments('id');
$table->string('tag')->unique();
$table->timestamps();
});
DB::table('blog_tags')->insert([
'tag' => "Pink"
]);
DB::table('blog_tags')->insert([
'tag' => "T-Shirt"
]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('blog_tags');
}
}
blog_post_tags
tableRun the following artisan command
1
php artisan make:migration blog_post_tags
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class BlogPostTags extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('blog_post_tags', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('post_id');
$table->unsignedInteger('tag_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('blog_post_tags');
}
}
php artisan migrate
Larashop Blog Dummy Records
We will use faker library to add dummy blog posts to our database. If you are not familiar with Faker library then I recommend you read this tutorial.Run the following command to create a blog posts seed file
1
php artisan make:seeder BlogPostsTableSeeder
/database/seeds/BlogPostsTableSeeder.php
Update the code to the following
<?php
use Illuminate\Database\Seeder;
class BlogPostsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$faker = Faker\Factory::create();
for ($i = 0; $i < 10; $i++){
DB::table('posts')->insert([ //,
'url' => $faker->unique()->word,
'title' => $faker->unique()->sentence($nbWords = 6),
'description' => $faker->paragraph($nbSentences = 3),
'content' => $faker->text,
'image' => $faker->randomElement($array = array ('blog-one.jpg','blog-two.jpg','blog-three.jpg')),
'blog' => '1',
'category_id' => $faker->numberBetween($min = 1, $max = 3),
]);
}
}
}
'url' => $faker->unique()->word,
generates a unique random word to be used as the post URL. using words will boost our search engine optimization efforts compared to using a post id such as 2.'image' => $faker->randomElement($array = array ('blog-one.jpg','blog-two.jpg','blog-three.jpg')),
we are picking a random image from the provided options. We only have three images for now that we will use for testing purposes.'category_id' => $faker->numberBetween($min = 1, $max = 3),
we only created three categories so we will restrict the category id to numbers 1,2 and 3.'created_at' => $faker->dateTime($max = 'now'),
created_at
field will be used to display the blog post date.
php artisan db:seed --class=BlogPostsTableSeeder
blog_post_tags
is used to link blog posts to tagsRun the following command to create the seed file
php artisan make:seeder BlogPostTagsTableSeeder
/database/seeds/BlogPostTagsTableSeeder.php
Modify the code to the following
<?php
use Illuminate\Database\Seeder;
class BlogPostTagsTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$faker = Faker\Factory::create();
for ($i = 1; $i < 11; $i++){
DB::table('blog_post_tags')->insert([ //,
'post_id' => $i,
'tag_id' => $faker->numberBetween($min = 1, $max = 2),
]);
}
}
}
'post_id' => $i,
we seeded only 10 records and our loop will be executed 10 times. We will use the loop counter variable for the post id.'tag_id' => $faker->numberBetween($min = 1, $max = 2),
we only seeded two tags. This line generates a random number between 1 and 2 to match the tag ids that we have in our database.
php artisan db:seed --class=BlogPostTagsTableSeeder
Blog Models
Let’s now create models for our blog. We will create the following models;Post
– this model will be responsible for interacting with theposts
tableBlogCategory
– this model will be responsible for interacting with theblog_categories
table.BlogTag
– this model will be responsible for interacting with theblog_tags
table.BlogPostTag
– this model will be responsible for interacting with theblog_post_tags
table
php artisan make:model Post
php artisan make:model BlogCategory
php artisan make:model BlogTag
php artisan make:model BlogPostTag
Blog Post Model
Our blog posts will have URL for the previous and next posts. We will use the post id to determine the previous and next post. Towards that end, we will create two functions prevBlogPostURL and nextBlogPostURL.We will use Eloquent ORM’s where clause and first function to get the previous and next URLs. The where clause for previous URL will use less than comparison operator and order the results in descending order. The where clause for next UR: will use greater than comparison operator and order the results on ascending order.
Blog post tags
The relationship between the blog post and the blog tags is many to many. A single post can have more than tag and a single tag can belong to more than one post. This relationship is implemented by introducing an intermediate table that links posts andblog_tags
tables.We will use Eloquent ORM’s belongsToMany function to define the relationship in Post model.
Open
/app/Post.php
Modify the code to the following.
<
?php namespace App; class Post extends BaseModel { protected $fillable = array('url', 'title', 'description', 'content', 'image', 'blog', 'category_id'); public static function prevBlogPostUrl($id) { $blog = static::where('id', '<', $id)->orderBy('id', 'desc')->first(); return $blog ? $blog->url : '#'; } public static function nextBlogPostUrl($id) { $blog = static::where('id', '>', $id)->orderBy('id', 'asc')->first(); return $blog ? $blog->url : '#'; } public function tags() { return $this->belongsToMany('App\BlogTag','blog_post_tags','post_id','tag_id'); } }
$blog = static::where('id', '<', $id)->orderBy('id', 'desc')->first();
builds the SQL statement similar to SELECT * FROM posts where id < 3 order by id limit 1 ; we are ordering the result using the id in an descending order so that we can get the lowest number just after 3. This will give us a single record with the id 2.$this->belongsToMany('App\BlogTag','blog_post_tags','post_id','tag_id');
defines a many to many relationship between posts and blog_tags using the intermediate tableblog_post_tags
.
Blog Category Model
For now, this model does not need any special business logic.Open
/app/BlogCategory.php
Modify the code to the following
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BlogCategory extends Model
{
protected $fillable = array('category');
}
Blog Tag Model
This model has a many to many relationship with Post model. We will add a function posts that defines the relationship between the two tables.Open
/app/BlogTag.php
Modify the code to the following
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class BlogTag extends Model {
protected $fillable = array('tag');
public function posts() {
return $this->belongsToMany('App\Post','blog_post_tags','post_id','tag_id');
}
}
Blog Post Tag Model
This is the model for the intermediate tableblog_post_tags
. It does not contain any special logic for now.Open /app/BlogPostTag.php
Modify the code to the following
<?php
namespace App;
class BlogPostTag extends BaseModel {
protected $fillable = array('post_id', 'tag_id');
}
Blog Controller functions
For the sake of simplicity, we are working with a single controller/app/Http/Controllers/Front.php
and it has two functions for our blog namely blog and blog_post.Add the following line to import the Post model
use App\Post;
Blog controller function
This function is used to display the blog page and list all the postings that we have. Let’s say the client asks us to display only 3 posts per page. We can use pagination to limit the posts that we display. Eloquent ORM supports pagination.Update the code for the function blog to the following
public function blog() {
$posts = Post::where('id', '>', 0)->paginate(3);
$posts->setPath('blog');
$data['posts'] = $posts;
return view('blog', array('data' => $data, 'title' => 'Latest Blog Posts', 'description' => '', 'page' => 'blog', 'brands' => $this->brands, 'categories' => $this->categories, 'products' => $this->products));
}
$posts = Post::where('id', '>', 0)->paginate(3);
paginates the returned results to three per page and assigns the result to the variable post.$posts->setPath('blog');
sets the pagination URL to base URL + blog.$data['posts'] = $posts;
assigns the result to $data array variable.
Blog_post controller function
This function accepts a URL as a parameter and retrieves a single post based on the supplied URL value.Modify the code to the following
public function blog_post($url) {
$post = Post::whereUrl($url)->first();
$tags = $post->tags;
$prev_url = Post::prevBlogPostUrl($post->id);
$next_url = Post::nextBlogPostUrl($post->id);
$title = $post->title;
$description = $post->description;
$page = 'blog';
$brands = $this->brands;
$categories = $this->categories;
$products = $this->products;
$data = compact('prev_url', 'next_url', 'tags', 'post', 'title', 'description', 'page', 'brands', 'categories', 'products');
return view('blog_post', $data);
}
Blog and Blog Post views
For the sake of brevity, we will not include the code for the views. Download the attached tutorial files to get the code for the views. The code is in/resources/views/blog.blade.php
and /resources/views/blog_post.blade.php
Use the comments section to ask if you have any questions regarding the code in the views.