├── .travis.yml
├── LICENSE.md
├── README.md
├── composer.json
├── phpunit.xml
├── src
├── Controllers
│ ├── CommentController.php
│ └── LikeController.php
├── Facade
│ └── LaravelLikeComment.php
├── LikeCommentServiceProvider.php
├── Models
│ ├── Comment.php
│ ├── Like.php
│ └── TotalLike.php
├── config
│ └── laravelLikeComment.php
├── migrations
│ ├── 2016_08_17_203844_create_laravellikecommet_comments_table.php
│ ├── 2016_08_18_152344_create_laravellikecomment_total_likes_table.php
│ └── 2016_08_18_152401_create_laravellikecomment_likes_table.php
├── public
│ └── assets
│ │ ├── css
│ │ └── style.css
│ │ └── js
│ │ └── script.js
├── routes.php
└── views
│ ├── comment.blade.php
│ └── like.blade.php
└── tests
├── PackageTestCase
└── Test.php
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 7.0
5 |
6 | env:
7 | - LARAVEL_VERSION=5.5.*
8 |
9 | matrix:
10 | fast_finish: true
11 |
12 | before_script:
13 | - travis_retry composer self-update
14 | - travis_retry composer install --prefer-source --no-interaction
15 | - if [ "$LARAVEL_VERSION" != "" ]; then composer require --dev "laravel/laravel:${LARAVEL_VERSION}" --no-update; fi;
16 | - composer update
17 |
18 | script:
19 | - phpunit
20 |
21 | notifications:
22 | email: false
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Risul Islam risul321@gmail.com
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://packagist.org/packages/risul/laravel-like-comment)
2 | [](https://packagist.org/packages/risul/laravel-like-comment)
3 | [](https://packagist.org/packages/risul/laravel-like-comment)
4 |
5 | Laravel like comment
6 | =====================
7 |
8 | Laravel Like Comment is a lightweight and developer-friendly package that makes it easy to add "like" and "comment" features to any Laravel model. Whether you're building a blog, forum, or social platform, this package handles the heavy lifting so you can focus on your core application.
9 |
10 | It supports polymorphic relationships out of the box, allowing you to attach likes and comments to multiple models effortlessly. With clean API methods and customizable migrations, it's built to be both powerful and flexible.
11 |
12 | Perfect for projects that need quick social interaction features without reinventing the wheel.
13 |
14 | ## Features
15 | * Like
16 | * Dislike
17 | * Comment
18 | * Comment voting
19 | * User avatar in comment
20 |
21 | ## Demo
22 | [Try it](http://risul.herokuapp.com/laravel-like-comment)
23 |
24 | [Watch](https://www.youtube.com/watch?v=06kcpsnN-bo)
25 |
26 | ## Installation
27 |
28 | Run
29 | ```bash
30 | composer require risul/laravel-like-comment
31 | ```
32 |
33 | ## Configuration
34 | Add
35 | ```
36 | risul\LaravelLikeComment\LikeCommentServiceProvider::class
37 | ```
38 | in your ```service providerr``` list.
39 |
40 | To copy views and migrations run
41 | ```
42 | php artisan vendor:publish
43 | ```
44 |
45 | Run
46 | ```
47 | php artisan migrate
48 | ```
49 | It will create like and comment table.
50 |
51 | Add this style links to your view head
52 | ```html
53 |
54 |
55 |
56 |
57 |
58 | ```
59 |
60 | Add jquery and script
61 | ```html
62 |
63 |
64 | ```
65 |
66 |
67 | In `config/laravelLikeComment.php` add user model path
68 | ```php
69 | 'userModel' => 'App\User'
70 | ```
71 |
72 | Add following code in your user model
73 | ```php
74 | /**
75 | * Return the user attributes.
76 |
77 | * @return array
78 | */
79 | public static function getAuthor($id)
80 | {
81 | $user = self::find($id);
82 | return [
83 | 'id' => $user->id,
84 | 'name' => $user->name,
85 | 'email' => $user->email,
86 | 'url' => '', // Optional
87 | 'avatar' => 'gravatar', // Default avatar
88 | 'admin' => $user->role === 'admin', // bool
89 | ];
90 | }
91 | ```
92 |
93 | ## Usage
94 | Add this line at where you want to integrate Like
95 | ```php
96 | @include('laravelLikeComment::like', ['like_item_id' => 'image_31'])
97 | ```
98 | `like_item_id:` This is the id of the item,page or model for which the like will be used
99 |
100 | Add this line where you want to integrate Comment
101 | ```php
102 | @include('laravelLikeComment::comment', ['comment_item_id' => 'video_12'])
103 | ```
104 | ```comment_item_id:``` This is the id of the item, page, or model for which the comment will be used
105 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "risul/laravel-like-comment",
3 | "description": "Ajax based site wide like and comment system for laravel",
4 | "type": "library",
5 | "license": "MIT",
6 | "keywords": ["laravel", "like", "comment", "ajax"],
7 | "authors": [
8 | {
9 | "name": "risul",
10 | "email": "risul321@gmail.com"
11 | }
12 | ],
13 | "minimum-stability": "stable",
14 | "require": {},
15 | "autoload": {
16 | "psr-4": {
17 | "risul\\LaravelLikeComment\\": "src/"
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests
16 |
17 |
18 |
19 |
20 |
21 | ./app
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/Controllers/CommentController.php:
--------------------------------------------------------------------------------
1 | id;
31 | $parent = $request->parent;
32 | $commentBody = $request->comment;
33 | $itemId = $request->item_id;
34 |
35 | $user = self::getUser($userId);
36 | $userPic = $user['avatar'];
37 | if($userPic == 'gravatar'){
38 | $hash = md5(strtolower(trim($user['email'])));
39 | $userPic = "http://www.gravatar.com/avatar/$hash?d=identicon";
40 | }
41 |
42 | $comment = new Comment;
43 | $comment->user_id = $userId;
44 | $comment->parent_id = $parent;
45 | $comment->item_id = $itemId;
46 | $comment->comment = $commentBody;
47 |
48 | $comment->save();
49 |
50 | $id = $comment->id;
51 | return response()->json(['flag' => 1, 'id' => $id, 'comment' => $commentBody, 'item_id' => $itemId, 'userName' => $user['name'], 'userPic' => $userPic]);
52 | // dd($comment);
53 | }
54 |
55 | /**
56 | * undocumented function
57 | *
58 | * @return void
59 | * @author
60 | **/
61 | public static function viewLike($id){
62 | echo view('laravelLikeComment::like')
63 | ->with('like_item_id', $id);
64 | }
65 |
66 | /**
67 | * undocumented function
68 | *
69 | * @return void
70 | * @author
71 | **/
72 | public static function getComments($itemId){
73 | $comments = Comment::where('item_id', $itemId)->orderBy('parent_id', 'asc')->get();
74 |
75 | foreach ($comments as $comment){
76 | $userId = $comment->user_id;
77 | $user = self::getUser($userId);
78 | $comment->name = $user['name'];
79 | $comment->email = $user['email'];
80 | $comment->url = $user['url'];
81 |
82 | if($user['avatar'] == 'gravatar'){
83 | $hash = md5(strtolower(trim($user['email'])));
84 | $comment->avatar = "http://www.gravatar.com/avatar/$hash?d=identicon";
85 | } else {
86 | $comment->avatar = $user['avatar'];
87 | }
88 | }
89 |
90 | return $comments;
91 | }
92 |
93 | /**
94 | * undocumented function
95 | *
96 | * @return void
97 | * @author
98 | **/
99 | public static function getUser($userId){
100 | $userModel = config('laravelLikeComment.userModel');
101 | return $userModel::getAuthor($userId);
102 | }
103 |
104 | /**
105 | * Delete Comment
106 | *
107 | * @return json []
108 | * @author risul3
109 | **/
110 | public function delete($id)
111 | {
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/Controllers/LikeController.php:
--------------------------------------------------------------------------------
1 | json(['flag' => 0]);
36 | }
37 |
38 | /* Prepare data */
39 | $userId = Auth::user()->id;
40 | $itemId = $request->item_id;
41 | $vote = $request->vote;
42 |
43 | /* Get item's current like, dislike total */
44 | $totalLike = TotalLike::where('item_id', $itemId)->first();
45 |
46 | /* Get user's vote on this item */
47 | $like = Like::where(['user_id' => $userId, 'item_id' => $itemId])->first();
48 |
49 | /* Check if users vote on this item exists */
50 | if ($like != null) {
51 | if ($like->vote == 1) { // if previous vote was like
52 | $totalLike->total_like--; // decrease item's total like by 1
53 | $totalLike->total_like = $totalLike->total_like == null ? 0 : $totalLike->total_like; // set 0 if total like is null
54 | if ($vote == 1) { // if current vote is like
55 | $vote = 0; // previous vote and current vote is same so discarde vote
56 | }
57 | } else if ($like->vote == -1) { // if previous vote was dislike
58 | $totalLike->total_dislike--; // decrease item's total like by 1
59 | $totalLike->total_dislike = $totalLike->total_dislike == null ? 0 : $totalLike->total_dislike; // set 0 if total dislike is null
60 | if ($vote == -1) { // if current vote is dislike
61 | $vote = 0; // previous vote and current vote is same so discarde vote
62 | }
63 | }
64 | } else {
65 | $like = new Like; // create new like object if previous vote not exists
66 | }
67 |
68 | /* Update vote data */
69 | $like->user_id = $userId;
70 | $like->item_id = $itemId;
71 | $like->vote = $vote;
72 |
73 | if ($vote == 1) {
74 | $totalLike->total_like++; // increase total like if vote is like
75 | } else if ($vote == -1) {
76 | $totalLike->total_dislike++; // increase total dislike if vote is dislike
77 | }
78 |
79 | $like->save(); // save like
80 | $totalLike->save(); //save total like,dislike
81 |
82 | return response()->json(['flag' => 1, 'vote' => $vote, 'totalLike' => $totalLike->total_like, 'totalDislike' => $totalLike->total_dislike]);
83 | }
84 |
85 | /**
86 | * undocumented function
87 | *
88 | * @return void
89 | * @author
90 | **/
91 | public static function getLikeViewData($itemId)
92 | {
93 | $totalCount = TotalLike::where('item_id', $itemId)->first();
94 | if($totalCount == NULL) {
95 | $totalCount = new TotalLike;
96 | $totalCount->item_id = $itemId;
97 | $totalCount->total_like = 0;
98 | $totalCount->total_dislike = 0;
99 |
100 | $totalCount->save();
101 | }
102 |
103 | $yourVote = 0; // 0 = Not voted, 1 = Liked, -1 = Disliked
104 | if (Auth::check()) {
105 | $checkYourVote = \risul\LaravelLikeComment\Models\Like::where([
106 | 'user_id' => Auth::user()->id,
107 | 'item_id' => $itemId
108 | ])->first();
109 | if ($checkYourVote != NULL) {
110 | $yourVote = $checkYourVote->vote;
111 | }
112 | }
113 |
114 | $likeDisabled = "";
115 | if (!Auth::check()) {
116 | $likeDisabled = "disabled";
117 | }
118 |
119 | $likeIconOutlined = $yourVote == 1 ? "" : "outline";
120 | $dislikeIconOutlined = $yourVote == -1 ? "" : "outline";
121 |
122 | return [
123 | $itemId.'likeDisabled' => $likeDisabled,
124 | $itemId.'likeIconOutlined' => $likeIconOutlined,
125 | $itemId.'dislikeIconOutlined' => $dislikeIconOutlined,
126 | $itemId.'total_like' => $totalCount->total_like,
127 | $itemId.'total_dislike' => $totalCount->total_dislike,
128 | ];
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/Facade/LaravelLikeComment.php:
--------------------------------------------------------------------------------
1 | publishes([
17 | __DIR__.'/views' => resource_path('views/vendor/laravelLikeComment'),
18 | __DIR__.'/migrations' => database_path('migrations'),
19 | __DIR__.'/public/assets' => public_path('vendor/laravelLikeComment'),
20 | __DIR__.'/config/laravelLikeComment.php' => config_path('laravelLikeComment.php')
21 | ]);
22 | }
23 |
24 | /**
25 | * Register the application services.
26 | *
27 | * @return void
28 | */
29 | public function register()
30 | {
31 | // Route
32 | include __DIR__.'/routes.php';
33 |
34 | $this->app->singleton('LaravelLikeComment', function ($app) { return new LaravelLikeComment; });
35 |
36 | // Config
37 | $this->mergeConfigFrom( __DIR__.'/config/laravelLikeComment.php', 'laravelLikeComment');
38 |
39 | // View
40 | $this->loadViewsFrom(__DIR__.'/views', 'laravelLikeComment');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Models/Comment.php:
--------------------------------------------------------------------------------
1 | 'App\User'
4 | ];
5 |
--------------------------------------------------------------------------------
/src/migrations/2016_08_17_203844_create_laravellikecommet_comments_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->integer('user_id');
18 | $table->integer('parent_id');
19 | $table->string('item_id'); // ModelName_modelId
20 | $table->string('comment');
21 | $table->timestamps();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | *
28 | * @return void
29 | */
30 | public function down()
31 | {
32 | Schema::drop('laravellikecomment_comments');
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/migrations/2016_08_18_152344_create_laravellikecomment_total_likes_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->string('item_id'); // ModelName_modelId
18 | $table->integer('total_like');
19 | $table->integer('total_dislike');
20 | $table->timestamps();
21 | });
22 | }
23 |
24 | /**
25 | * Reverse the migrations.
26 | *
27 | * @return void
28 | */
29 | public function down()
30 | {
31 | Schema::drop('laravellikecomment_like_totals');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/migrations/2016_08_18_152401_create_laravellikecomment_likes_table.php:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->integer('user_id');
18 | $table->string('item_id'); // ModelName_modelId
19 | $table->smallInteger('vote');
20 | $table->timestamps();
21 | });
22 | }
23 |
24 | /**
25 | * Reverse the migrations.
26 | *
27 | * @return void
28 | */
29 | public function down()
30 | {
31 | Schema::drop('laravellikecomment_likes');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/public/assets/css/style.css:
--------------------------------------------------------------------------------
1 | .laravelLike-icon{
2 | cursor: pointer;
3 | }
4 | .laravelLike-icon.up{
5 | color: #27ae61;
6 | }
7 | .laravelLike-icon.down{
8 | color: #c0392b;
9 | margin-left: 10px !important;
10 | }
11 | .comment{
12 | display: none;
13 | }
--------------------------------------------------------------------------------
/src/public/assets/js/script.js:
--------------------------------------------------------------------------------
1 | $('.laravelLike-icon').on('click', function(){
2 | if($(this).hasClass('disabled'))
3 | return false;
4 |
5 | var item_id = $(this).data('item-id');
6 | var vote = $(this).data('vote');
7 |
8 | $.ajax({
9 | method: "get",
10 | url: "/laravellikecomment/like/vote",
11 | data: {item_id: item_id, vote: vote},
12 | dataType: "json"
13 | })
14 | .done(function(msg){
15 | if(msg.flag == 1){
16 | if(msg.vote == 1){
17 | $('#'+item_id+'-like').removeClass('outline');
18 | $('#'+item_id+'-dislike').addClass('outline');
19 | }
20 | else if(msg.vote == -1){
21 | $('#'+item_id+'-dislike').removeClass('outline');
22 | $('#'+item_id+'-like').addClass('outline');
23 | }
24 | else if(msg.vote == 0){
25 | $('#'+item_id+'-like').addClass('outline');
26 | $('#'+item_id+'-dislike').addClass('outline');
27 | }
28 | $('#'+item_id+'-total-like').text(msg.totalLike == null ? 0 : msg.totalLike);
29 | $('#'+item_id+'-total-dislike').text(msg.totalDislike == null ? 0 : msg.totalDislike);
30 | }
31 | })
32 | .fail(function(msg){
33 | alert(msg);
34 | });
35 | });
36 |
37 |
38 | $(document).on('click', '.reply-button', function(){
39 | if($(this).hasClass("disabled"))
40 | return false;
41 | var toggle = $(this).data('toggle');
42 | $("#"+toggle).fadeToggle('normal');
43 | });
44 |
45 | $(document).on('submit', '.laravelComment-form', function(){
46 | var thisForm = $(this);
47 | var parent = $(this).data('parent');
48 | var item_id = $(this).data('item');
49 | var comment = $('textarea#'+parent+'-textarea').val();
50 |
51 | $.ajax({
52 | method: "get",
53 | url: "/laravellikecomment/comment/add",
54 | data: {parent: parent, comment: comment, item_id: item_id},
55 | dataType: "json"
56 | })
57 | .done(function(msg){
58 | $(thisForm).toggle('normal');
59 | var newComment = '
';
60 | $('#'+item_id+'-comment-'+parent).prepend(newComment);
61 | $('textarea#'+parent+'-textarea').val('');
62 | })
63 | .fail(function(msg){
64 | alert(msg);
65 | });
66 |
67 | return false;
68 | });
69 |
70 |
71 | $(document).on('click', '#showComment', function(){
72 | var show = $(this).data("show-comment");
73 | $('.show-'+$(this).data("item-id")+'-'+show).fadeIn('normal');
74 | $(this).data("show-comment", show+1);
75 | $(this).text("Show more");
76 | });
77 |
78 |
79 | $(document).on('click', '#write-comment', function(){
80 | $($(this).data("form")).show();
81 | });
82 |
83 |
--------------------------------------------------------------------------------
/src/routes.php:
--------------------------------------------------------------------------------
1 | 'risul\LaravelLikeComment\Controllers', 'prefix'=>'laravellikecomment', 'middleware' => 'web'], function (){
15 | Route::group(['middleware' => 'auth'], function (){
16 | Route::get('/like/vote', 'LikeController@vote');
17 | Route::get('/comment/add', 'CommentController@add');
18 | });
19 | });
--------------------------------------------------------------------------------
/src/views/comment.blade.php:
--------------------------------------------------------------------------------
1 |
7 |
74 |
--------------------------------------------------------------------------------
/src/views/like.blade.php:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
10 | {{ $data[$like_item_id.'total_like'] }}
11 |
15 |
16 | {{ $data[$like_item_id.'total_dislike'] }}
17 |
18 |
--------------------------------------------------------------------------------
/tests/PackageTestCase:
--------------------------------------------------------------------------------
1 | assertTrue(true);
17 | }
18 | }
--------------------------------------------------------------------------------