Advertisement
  1. Code
  2. PHP
  3. Yii

Programming With Yii2: Building Community With Voting, Comments, and Sharing

Scroll to top
This post is part of a series called How to Program With Yii2.
Programming With Yii2: Building a RESTful API
Programming With Yii: Generating Documentation
Final product imageFinal product imageFinal product image
What You'll Be Creating

In this Programming With Yii2 series, I'm guiding readers in use of the Yii2 Framework for PHP. You may also be interested in my Introduction to the Yii Framework, which reviews the benefits of Yii and includes an overview of what's new in Yii 2.x.

Introduction

In today's tutorial, I'm going to show you how to extend Yii to easily mimic a site like Reddit with voting, comments, and sharing.

Recently, I've been working on creating my own personal extension of the great Yii advanced template. The template provides built-in user registration and authentication and multiple sites for front-end and administrative websites.

I built some my latest Twitter API episodes on the early version of this platform, following friends on behalf of users and analyzing our followers. The site I described in those, Twixxr, forms the foundation of my Yii customization work.

So adding core functionality like voting, comments and sharing makes so much sense. As you expand your Yii codebase with these kinds of features, building new sites becomes faster, easier and increasingly powerful. 

Getting Started

I'm going to walk you through using three Yii2 plugins:

They make it relatively fast and easy to build a powerful social community on Yii2. 

I've created a model called Item which represents an object that you want users to vote on, comment on, and share. 

Frankly, after building the item pages with these features in my platform, I felt more impressed than ever with Yii... more impressed than I've been to date even building my startup series. You can do so much with this framework.

Let's dig in.

Installing the Extensions

First, let's add all three extensions to composer.json at once:

1
{
2
    "name": "yiisoft/yii2-app-advanced",
3
    "description": "Yii 2 Advanced Project Template",
4
    "keywords": ["yii2", "framework", "advanced", "project template"],
5
    "homepage": "https://www.yiiframework.com/",
6
    "type": "project",
7
    "license": "BSD-3-Clause",
8
    "support": {
9
        "issues": "https://github.com/yiisoft/yii2/issues?state=open",
10
        "forum": "http://www.yiiframework.com/forum/",
11
        "wiki": "http://www.yiiframework.com/wiki/",
12
        "irc": "irc://irc.freenode.net/yii",
13
        "source": "https://github.com/yiisoft/yii2"
14
    },
15
    "minimum-stability": "stable",
16
    "require": {
17
        "php": ">=5.4.0",
18
        "yiisoft/yii2": ">=2.0.10",
19
        "yiisoft/yii2-bootstrap": "*",
20
        "yiisoft/yii2-swiftmailer": "*",
21
        "yiisoft/yii2-authclient": "~2.1.0",
22
        "google/apiclient": "1.0.*@beta",
23
        "machour/yii2-google-apiclient":"@dev",
24
        "machour/yii2-google-gmail": "@dev",
25
        "ruskid/yii2-stripe": "dev-master",
26
        "2amigos/yii2-disqus-widget":"~1.0",
27
        "abraham/twitteroauth":"*",
28
        "codeception/codeception":"*",
29
        "notamedia/yii2-sentry": "^1.1",
30
        "chiliec/yii2-vote": "^4.0",
31
        "yiidoc/yii2-redactor": "*",
32
        "kartik-v/yii2-social": "@dev"

Then run composer update.

Adding Voting

Vladimir Babin is Chiliec, and I very much like the way he and others have collaborated to create this plugin. All the basic features that you want are included, and you can easily customize it, specifically by overriding the view. They have great documentation and keep it well updated too.

Here's a helpful animated gif of the plugin's default features which they host on GitHub. I've posted a static image below (Envato Tuts+ doesn't support gifs in our tutorials).

Programming With Yii - Voting Plugin Default ExampleProgramming With Yii - Voting Plugin Default ExampleProgramming With Yii - Voting Plugin Default Example

Of course, I decided to customize the view and eliminate down votes, and it was fairly easy.

Configuration

Next, we add the voting plugin to /active/config/main.php so that it's loaded everywhere in bootstrap and configured for our application:

1
return [
2
    'id' => 'app-active',
3
    'basePath' => dirname(__DIR__),
4
    'bootstrap' => ['chiliec\vote\components\VoteBootstrap',
5
        'log','\common\components\SiteHelper'],
6
    'modules' => [
7
    ...
8
    'vote' => [
9
      'class' => 'chiliec\vote\Module',
10
      // show messages in popover
11
      'popOverEnabled' => true,
12
      // global values for all models
13
      // 'allowGuests' => true,
14
      // 'allowChangeVote' => true,
15
      'models' => [
16
          1 => [
17
              'modelName' => \active\models\Item::className(),
18
              'allowGuests' => false,
19
          ],
20
          // example declaration of models
21
          // \common\models\Post::className(),
22
          // 'backend\models\Post',
23
          // 2 => 'frontend\models\Story',
24
          // 3 => [
25
          //     'modelName' => \backend\models\Mail::className(),
26
          //     you can rewrite global values for specific model
27
          //     'allowGuests' => false,
28
          //     'allowChangeVote' => false,
29
          // ],
30
      ],
31
      ],        
32

You can see that I've turned off guest voting so that people are required to sign up to vote on items.

Database Integration

Next, you have to run the database migration to create tables that track the votes.

1
$ php yii migrate/up --migrationPath=@vendor/chiliec/yii2-vote/migrations

It's important to remember to run this migration when installing your product server! It's quite easy to forget.

Displaying the Voting Widget

My item model is part of a collection model called Topic, so you can find the partial view for my voting widget in /views/topic/_item.php:

1
<?php
2
use yii\helpers\Html;
3
use yii\helpers\HtmlPurifier;
4
use dosamigos\disqus\CommentsCount;
5
use kartik\social\TwitterPlugin;
6
use kartik\social\FacebookPlugin;
7
use yii\helpers\StringHelper;
8
9
?>
10
<div class="item_row row">
11
  <div class="col-xs-1 col-md-1 col-lg-1">
12
<?= \chiliec\vote\widgets\Vote::widget([
13
    'model' => $model,
14
    // optional fields

15
     'showAggregateRating' => false,
16
]);?>
17
  </div>

Topic index calls displays a grid which displays _item.php as a row. I didn't want to display a rating, just the positive vote totals, so I set that to false.

To override the view, I created /views/vote/vote.php:

1
<div class="vote-row text-center" id="vote-<?=$modelId?>-<?=$targetId?>" data-placement="top" data-container="body" data-toggle="popover">
2
    <span  class="glyphicon glyphicon-chevron-up" onclick="vote(<?=$modelId?>, <?=$targetId?>, 'like'); return false;" style="cursor: pointer;"></span><br /><span id="vote-up-<?=$modelId?>-<?=$targetId?>"><?=$likes?></span>    
3
    <div id="vote-response-<?=$modelId?>-<?=$targetId?>">
4
        <?php if ($showAggregateRating) { ?>
5
            <?=Yii::t('vote', 'Aggregate rating')?>: <?=$rating?>
6
        <?php } ?>
7
    </div>
8
</div>
9
<div itemprop="aggregateRating" itemscope itemtype="http://schema.org/AggregateRating">
10
    <meta itemprop="interactionCount" content="UserLikes:<?=$likes?>"/>
11
    <meta itemprop="interactionCount" content="UserDislikes:<?=$dislikes?>"/>
12
    <meta itemprop="ratingValue" content="<?=$rating?>"/>
13
    <meta itemprop="ratingCount" content="<?=$likes+$dislikes?>"/>
14
    <meta itemprop="bestRating" content="10"/>
15
    <meta itemprop="worstRating" content="0"/>
16
</div>

Not a lot of plugins make overriding so easy.

I removed the vote down icon and changed the vote up icon to a chevron. Here's what it looks like now:

Programming With Yii - Voting WidgetProgramming With Yii - Voting WidgetProgramming With Yii - Voting Widget

I know this seems like a lot of layers, but it actually didn't take too long to make it work.

Adding Disqus Comments

Next, I created a Disqus site for the upcoming site, ActiveTogether.org, which will be available for you to see these features in action by the time you read this. Thus, the Disqus site shortname is 'active-together'.

I began using 2Amigos' widget before I integrated Kartik's social extension (discussed below), which also offers Disqus comments.

Creating a Unique Identifier for Each Comment Board

Whenever a new Item is created, the Item::beforeSave() action creates a unique identifier for Disqus to link comments too. You can also rely on the URL of a page, but this is more predictable generally.

In other words, Disqus collates all comments for each item separately, and that helps makes up each item's comment board.

1
public function beforeSave($insert)
2
      {
3
          if (parent::beforeSave($insert)) {
4
            if ($insert) {
5
              $this->identifier = Yii::$app->security->generateRandomString(8);
6
              $this->site_id = Yii::$app->params['site']['id'];
7
            }
8
          }
9
          return true;
10
      }

Displaying the Comments

Then, the comments board is easily displayed at the bottom of the Item view in /active/views/Item.php:

1
<?php
2
3
use yii\helpers\Html;
4
use yii\helpers\HtmlPurifier;
5
use yii\helpers\Url;
6
use dosamigos\disqus\Comments;
7
...
8
<?= Comments::widget([
9
    'shortname' => 'active-together',
10
    'identifier'=>$model->identifier,
11
    ]); ?>

Notice how the widget needs the shortname and the identifier to provide Disqus for the comments.

Here's an example of what the comments board looks like:

Programming With Yii - Item Page with voting sharing and comments boardProgramming With Yii - Item Page with voting sharing and comments boardProgramming With Yii - Item Page with voting sharing and comments board

The Index View With Comment Counts

2Amigos also leverages the Disqus JavaScript libraries for displaying comment counts. But there are a few pieces to put together to make this happen.

First, I created a jQuery script to request an item's comment counts. When there are lots of items on a page, you need to request it with reset: true;:

1
$(document).ready(function(){
2
  DISQUSWIDGETS.getCount({reset: true});  
3
});

Then I created a TopicAsset.php file to load the .js file:

1
<?php
2
namespace active\assets;
3
use yii\web\AssetBundle;
4
5
class TopicAsset extends AssetBundle
6
{
7
    public $basePath = '@webroot';
8
    public $baseUrl = '@active';
9
    public $css = [
10
11
    ];
12
    public $js = [
13
      'js/topic.js',
14
    ];
15
    public $depends = [
16
        'yii\web\YiiAsset',
17
        'yii\bootstrap\BootstrapAsset',
18
    ];

Then, the /active/views/Topic.php file registers the TopicAsset bundle:

1
<?php
2
3
use yii\helpers\Html;
4
use yii\grid\GridView;
5
use yii\widgets\Breadcrumbs;
6
use common\widgets\Alert;
7
use active\assets\TopicAsset;
8
TopicAsset::register($this);

Next, each _item.php partial includes a comment count:

1
<p><?= $this->render('_social', ['model' => $model,
2
    'includeCommentCount'=>true]);?></p>

And the _social partial displays it like this using each Item->identifier:

1
<li class="share_adjust_vert"><?= Html::a(Yii::t('active','Comments')
2
    ,['/item/'.$model->slug.'#disqus_thread'],
3
    ['data-disqus-identifier'=>$model->identifier]) ?>
4
    <?= CommentsCount::widget([
5
    'shortname' => 'active-together',
6
    'identifier' => $model->identifier,
7
  ]);
8
  ?>

In order for Disqus to find where to update elements with comment counts, each link must end with #disqus_thread.

Here's what that page looks like. Each item has a distinct comment count loaded by referencing its identifier:

Programming With Yii - Index page of with voting and social comments and sharingProgramming With Yii - Index page of with voting and social comments and sharingProgramming With Yii - Index page of with voting and social comments and sharing

Let's move on to those social sharing buttons you've been seeing.

Adding Social Sharing

Kartik's done a great job with his social widget building a basic configuration for connection to a number of social companies like Twitter, Disqus, and Facebook. For now, I'm only using the Facebook share button. Twitter's share button doesn't have very good aesthetics, so I replaced it with an HTML web intents link.

Here's my code for the pair of buttons beside the comments count in /active/views/topic/_social.php:

1
</li>
2
  <li class="share_adjust_vert"><a class="twitter-share"
3
    href="https://twitter.com/intent/tweet?

4
        text=<?= urlencode($model->title); ?>

5
        &url=<?= urlencode(Url::canonical());?>

6
        &via=<?= Yii::$app->params['site']['twitter_account']?>

7
        "><img src="<?= Url::to(Url::home(true).'/images/social/twitter_icon.png');
8
        ?>"> Tweet</a></li>
9
<li><?= FacebookPlugin::widget
10
        (['type'=>FacebookPlugin::SHARE,
11
        'settings' => ['dataSize'=>'small',
12
        'class'=>"fb_iframe_widget"]]); ?></li>
13
</ul>

Seems simple, except that vertically aligning Facebook's widget requires some CSS adjustments. In /active/views/topic/_grid.php, I placed this adjustment:

1
<style media="screen" type="text/css">
2
.fb_iframe_widget span
3
{
4
    vertical-align: baseline !important;
5
}
6
</style>

It has to come after the other CSS files load.

And, in the site.css file, I placed this to get the precise look I wanted:

1
.share_adjust_vert {
2
  margin-top:-1px;
3
  font-size:90%;
4
  vertical-align: top;
5
}

Wrapping Up

Honestly, I'm so excited at how easy it was to use Yii and essentially create a mini social clone. These are great plugins for a great framework, and generally Yii's developers and its community of plugin developers are responsive on GitHub with questions and issues.

I hope you are eager to check out ActiveTogether and try out this framework for yourself.

If you have any questions or suggestions, please post them in the comments. If you'd like to keep up on my future Envato Tuts+ tutorials and other series, please visit my instructor page or follow @lookahead_io. Definitely check out my startup series and Meeting Planner.

Related Links

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.