Yii 1

CLinkPager and first page and last page display

By Ronald van Belzen | February 7, 2015

When showing content with CGridView or CListView a pager is included. This pager shows the navigation for 10 pages and a next page and a previous page link.

What is not shown by default are the first and last page links. The simplest way of displaying these links, when you need them, is by overriding the class associated to these links. Below an example is shown for a CListView.

<?php 
$this->widget('zii.widgets.CListView', array( 
  'id'=>'bbiiTopic', 
  'dataProvider'=>$dataProvider, 
  'itemView'=>'_topic', 
  'template'=>'{pager}{items}{pager}', 
  'pager'=>array('firstPageCssClass'=>'previous', 'lastPageCssClass'=>'next'), 
)); ?>

The firstPageCssClass and lastPageCssClass are the values that are overridden.

This is very much a short cut. The proper way to do it is to overwrite the CSS yiiPager.first and yiiPager.last and not to override the pager in a CListView or CGridView at all.

Passing data to the CButtonColumn click event

By Ronald van Belzen | July 20, 2013

One of the limitations of the click event of an CButtonColumn is that it does not take data from the CGridView. But passing row specific data to a Javascript on clicking the button is precisely what is needed in many cases.

The simplest workaround is to use jQuery to deliver the required data:

$this->widget('zii.widgets.grid.CGridView', array( 

  // omitted parameters

  'columns'=>array( 

    // omitted columns 

    array( 
      'class'=>'CButtonColumn', 
      'template'=>'{view}', 
      'buttons' => array( 
        'view' => array( 
          'url'=>'$data->id', 
          'click'=>'js:function() { viewPost($(this).attr("href"));return false; }', 
        ), 
      ) 
    ), 
  ), 
));

The trick is that the data is passed to the url and $(this).attr("href") passes it to the function called by the click event. Of course, the click event should return false to prevent the web browser from visiting the url. All navigation (if any) should be triggered by the Javascript in this solution.

Adding Fancybox to an Yii application

By Ronald van Belzen | June 15, 2013

After showing how to add AD Gallery to an application I am showing here how to use Fancybox in an Yii application for the display of an image gallery. "FancyBox is a tool for displaying images, html content and multi-media in a Mac-style "lightbox" that floats overtop of web page." It is capable of showing much more then just images. In fact the Yii module Gii uses Fancybox to display the generated code in a lightbox. But in this example I am just going to show how to use Fancybox with images to show how it can be used to produce an image gallery.

As with AD Gallery Fancybox is not overly complicated, but it is different. In order not to repeat the former blog post too much I am adding pagination to the example, which is useful when using extremely large sets of images. The controller used in the previous example will be changed to look like this:

class OverviewController extends Controller { 
  const PAGE_SIZE = 50;

  // other action functions 

  public function actionView($id) { 
    $model=Gallery::model()->findByPk($id); 
    if($model===null) 
      throw new CHttpException(404,'The requested page does not exist.'); 

    $criteria = new CDbCriteria; 
    $criteria->condition = "gallery_id = $id"; 

    $count = GalleryImage::model()->count($criteria); 
    $pageCount = ceil($count/self::PAGE_SIZE); 
    $pager = new CPagination($pageCount); 
    $pager->pageSize=1; 

    $criteria->order = 'sort ASC'; 
    $criteria->limit = self::PAGE_SIZE; 
    $criteria->offset = self::PAGE_SIZE*$pager->currentPage; 
    $models = GalleryImage::model()->findAll($criteria); 
    $this->render('view', array( 
      'gallery'=>$model, 
      'models'=>$models, 
      'pager'=>$pager, 
    )); 
  } 
}

The class constant PAGE_SIZE represents the number of thumbnail images that will be displayed on a page. The variable $pageCount contains the number of pages that are needed for pagination and is passed to the CPagination class constructor. The page size for CPagination is set to 1, which is not related to the number of images on a page but chosen to make the offset calculation simpler.

Adding AD Gallery to an Yii application

By Ronald van Belzen | June 7, 2013

AD Gallery is "A highly customizable gallery/showcase plugin for jQuery". In other words it is a jQuery plugin to create an image gallery on a web page. As many lightbox and image gallery plugins, AD Gallery makes implementation relatively easy. For that reason the javascript part of the example shown below is the easiest part, while the rest is not overly complex either.

Still, to understand the script it is useful to know how it is called from the controller:

class OverviewController extends Controller { 
  // other action functions 

  public function actionView($id) { 
    $model=Gallery::model()->findByPk($id); 
    if($model===null) 
      throw new CHttpException(404,'The requested page does not exist.'); 
    $criteria = new CDbCriteria; 
    $criteria->condition = "gallery_id = $id"; 
    $criteria->order = 'sort ASC'; 
    $models = GalleryImage::model()->findAll($criteria); 
    $this->render('view', array( 
      'gallery'=>$model, 
      'models'=>$models, 
    ));
  } 
}

The model Gallery contains the information about different galleries. Most important attribute of this model is storage that contains the location of the directory in which the images are stored. The model GalleryImage links images to a gallery and contains the information about the image file names. The controller action renders the view view that shows the image gallery:

Multiple files upload in Yii - tabular input fields

By Ronald van Belzen | May 31, 2013

There are several ways to tackle the challenge of uploading multiple files in Yii. The differences are based upon the form field(s) being used. Here I will show an example of a tabular file input form. Other methods include the use of the CMultiFileUpload widget and the multiple attribute for the file input field introduced by HTML5.

When you have not read my previous blog post about single file upload, you should consider it when you run into problems. Also you should take into account the php.ini settings for max_file_uploads, post_max_size and upload_max_filesize.

To the model that will store the information about the image the field $images is added for the file upload:

class GalleryImage extends CActiveRecord { 
  public $images;

And to this class the validation rule is added for the new field:

  public function rules() { 
    return array( 
    // other rules 
    array('images', 'file', 'allowEmpty' => true, 'types' => 'gif,jpg,jpeg,png', 'maxSize' => 5242880, 'maxFiles' => 5, ), 
    // other rules ); 
  }

Next we create a form for the input of the files and their descriptions. To add a bit of complexity the images can be added to different galleries:

Single file upload in Yii

By Ronald van Belzen | May 24, 2013

When uploading files in Yii the first choice to be made is to either use an existing model or create a new model. The existing model can be of the type CActiveRecord. The new model is usually of the type CFormModel. The reason for the choice for a class inheriting from CFormModel is that the uploaded file is usually not saved in a database table and therefore no CActiveRecord is required.

That does not mean that a model based upon CActiveRecord cannot be used. When the meta data of an uploaded file is saved in a database table it even makes perfect sense to use that model for the file upload.

In this example an image is uploaded to serve as logo for one of the blogs.The image will be saved in the file system. The name of the image is saved in the database table field "logo". To the existing Blog model the variable "$filename" is added to be used in the upload form:

class Blog extends CActiveRecord { 
  public $filename;

The validation of the uploaded image will also be done by the model. To the function "rules()" the validation rule for "filename" is added:

public function rules() { 
  return array( 
    // other rules 
    array('filename', 'file', 'allowEmpty' => true,'maxSize' => 102400, 'types' => 'jpg, jpeg, png'), 
    // other rules 
  ); 
}

The form that is used to create and update the database table already exists. This CActiveForm is adapted to be able to handle file uploads by adding the following "htmlOptions" property:

<?php $form=$this->beginWidget('CActiveForm', array( 
  'id'=>'blog-form', 
  'enableAjaxValidation'=>false, 
  'htmlOptions'=>array('enctype'=>'multipart/form-data'), 
)); ?>

This adds the mandatory attribute enctype="multipart/form-data" for file upload to the html form.
NOTE: The default method POST must be used and Ajax validation needs to be disabled.

Next the field for the file upload needs to be added to the form:

Spam control by IP blocking in Yii

By Ronald van Belzen | May 4, 2013

Any interactive website needs to take into account that that interactivity will be abused. The most common form of abuse is spam. Even when you control the user input by moderation there comes a time that you need to structure the control of user input. One way of getting control over input from abusive users is by using IP blocking.

The approach I will describe here will require that your application registers the IP address in all the database tables that store user input. As an example I will use a "blog_comment" table.

The stategy that will be followed is that upon creation of a comment (INSERT) the IP address will be checked against a database table containing the IP addresses that need to be blocked.

The moment to determine the IP address of a user and store it in the table "blog_comment" is during validation in the function "rules()":

    return array( 
      // ... other rules 
      array('ip', 'default', 'value'=>Yii::app()->request->userHostAddress), 
      // ... other rules 
    );

We start by creating the database table that will contain the IP addresses that need to be blocked.

CREATE TABLE `ipaddress` ( 
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT , 
  `ip` VARCHAR(39) NULL , 
  `address` VARCHAR(255) NULL , 
  `source` TINYINT NULL DEFAULT 0 , 
  `count` INT NULL DEFAULT 0 , 
  `create_time` TIMESTAMP NULL , 
  `update_time` TIMESTAMP NULL , 
  PRIMARY KEY (`id`) , 
  UNIQUE INDEX `ip_UNIQUE` (`ip` ASC) );

The only mandatory field in that table will be "ip". The other fields are for extra functionality that can be used for maintaining the IP addresses. The field "source" can be used to distinguish manual input from automated input of IP addresses. The field "count" can be used to count the number of times an IP address has been checked.The field "update_time" is used to check when an IP address was last checked.

We use Gii to create a model from this table and use the Crud Generator. Next we improve the views that the Crud Generator has made, but I leave it to you to do that. In this blog post we concentrate on the model Ipaddress to which we add the following function:

Retrieving all controller actions in an application

By Ronald van Belzen | May 3, 2013

Well not all actions, the method does not incorporate the CController actions() function to retrieve the external action classes of a controller, but these can be added quite easy for each controller found.

The method does not bother to search for nested modules either, but when you have them in your application you can add a search for them to the method.

Making your own portal widget with Yii

By Ronald van Belzen | April 30, 2013

As an example of how to make your own portlet let me show how I made a "recent comments" portlet for this blog.

The Blog tutorial shows a couple of excellent examples of how to make your own portlets, including how to make a recent comments portlet, but this one will be different. It will not just select to show the recent comments of the specified blog (multiple blogs can be defined, see the database schema in a previous post for reference). It will actually show the recently commented blog posts instead of the recent comments.

This is the code that I placed in "/protected/component":

<?php 
Yii::import('zii.widgets.CPortlet'); 

class RecentComments extends CPortlet { 
  public $title='Recent Comments'; 
  public $blogId=0; 
  public $maxComments=10; 

  protected function renderContent() { 
    $criteria = new CDbCriteria; 
    $criteria->select = 'post_id, max(t.id) as id'; 
    $criteria->group = 'post_id'; 
    $criteria->condition = 'blog_id = ' . $this->blogId; 
    $criteria->order = 'MAX(t.id) DESC'; 
    $criteria->limit = $this->maxComments; 
    $models = Blogcomment::model()
      ->with(array('post'=>array('select'=>false)))
      ->findAll($criteria); 

    foreach($models as $model) { 
      $comment = BlogComment::model()->with('commentUser')->findByPk($model->id); 
      $post = BlogPost::model()->findByPk($model->post_id)->title; 
      $username = ($comment->commentUser)?$comment->commentUser->username:$comment->username;
      $link=CHtml::link(CHtml::encode($username) . ' • ' . CHtml::encode($post), array('/blog/blogPost/view','id'=>$model->post_id,'#'=>'bottom')); 
      echo CHtml::tag('div', array('class'=>'recent',), $link) . PHP_EOL; 
    } 
  } 
}

The default value for $blogId will result in an empty portlet and portlets have the advantage of not being shown when they have no content.

The real work is contained in the criteria. Translated into a SQL statement it would read: