The Ultimate Like System For Laravel Thanks To Many to Many Polymorphic Relationships
Several months ago I wrote an article on How to create a Simple Like System For Laravel. In the comment of this article, a cool guy asked me about using a "Many to Many Polymorphic Relationship" for this "Like" logic.
I found the idea very appealing and I decided to create another article to write about this topic, and I hope to achieve the most perfect and simple Like System for any Laravel Application. The purpose is to make it so easy that you could actually include this system in each of your projects, in only a bunch of minutes.
In the first article, we created a system that allows the users of your app to "like" and "unlike" a specific entity (or model) of your application (e.g. an article, a comment, a photo, whatever..). But if you want your users to be able to like several things, you have to create other "likes" tables. On Facebook, you can like a post, a comment, a picture, etc. So let's be efficient, and create a single table that will contain every likes of all your likeable entities.
Less database tables, more efficient and more "Laravel oriented".
We will obviously keep the "soft delete" feature from the previous article, so you'll be able to only notify your users once, at the creation of a new Like.
In the example I will setup in this article, I will have products and posts, that users can like.
We only need a single table to store de likes. Of course you need one table per likeable entities (posts, products, comments, etc - whatever you actually use in your application). And you also need a "users" table.
Now let's create a "likeables" table!
First, run this command php artisan make:migration create_likeables_table --create=likeables
In your new migration file, set the up() method like this:
// migration file
public function up()
{
Schema::create('likeables', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->integer('likeable_id');
$table->string('likeable_type');
$table->softDeletes();
$table->timestamps();
});
}
You can now update your database by running the command php artisan migrate
First, create a Like model: php artisan make:model Like
and write this inside your new Like.php file
// app/Like.php
class Like extends Model
{
use SoftDeletes;
protected $table = 'likeables';
protected $fillable = [
'user_id',
'likeable_id',
'likeable_type',
];
/**
* Get all of the products that are assigned this like.
*/
public function products()
{
return $this->morphedByMany('App\Product', 'likeable');
}
/**
* Get all of the posts that are assigned this like.
*/
public function posts()
{
return $this->morphedByMany('App\Post', 'likeable');
}
}
Quick analysis:
Second, you need to update the model of the entity your users should be able to like. For our example, I have a Post model and a Product model. As I'll write the exact same thing in both of them, I give you the Post.php file only:
// app/Post.php
class Post extends Model
{
public function likes()
{
return $this->morphToMany('App\User', 'likeable')->whereDeletedAt(null);
}
public function getIsLikedAttribute()
{
$like = $this->likes()->whereUserId(Auth::id())->first();
return (!is_null($like)) ? true : false;
}
}
And that's it! From your views, you can now grab all users that have liked a post. You can also check if the current user has liked the post or not.
We are now adding a nice method in our User.php model so you can get all the posts that a specific user has liked:
// app/User.php
public function likedPosts()
{
return $this->morphedByMany('App\Post', 'likeable')->whereDeletedAt(null);
}
In order to see it all in action we still need to create a LikeController and define some routes. Here we go.
You need one new route per likeable entity. In the scope of this example, I just need to add these two routes to the routes.php file.
// app/Http/routes.php
Route::get('product/like/{id}', ['as' => 'product.like', 'uses' => 'LikeController@likeProduct']);
Route::get('post/like/{id}', ['as' => 'post.like', 'uses' => 'LikeController@likePost']);
Go ahead and create the controller with this command php artisan make:controller LikeController
Here's what you need inside it.
// app/Http/Controllers/LikeController.php
public function likePost($id)
{
// here you can check if product exists or is valid or whatever
$this->handleLike('App\Post', $id);
return redirect()->back();
}
public function handleLike($type, $id)
{
$existing_like = Like::withTrashed()->whereLikeableType($type)->whereLikeableId($id)->whereUserId(Auth::id())->first();
if (is_null($existing_like)) {
Like::create([
'user_id' => Auth::id(),
'likeable_id' => $id,
'likeable_type' => $type,
]);
} else {
if (is_null($existing_like->deleted_at)) {
$existing_like->delete();
} else {
$existing_like->restore();
}
}
}
Some notes about this:
I know that the handleLike() method is not perfect. If you have ideas or suggestions, please share in the comments. As a wise man once said : "at least it works".
Important: These routes/controller combination is actually just an example. You can use POST methods if you prefer. You can use Ajax. You can do whatever you want. I just made it very simple to make it work without any JavaScript.
// someview.blade.php
@foreach ($products as $product)
@foreach ($product->likes as $user)
{{ $user->name }} likes this !
@endforeach
...
@if ($product->isLiked)
Unlike this shit
@else
Like this awesome product!
@endif
@endforeach
And voila ! I'm done !
I really hope you like all this ! (haha)
For those who might be interested here's a working example repo of what I setup in this article: https://github.com/mydnic/Laravel-Like-System-Example
If you have any questions or suggestions, please post a comment below.
I consider myself as an IT Business Artisan. Or Consultant CTO. I'm a self-taught Web Developper, coach and teacher. My main work is helping and guiding digital startups.
more about meBTC
18SY81ejLGFuJ9KMWQu5zPrDGuR5rDiauM
ETH
0x519e0eaa9bc83018bb306880548b79fc0794cd08
XMR
895bSneY4eoZjsr2hN2CAALkUrMExHEV5Pbg8TJb6ejnMLN7js1gLAXQySqbSbfzjWHQpQhQpvFtojbkdZQZmM9qCFz7BXU
2024 © My Dynamic Production SRL All rights Reserved.