Search a file size range in CGridView

You are viewing revision #3 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.

« previous (#2)

Introduction

Working on a report system of files transfert, I had to let the users simply find files by size. I didn't want to have two fields, I prefer a simple text input, with "human syntax".

Overview

To be able to add file size range searching functionality to your advanced search forms (not grid view filters) without having to add public attributes, 'safe' rules, and chunky search conditions for each file size attribute.

  1. Create an Active Record Behavior that builds the criteria for the search() method and attach it to your model
  2. Modify the search() method of the model to merge the behavior's criteria in with the rest of the searchable attributes
Assumptions
  1. You already have a functioning advanced search form for your grid view that contains the filesize input
  2. Your filesize is stored in bytes.
Behavior

Create a file called EFilesizeRangeSearchBehavior.php. Copy the following code in to the file and save it in to your components/behaviors folder.

/**
 * This model behavior builds a file size range search condition.
 */
class EFilesizeRangeSearchBehavior extends CActiveRecordBehavior
{
 
	
	/**
	 * Converts human readable file size (e.g. 10M, 200.20G) into bytes.
	 *
	 * @param string $str
	 * @return int the result is in bytes
	 */
	private function filesize2bytes($str) {
		if ($str=='') return 0;
		$str = str_replace(array('B', ' '), '', strtoupper($str));
		$bytes = 0;
		$bytes_array = array(
				'K' => pow(1024, 1),
				'M' => pow(1024, 2),
				'G' => pow(1024, 3),
				'T' => pow(1024, 4),
				'P' => pow(1024, 5),
		);
		$bytes = floatval($str);

		if (preg_match('#([KMGTP]?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
			$bytes *= $bytes_array[$matches[1]];
		}
		$bytes = intval(round($bytes, 2));
	
		return $bytes;
	}
    
	/**
	 * Creates the new criteria
	 * @param string $attribute field to search
	 * @param string $value Human style search value
	 * @return return instance of CDbCriteria for the model's search() method
	 * @see filesize2bytes()
	 */
    public function filesizeRangeSearchCriteria($attribute, $value)
    {
        $criteria = new CDbCriteria;
 
		if ($value != '') {
			$sizes = preg_split('/[<>]/', $value);
			if (count($sizes)==1) {
				// No operator : searching an equal value
				$sizeEqual = $this->filesize2bytes($sizes[0]); 
				$criteria->addCondition($attribute.'='.$sizeEqual);
			} else {
				// We have a comparision operator
				if ($sizes[0]=='') {
					// Begins with operator
					if ($value[0] == '<') {
						// <100M => between 0 and 100M
						$sizeMax = $this->filesize2bytes($sizes[1]);
						$criteria->addBetweenCondition($attribute, 0, $sizeMax);
					} else {
						// >100M => more than 100M
						$sizeMin = $this->filesize2bytes($sizes[1]);
						$criteria->addCondition($attribute.'>='.$sizeMin);
					}
				} else {
					// Range of two sizes
					$sizeMin = $this->filesize2bytes($sizes[0]);
					$sizeMax = $this->filesize2bytes($sizes[1]);
					if ($sizeMin>$sizeMax) {
						list($sizeMin, $sizeMax) = array($sizeMax, $sizeMin);
					}
					$criteria->addBetweenCondition($attribute, $sizeMin, $sizeMax);
				}
			}
		} else {
			$criteria->compare($attribute, $value, true);
		}
        // Return the search criteria to merge with the model's search() method
        return $criteria;
    }
 
}
Model

Then, attach the behavior by adding it to the behaviors() array in your model

/**
 * Model behaviors
 */
public function behaviors()
{
	return array(
		'filesizeRangeSearch'=>array(
			'class'=>'application.components.behaviors.EFilesizeRangeSearchBehavior',
		),
	);
}

Next, modify the search() method in the same model and replace the $criteria->compare() line for the filesize attribute you want to range search.

public function search()
{
	$criteria = new CDbCriteria;

	$criteria->compare('id', $this->id);
	...

	// Replace the standard date compare line...
	$criteria->compare('filesize', $this->filesize, true);

	// with the new mergeWith line...
	$criteria->mergeWith($this->filesizeRangeSearchCriteria('filesize', $this->filesize));
	...

	return new CActiveDataProvider($this, array(
		'criteria'=>$criteria,
		));
}

View

The input file in the search form must be corrected to allow enough characters.

Search syntaxe

Here are some examples and there meaning:

  • 1024 : search for an exact value of 1024,
  • 2K : search for an exacte value of 2048,
  • <3M : search for a value between 0 and 3145728
  • >3M : search for a value >= 3145728
  • 2K<3M : search for a value between 2048 and 3145728
    Note
  • Notice that 2K<3M, 2K>3M, 3M<2K and 3M>2K are exactly the same searches.