Comment module ¶
makes every entity of your application commentable. Features:
- Create, Update, Delete comments with ajax
- Gravatar support
- define multiple models that can be commented
- Events raised on new, update, delete
- more coming soon...
If there is something missing here, or you think one step should be described more detailed, please report it. Thanks!
Requirements ¶
- Tested with Yii 1.1.8, should work in earlier versions
- yii-gravatar extension for gravatar support
Resources ¶
- Found a bug or want a feature? Report it on github
- Code on github
- E-Mail the author: CeBe <mail@cebe.cc>
- demo on http://demo.cebe.cc/comment-module
Download ¶
There are two ways to get this extension working:
Clone repo:
- Go to your application baseDir (
protected
in default yii webapp). git clone https://github.com/yiiext/comment-module.git extensions/comment-module
- If your project is in a git repository you can alternatively add comment-module as a submodule like this:
git submodule add https://github.com/yiiext/comment-module.git protected/extensions/comment-module
- go to new comment-modules base dir and run
git submodule update --init
to get the gravatar extension that's included.
- Go to your application baseDir (
Download latest release and put all the files into
extensions/comment-module
under your application baseDir (protected
in default yii webapp). To be able to use Gravatar support you have to copy YiiGravatar.php intoextensions/comment-module/extensions/gravatar
.
Quickstart ¶
Add module to your application config (optional config values are commented):
<?php
// ...
'modules'=>array(
// ...
'comment'=>array(
'class'=>'ext.comment-module.CommentModule',
'commentableModels'=>array(
// define commentable Models here (key is an alias that must be lower case, value is the model class name)
'post'=>'Post'
),
// set this to the class name of the model that represents your users
'userModelClass'=>'User',
// set this to the username attribute of User model class
'userNameAttribute'=>'username',
// set this to the email attribute of User model class
'userEmailAttribute'=>'email',
// you can set controller filters that will be added to the comment controller {@see CController::filters()}
// 'controllerFilters'=>array(),
// you can set accessRules that will be added to the comment controller {@see CController::accessRules()}
// 'controllerAccessRules'=>array(),
// you can extend comment class and use your extended one, set path alias here
// 'commentModelClass'=>'comment.models.Comment',
),
// ...
),
// ...
Create database tables: You can use the database migration provieded by this extension or create a table (example for mysql):
[sql]
CREATE TABLE IF NOT EXISTS `comments` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`message` text COLLATE utf8_unicode_ci,
`userId` int(11) UNSIGNED DEFAULT NULL,
`createDate` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_comments_userId` (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
You might also want to add a foreign key for userId
column that references you user tables pk.
Create a database table for every commentable Model relation:
[sql]
CREATE TABLE IF NOT EXISTS `posts_comments_nm` (
`postId` int(11) UNSIGNED NOT NULL,
`commentId` int(11) UNSIGNED NOT NULL,
PRIMARY KEY (`postId`,`commentId`),
KEY `fk_posts_comments_comments` (`commentId`),
KEY `fk_posts_comments_posts` (`postId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
You might want to add foreign keys here too.
Add commentable behavior to all Models you want to be commented.
<?php
// ...
public function behaviors() {
return array(
'commentable' => array(
'class' => 'ext.comment-module.behaviors.CommentableBehavior',
// name of the table created in last step
'mapTable' => 'posts_comments_nm',
// name of column to related model id in mapTable
'mapRelatedColumn' => 'postId'
),
);
}
Finally add comments to your view template of the commentable model:
<h1>comments</h1>
<?php $this->renderPartial('comment.views.comment.commentList', array(
'model'=>$model
)); ?>
Extending Comment-Module ¶
Comment module raises events to which you can attach event handlers to handle them. See The Definitive Guide to Yii on how to do this.
You can also attach behaviors
to CommentModule by setting 'behaviors'=>array(/* ... */)
in the module config described above.
See CModule::behaviors on how to add behaviors to a module.
onNewComment ¶
This event is raised when a new comment has been saved.
The following attributes are available on the $event
given as the first parameter to the event handler:
$event->comment
is the ActiveRecord instance of the currently added comment.$event->commentedModel
is the model the comment was added to.
Possible use cases:
- Send an E-Mail-Notification
onUpdateComment ¶
This event is raised when a user edited a comment.
The following attributes are available on the $event
given as the first parameter to the event handler:
$event->comment
is the ActiveRecord instance of the updated comment.
onDeleteComment ¶
This event is raised when a user deleted a comment.
The following attributes are available on the $event
given as the first parameter to the event handler:
$event->comment
is the ActiveRecord instance of the deleted comment.
Changelog ¶
0.6.0 (2011-12-22) ¶
- added getCommentCount() to CommentableBehavior (cebe)
- added delete button and action (cebe)
- added update button and action (cebe)
- fixed problem with sqlite timestamp (cebe)
0.5.0 (2011-12-19) ¶
- initial public release (cebe)
Nice extension
Can a feature with star rating added. I would like to add it to my app where by users rate on items and with star rating maybe add additional column rating_value with int value.
Maybe...
There are far more important things to do, but I added it to github tracker:
https://github.com/yiiext/comment-module/issues/9
btw: better use that bugtracker for bugs and feature requests, than these comments.
New release 0.6.0
There are more great things planned for 0.7.0.
btw: I fixed corrupted download files on this page.
UserID
Nice code , it was easy to install maybe I missed a step as
UserID is getting set to zero , so the posts are showing as "Guest"
userid comes from Yii::app()->user
I am using
Yii::app()->user->id
to get the userId andYii::app()->user->isGuest
to check if the user is a guest where userId is set to null.Check if there are the right values set in Yii::app()->user, if everything is okay on your side, please file a bug on github with a little bit more details:
https://github.com/yiiext/comment-module/issues/new
yes it was me
yes it was me it works on the code where i am using the yii-user
Sorry but errors...
Thanks in advance...
But i really meet some problems. Each time AFTER i add the view's code , I got "No scope defined in CommentModule"..... What should I do then? Also would you please be more specific about the database part... For me, my users table's id is not "UNSIGNED" so i have to cancel that value in order to make a reference, will that make the "no scope" error...?
...
@jzhong5 just do the database column definition for userId as it is required for your db as long as it is an integer it does not matter if it is unsigned or not, or BIGINT or whatever.
Also make sure you have commentableModels configured in your module.
Thx!!
@CeBe It works now. Thanks so much for your amazing extensions!!!
how to get date?
I've added my own field to CTimestampBehavior behavior (createAttribute), but when I'm trying to get comment added date in onNewComment event, I've got CDBexpression instead of string date representation... How can I get the string of date?
$model->refresh helps
@p0rsche: you have to refresh the model after saving with CTimeStampBehavior like it is discussed here.
How to trigger events in this extensions...
Any hints about how to trigger events in this extensions...?
I want to send email to the admin when specific model was commented....
I tried to add some trigger codes in the Comment Controller but not works...
Any ideas??
Thanks so much!
handling events
To attach eventhandlers to modules events, you can configure a callback inside your main config, or if you have PHP 5.3 or higher you can even add an anonymous function there:
<?php // ... 'modules'=>array( // ... 'comment'=>array( 'class'=>'ext.comment-module.CommentModule', // ... 'onNewComment' => array('MyHandlerClass', 'staticMethodName'), 'onUpdatedComment' => function($event) { mail('admin@example.com', 'new comment!', $event->comment->message); }, ), // ... ), // ...
Thanks!!!!
THANKS!!
Works like a charm now...
THANKS FOR YOUR HELP!!!
How can I get the model's name?
Hi, I am using this module to send email notification and also using it in other models.
However, I just send email in one Model, so I don't want to messed things up...
I know $event->commentedModel will give me the specific values in the model...
But How can i get the model's name?
For example ,I use this ext in Questions Model, how can I get "Questions" instead of a specific id or name belongs to 'Questions'..?
Because if i can get this, I can only send email notification in the specific model....
Best,
jiaming
get_class
@jzhong5: how about get_class?
if (get_class($event->commentedModel) == 'Question') { // ... }
@Cebe
Thanks..That works!
Amazing extension...save me tons of time
Cheers
Jiaming
Multiple Model Classes Same ID ?
Might be slightly confused about this Cebe, but installed it and it's working great.
However, how is the itemview supposed to know which model the comment is related to.
For instance, let's say you have a question with an id of 1 and an answer with a id of 1.
If both models are commentable (question && answer) and each one has a comment added, then when the view searches for id of 1, won't it pull both comments for each individual model?
This is the case I am having currently. In modules that I've designed for my app that are reusable by multiple models I have had to add a column "parent_model" to store the model class that this particular item is for. So in the question/answer example it would be stored as follows:
comment_id: 1,
parent_model: Question,
parent_id: 1,
message: test,
createDate: NOW()
and
comment_id: 2,
parent_model: Answer,
parent_id: 1,
message: test2,
createDate: NOW()
Hopefully, that makes sense. Am I doing something wrong? Is this functionality already built in?
Thanks CeBe!
Table Prefix Solution
Also,
for others who are using table prefixes, I found the following was necessary in Comment.php:
/** * @return string the associated database table name */ public function tableName() { return '{{comments}}'; }
you need a relation table from comments to any model using it
@w00tw00t111 do you have a relation table created for every model that is commentable? All you have to do is described here:
https://github.com/yiiext/comment-module#quickstart
is there a way to create pagination?
is there a way to create pagination?...or the comment list is getting too long...just like the comments in this page...=.=
Re: you need a relation table from comments to any model using it
@CeBe,
I did create that table
CREATE TABLE IF NOT EXISTS `posts_comments_nm` ( `postId` int(11) UNSIGNED NOT NULL, `commentId` int(11) UNSIGNED NOT NULL, PRIMARY KEY (`postId`,`commentId`), KEY `fk_posts_comments_comments` (`commentId`), KEY `fk_posts_comments_posts` (`postId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
If that's what you were referring to, but still not sure how the module knows the "context" or owner of the post_id.
Ended up editing the above table and adding parent_model and made the appropriate changes to the module model.
Didn't know if you had built the ability into the module to begin with.
No scope defined in CommentModule for commentable Model Request
No scope defined in CommentModule for commentable Model Request
I followed your instructions. but I found the error. I changed 'userModelClass'=>'User', to 'userModelClass'=>'Users', and userId at comments tbl is int(11) unsigned null, also same as the id of Users tbl. How could I solve this problem?
RE: No Scope Error
This should get rid of your No Scope Error. All it is saying is that you don't have $comment defined. So you will have to define it...
In your main.php under the section below add your commentable models names there.
i.e. I have a models that I added this too named Products and one called Companies.
// define commentable Models here (key is an alias that must be lower case, value is the model class name) 'post'=>'Products', 'Companies' ),
Also, in the database schema I just left out the unsigned part because my user table doesn't use it and you won't be able to create a FK relationship if they aren't the same.
You should also add FKs when adding your comment tables. If you don't the comments won't delete out of the table. It will only delete out of the comment table.
Here is an example of what I used.
SET FOREIGN_KEY_CHECKS=0;//turns off foreign key checks so you don't throw an errno 150 error... DROP TABLE IF EXISTS `comments`; CREATE TABLE IF NOT EXISTS `comments` ( `id` int(11) NOT NULL AUTO_INCREMENT, `message` text COLLATE utf8_unicode_ci, `userId` int(11) DEFAULT NULL, `createDate` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `comments` (`userId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; DROP TABLE IF EXISTS `company_comments`; CREATE TABLE IF NOT EXISTS `company_comments` ( `postId` int(11) NOT NULL, `commentId` int(11) NOT NULL, PRIMARY KEY (`postId`,`commentId`), KEY `fk_posts_comments_comments` (`commentId`), KEY `fk_posts_comments_posts` (`postId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; ALTER TABLE `comments` ADD CONSTRAINT `comments` FOREIGN KEY (`userId`) REFERENCES `users` (`id`) ON UPDATE CASCADE; ALTER TABLE `company_comments` ADD CONSTRAINT `company_comments` FOREIGN KEY (`commentId`) REFERENCES `comments` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; SET FOREIGN_KEY_CHECKS=1;//turns foreign key checks back on
Changed Styles
EDIT:
There is a problem when adding/deleting comments... for example when it shows 1 of 15 comments it doesn't update until you refresh the page. Also, if you are using pagination the comment goes below the pager instead of under the last comment until you refresh the page. Anyone know fixes to these?
I changed the styles (see screen shot at the bottom) I think it looks a lot more professional. This also add an edit link to the comments if the user is admin and it is not the admin's post.
Please Note: the submit button style will change site wide you can change it to be just for the comment if you like. Also, I moved my styles to my main css file. however, if you don't do so you may have to clear the site cache to see changes (you can manually delete them by going to your webroot/assets/ (assets folder is at the same level as protected folder) and deleting everything in there.)
CSS
/*comment extension*/ div.ext-comment { background: #f7f7f7; width: 100%; margin: 15px auto; height: auto; border: 1px solid #e9e9e9; overflow: hidden; } div.commentspacer { padding: 10px; } div.ext-comment p { margin: 0px 0px 0px 100px; min-height: 77px; height: auto; background: #fff; padding: 8px; } div.ext-comment img { float: left; width: 80px; height: 80px; } span.ext-comment-name { font-weight: bold; } div.comment-head { font-weight: bold; text-transform: capitalize; padding: 5px; } span.ext-comment-options { float: right; color: #aaa; } div.comment-img { float: left; width: 80px; height: 80px; margin: auto; padding: 5px; border: 1px solid #e0e0e0; background: #fff; } .comment-head { border-bottom: 1px solid #e9e9e9; font-weight: bold; text-transform: capitalize; padding: 8px; color: #6a6a6a; } div.textfieldholder { margin: 0px 0px 0px 100px; min-height: 70px; height: auto; display: block; padding: 0px; } .commenttextfield { min-height: 77px; height: auto; background: #fff; padding: 8px; width: 98%; margin: 0px; } .commenttextfield:focus { box-shadow: 0 0 5px rgba(81, 203, 238, 1); border: 1px solid rgba(81, 203, 238, 1); } .commentbutton { float: right; margin-top: 0px; margin-right: 13px; padding: 0px 10px 10px; 10px; } #ext-comment .button, input[type="submit"] { color: #ffffff; background-color: hsl(200, 0%, 15%) !important; background-repeat: repeat-x !important; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#b5b5b5", endColorstr="#262626") !important;; background-image: -khtml-gradient(linear, left top, left bottom, from(#b5b5b5), to(#262626)) !important; background-image: -moz-linear-gradient(top, #b5b5b5, #262626) !important; background-image: -ms-linear-gradient(top, #b5b5b5, #262626) !important; background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #b5b5b5), color-stop(100%, #262626)) !important; background-image: -webkit-linear-gradient(top, #b5b5b5, #262626) !important; background-image: -o-linear-gradient(top, #b5b5b5, #262626) !important; background-image: linear-gradient(#b5b5b5, #262626) !important; border-color: #262626 #262626 hsl(200, 0%, 1%) !important; color: #fff !important; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.92) !important; -webkit-font-smoothing: antialiased !important; min-height: 0px !important; min-width: 0px !important; -moz-border-radius: 0px !important; -webkit-border-radius: 0px!important; -khtml-border-radius: 0px!important; border-radius: 0px!important; margin: 0px!important; padding: 3px!important; } #ext-comment .button, input[type="submit"]:hover { opacity:0.7; } #comments a:link { font: #fff; color: #ffffff !important; } #comments a:visited { text-decoration: none; } #comments a:hover { text-decoration: none; }
views/_form
<?php if (Yii::app()->user->isGuest) { ?><div class="ext-comment-not-loggedin"> Sorry, you have to login to leave a comment. </div><?php } else { ?> <div id="ext-comment-form-<?php echo $comment->isNewRecord ? 'new' : 'edit-'.$comment->id; ?>" class="form"> <?php $form = $this->beginWidget('CActiveForm', array( 'id'=>'ext-comment-form', 'action'=>array('/comment/comment/create'), 'enableAjaxValidation'=>false )); ?> <?php echo $form->errorSummary($comment); ?> <div class="ext-comment"> <div class="comment-head"> <span class="ext-comment-head"> Create New Comment </span> </div> <div class="commentspacer"> <div class="comment-img"> <?php $this->widget('comment.extensions.gravatar.yii-gravatar.YiiGravatar', array( 'email'=>$data->userEmail, 'size'=>80, 'defaultImage'=>'monsterid', 'secure'=>false, 'rating'=>'r', 'emailHashed'=>false, 'htmlOptions'=>array( 'alt'=>CHtml::encode($data->userName), 'title'=>CHtml::encode($data->userName) ) )); ?> </div> <?php echo $form->error($comment,'message'); ?> <div class="textfieldholder"> <?php echo $form->textArea($comment,'message',array( 'class'=>'commenttextfield', //height/width is set by this css class above 'placeholder'=>'1,000 Charter Max...',//text to display when new message field is empty 'maxlength'=>1000, //sets number of charters aloud to be input for comment. )); ?> </div> </div> <div class="commentbutton"> <?php if ($comment->isNewRecord) { echo $form->hiddenField($comment, 'type'); echo $form->hiddenField($comment, 'key'); /* echo CHtml::hiddenField('returnUrl', $this->createUrl(''));} echo CHtml::submitButton('Save'); */ echo CHtml::ajaxSubmitButton('Post Comment', array('/comment/comment/create'), array( 'replace'=>'#ext-comment-form-new', 'error'=>"function(){ $('#Comment_message').css('border-color', 'red'); $('#Comment_message').css('background-color', '#fcc'); }" ), array('id'=>'ext-comment-submit' . (isset($ajaxId) ? $ajaxId : '')) ); } else { echo CHtml::ajaxSubmitButton('Update Comment', array('/comment/comment/update', 'id'=>$comment->id), array( 'replace'=>'#ext-comment-form-edit-'.$comment->id, 'error'=>"function(){ $('#Comment_message').css('border-color', 'red'); $('#Comment_message').css('background-color', '#fcc'); }" ), array('id'=>'ext-comment-submit' . (isset($ajaxId) ? $ajaxId : '')) ); } ?> </div> </div> <?php $this->endWidget() ?> </div><!-- form --> <?php } ?>
views/_view
<?php Yii::app()->clientScript->registerCss('ext-comment', ""); ?> <div class="ext-comment" id="ext-comment-<?php echo $data->id; ?>"> <div class="comment-head"> <span class="ext-comment-head"> <span class="ext-comment-name"><?php echo CHtml::encode($data->userName); ?></span> wrote on <span class="ext-comment-date"> <?php echo Yii::app()->format->formatDateTime( is_numeric($data->createDate) ? $data->createDate : strtotime($data->createDate) ); ?> </span> </span> <span class="ext-comment-options"> <?php if (!Yii::app()->user->isGuest && (Yii::app()->user->id == $data->userId)) { echo CHtml::ajaxLink('delete', array('/comment/comment/delete', 'id'=>$data->id), array( 'success'=>'function(){ $("#ext-comment-'.$data->id.'").remove(); }', 'type'=>'POST', ), array( 'id'=>'delete-comment-'.$data->id, 'confirm'=>'Are you sure you want to delete this item?', )); echo " | "; echo CHtml::ajaxLink('edit', array('/comment/comment/update', 'id'=>$data->id), array( 'replace'=>'#ext-comment-'.$data->id, 'type'=>'GET', ), array( 'id'=>'ext-comment-edit-'.$data->id, )); } /* adds edit link to post if is not admin's post so they can still edit it */ elseif (Yii::app()->getModule('user')->isAdmin()) { echo CHtml::ajaxLink('edit', array('/comment/comment/update', 'id'=>$data->id), array( 'replace'=>'#ext-comment-'.$data->id, 'type'=>'GET', ), array( 'id'=>'ext-comment-edit-'.$data->id, )); }?> </span> </div> <div class="commentspacer"> <div class="comment-img"> <?php $this->widget('comment.extensions.gravatar.yii-gravatar.YiiGravatar', array( 'email'=>$data->userEmail, 'size'=>80, 'defaultImage'=>'monsterid', 'secure'=>false, 'rating'=>'r', 'emailHashed'=>false, 'htmlOptions'=>array( 'alt'=>CHtml::encode($data->userName), 'title'=>CHtml::encode($data->userName) ) )); ?> </div> <p><?php echo nl2br(CHtml::encode($data->message)); ?></p> </div> </div>
views/commentList
<?php /** @var CArrayDataProvider $comments */ $comments = $model->getCommentDataProvider(); $comments->pagination->pageSize = 20; //sets number of comments to display per page $this->widget('zii.widgets.CListView', array( 'dataProvider'=>$comments, 'itemView'=>'comment.views.comment._view', 'template'=>'{items}{pager}<br/><br/>', //breaks are here because my css wasn't cooperating and it needed to be spaced because of overlap )); $this->renderPartial('comment.views.comment._form', array( 'comment'=>$model->commentInstance ));
New Style
Default Style
This is how i added user image
Please read all of the comments. you also have to add this to your Comment.php model
public function getUserImg()
{
return $this->user->filename;
}
Also, please read this post i made too on how to upload files to your db for users User widget this is where the filename comes from.
In the section below where it has yii gravitar widget in _view.php I replace it with this.
<?php $userObject = $data->userImg; //Renders the comment users filename path Note: public function getUserImg() {return $this->user->filename;} was added to Comment.php model to make it accessable here. filename would be whatever your image column is in your user table $imgbaseurl = Yii::app()->baseUrl.'/'; //gets your base url $defaultimg = $imgbaseurl.'images/users/User/default.png'; //Path to your default image location and image name $defaultguestimg = $imgbaseurl.'images/users/User/default.png'; //Path to your default guest image location and image name $imagelink = $imgbaseurl.''.$userObject; //actual user image if (Yii::app()->user->isGuest) //if user is guest { echo "<img src=$defaultguestimg>";//render defualt guest image } elseif (empty($userObject)) //if image column in your database is empty { echo "<img src=$defaultimg>"; //render the default image } else //if user is not guest and and the db field isn't empty { echo "<img src=$imagelink>"; //render the user image } ?>
Suggestion for Improvement
thanks for this awesome extension.
a small suggestion:
Currently logged in users are being able to post empty comments as well.
Some sort of error message would have been better
thanks again, using this in my first yii app and its working great ! :)
Thanks lot...
save me..
but i have a problem when page refresh all commented disappear only showing the comment box,
i want to show all post that add
plzz help me any one?
Help..
Thanks for wonderfull extension...
But i have one problem...
When i refresh page previous comments are not displaying..
lacks pagination
wow great it works! unfortunately there's a missing part, pagination, it's inappropriate ti load thousands of comments in a load out, the proper way of doing this is loading 25 comments at a time, then at the bottom there should be this "load more comments" or "next page" button
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.