Yii Framework Forum: jqGrid Widget - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

jqGrid Widget Rate Topic: -----

#1 User is offline   lucifurious 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 184
  • Joined: 15-March 09
  • Location:Atlanta, GA.

Posted 30 March 2009 - 07:31 PM

Hey folks...

I've created my first extension! It wraps the jqGrid (which is a jQuery grid) in an easy to use widget.

Looking for feedback/bugs/comments before I post to extensions. Plus I have to write some doc.

I've installed the jqGrid into a directory on my server at /extra. So the base URL for jqGrid is /extra/jqGrid. This can be overridden with the baseUrl option (see below).

The class (CjqGridWidget.php) should be placed in your /protected/extensions/jqGrid directory. If you put it somewhere else, be sure to change the widget invocation string to the appropriate path.

So, here's the class (CjqGridWidget.php):


<?php
/**
* CjqGridWidget class file.
*
* @author Jerry Ablan <jablan@pogostick.com>
* @link http://www.pogostick.com/
* @copyright Copyright &copy; 2009 Pogostick, LLC
* @license http://www.gnu.org/licenses/gpl.html
*
* Install in <yii_app_base>/extensions/jqGrid
*/

/**
* The CjqGridWidget allows the jqGrid (@link http://www.trirand.com/blog/) to be used in Yii.
* Thanks to MetaYii for some ideas on valid options and callbacks.
*
* @author Jerry Ablan <jablan@pogostick.com>
* @version $Id: CjqGridWidget.php 1 2009-03-31 00:30:25Z jablan $
* @package applications.extensions.CjqGridWidget
* @since 1.0.3
*/
class CjqGridWidget extends CInputWidget
{
//********************************************************************************
//* Member Variables
//********************************************************************************

/**
* Where the the base jqGrid files are installed.
*
* @var string
*/
protected $m_sBaseUrl = '/extra/jqGrid';

/**
* Css file to override default style
*
* @var string
*/
protected $m_sCssFile = null;

/**
* Indicates whether or not to validate options
*
* @var boolean
*/
protected $m_bCheckOptions = true;

/**
* Indicates whether or not to validate callbacks
*
* @var boolean
*/
protected $m_bCheckCallbacks = true;

/**
* Valid options for this widget
*
* @var array
*/
protected $m_arValidOptions = array(
'altRows' => array( 'type' => 'boolean' ),
'caption' => array( 'type' => 'string' ),
'cellEdit' => array( 'type' => 'boolean' ),
'cellsubmit' => array( 'type' => 'string', 'valid' => array( 'remote', 'clientarray' ) ),
'cellurl' => array( 'type' => 'string' ),
'colModel' => array( 'type' => 'array' ),
'colNames' => array( 'type' => 'array' ),
'datastr' => array( 'type' => 'string' ),
'datatype' => array( 'type' => 'string', 'valid' => array( 'xml', 'xmlstring', 'json', 'jsonstring', 'clientside' ) ),
'deselectAfterSort' => array( 'type' => 'boolean' ),
'editurl' => array( 'type' => 'string' ),
'expandcolumn' => array( 'type' => 'boolean' ),
'forceFit' => array( 'type' => 'boolean' ),
'gridstate' => array( 'type' => 'string', 'valid' => array( 'visible', 'hidden' ) ),
'hiddengrid' => array( 'type' => 'boolean' ),
'hidegrid' => array( 'type' => 'boolean' ),
'height' => array( 'type' => array( 'string', 'integer' ) ),
'imgpath' => array( 'type' => 'string' ),
'jsonReader' => array( 'type' => 'array' ),
'loadonce' => array( 'type' => 'boolean' ),
'loadtext' => array( 'type' => 'string' ),
'loadui' => array( 'type' => 'string', 'valid' => array( 'disable', 'enable', 'block' ) ),
'multiselect' => array( 'type' => 'boolean' ),
'mtype' => array( 'type' => 'string', 'valid' => array( 'GET', 'PUT' ) ),
'multikey' => array( 'type' => 'string' ),
'multiboxonly' => array( 'type' => 'boolean' ),
'pagerId' => array( 'type' => 'string' ),
'prmNames' => array( 'type' => 'array' ),
'postData' => array( 'type' => 'array' ),
'resizeclass' => array( 'type' => 'string' ),
'rowNum' => array( 'type' => 'integer' ),
'rowList' => array( 'type' => 'array' ),
'scroll' => array( 'type' => 'boolean' ),
'scrollrows' => array( 'type' => 'boolean' ),
'sortclass' => array( 'type' => 'string' ),
'shrinkToFit' => array( 'type' => 'boolean' ),
'sortascimg' => array( 'type' => 'string' ),
'sortdescimg' => array( 'type' => 'string' ),
'sortname' => array( 'type' => 'string' ),
'sortorder' => array( 'type' => 'string' ),
'theme' => array( 'type' => 'string', valid => array( 'basic', 'coffee', 'green', 'sand', 'steel' ) ),
'toolbar' => array( 'type' => 'array' ),
'treeGrid' => array( 'type' => 'boolean' ),
'tree_root_level' => array( 'type' => 'integer' ),
'url' => array( 'type' => 'string' ),
'userData' => array( 'type' => 'array' ),
'viewrecords' => array( 'type' => 'boolean' ),
'width' => array( 'type' => 'integer' ),
'xmlReader' => array( 'type' => 'array' ),
);

/**
* The valid callbacks for this widget
*
* @var mixed
*/
protected $m_arValidCallbacks = array(
'afterInsertRow',
'gridComplete',
'loadBeforeSend',
'loadComplete',
'loadError',
'onCellSelect',
'ondblclickRow',
'onHeaderClick',
'onRighClickRow',
'onselectAll',
'onselectRow',
'onSortCol'
);

/**
* Placeholder for widget options
*
* @var array
*/
public $m_arOptions = array();

/**
* Placeholder for callbacks
*
* @var array
*/
protected $m_arCallbacks = array();

//********************************************************************************
//* Methods
//********************************************************************************

/***
* Runs this widget
*
*/
public function run()
{
// Validate baseUrl
if ( empty( $this->m_sBaseUrl ) )
throw new CHttpException( 500, 'CjqGridWidget: baseUrl is required.');

// Get the id/name of this widget
list( $_sName, $_sId ) = $this->resolveNameID();

// Register the scripts/css
$this->registerClientScripts( $_sId );

// Generate the HTML for this widget
echo $this->generateHtml( $_sId );
}

/**
* Registers the needed CSS and JavaScript.
*
* @param string $sId
*/
public function registerClientScripts( $sId = 'list' )
{
// If image path isn't specified, set to current theme path
if ( ! array_key_exists( 'imgpath', $this->m_arOptions ) || empty( $this->m_arOptions[ 'imgpath' ] ) )
$this->m_arOptions[ 'imgpath' ] = "{$this->m_sBaseUrl}/themes/{$this->m_arOptions[ 'theme' ]}/images";

// Register scripts necessary
$_oCS = Yii::app()->getClientScript();
$_oCS->registerScriptFile( "{$this->m_sBaseUrl}/jquery.jqGrid.js" );
$_oCS->registerScriptFile( "{$this->m_sBaseUrl}/js/jqModal.js" );
$_oCS->registerScriptFile( "{$this->m_sBaseUrl}/js/jqDnR.js" );

// Get the javascript for this widget
$_sScript = $this->generateJavascript( $sId );
$_oCS->registerScript( 'Yii.' . get_class( $this ) . '#' . $sId, $_sScript, CClientScript::POS_READY );

// Register css files...
$_oCS->registerCssFile( "{$this->m_sBaseUrl}/themes/{$this->m_arOptions[ 'theme' ]}/grid.css", 'screen' );
$_oCS->registerCssFile( "{$this->m_sBaseUrl}/themes/jqModal.css", 'screen' );

if ( ! empty( $this->m_sCssFile ) )
$_oCS->registerCssFile( Yii::app()->baseUrl . "{$this->m_sCssFile}", 'screen' );
}

//********************************************************************************
//* Property Accessors
//********************************************************************************

/**
* Get the BaseUrl property
*
*/
public function getBaseUrl()
{
return( $this->m_sBaseUrl );
}

/**
* Set the BaseUrl property
*
* @param mixed $sUrl
*/
public function setBaseUrl( $sUrl )
{
$this->m_sBaseUrl = $sUrl;
}

/***
* Get the Css File
*
*/
public function getCssFile()
{
return( $this->m_sCssFile );
}

/***
* Set the Css file
*
* @param mixed $_sFile
*/
public function setCssFile( $_sFile )
{
$this->m_sCssFile = $_sFile;
}

/**
    * Setter
    *
    * @var array $value options
    */
public function setOptions( $arOptions )
{
if ( ! is_array( $arOptions ) )
throw new CException( Yii::t( 'CjqGridWidget', 'options must be an array' ) );

if ( $this->m_bCheckOptions )
self::checkOptions( $arOptions, $this->m_arValidOptions );

$this->m_arOptions = $arOptions;
}

/**
* Gets the CheckOptions option
*
*/
public function getCheckOptions()
{
return( $this->m_bCheckOptions );
}

/**
* Sets the CheckOptions option
*
* @param mixed $_bValue
*/
public function setCheckOptions( $_bValue )
{
$this->m_bCheckOptions = $_bValue;
}

/**
* Gets the CheckCallbacks option
*
*/
public function getCheckCallbacks()
{
return( $this->m_bCheckCallbacks );
}

/***
* Sets the CheckCallbacks option
*
* @param mixed $_bValue
*/
public function setCheckCallbacks( $_bValue )
{
$this->m_bCheckCallbacks = $_bValue;
}

/**
* Getter
*
* @return array
*/
public function getOptions()
{
return( $this->m_arOptions );
}

/**
* Setter
*
* @param array $value callbacks
*/
public function setCallbacks( $arCallbacks )
{
if ( ! is_array( $arCallbacks ) )
throw new CException( Yii::t( 'CjqGridWidget', 'callbacks must be an associative array' ) );

if ( $this->m_bCheckCallbacks )
self::checkCallbacks( $arCallbacks, $this->m_arValidCallbacks );

$this->m_arCallbacks = $arCallbacks;
}

/**
* Getter
*
* @return array
*/
public function getCallbacks()
{
return $this->m_arCallbacks;
}

//********************************************************************************
//* Private methods
//********************************************************************************

/**
    * Check the options against the valid ones
    *
    * @param array $value user's options
    * @param array $validOptions valid options
    */
protected static function checkOptions( $arOptions, $arValidOptions )
{
if ( ! empty( $arValidOptions ) )
{
        foreach ( $arOptions as $_sKey => $_oValue )
        {
if ( ! array_key_exists( $_sKey, $arValidOptions ) )
throw new CException( Yii::t( 'CjqGridWidget', '"{x}" is not a valid option', array( '{x}' => $_sKey ) ) );

            $_sType = gettype( $_oValue );

if ( ( ! is_array( $arValidOptions[ $_sKey ][ 'type' ] ) && ( $_sType != $arValidOptions[ $_sKey ][ 'type' ] ) ) || ( is_array( $arValidOptions[ $_sKey ][ 'type' ] ) && ! in_array( $_sType, $arValidOptions[ $_sKey ][ 'type' ] ) ) )
throw new CException( Yii::t( 'CjqGridWidget', '"{x}" must be of type "{y}"', array( '{x}' => $_sKey, '{y}' => ( is_array( $arValidOptions[ $_sKey ][ 'type' ] ) ) ? implode( ', ', $arValidOptions[ $_sKey ][ 'type' ] ) : $arValidOptions[ $_sKey ][ 'type' ] ) ) );

            if ( array_key_exists( 'valid', $arValidOptions[ $_sKey ] ) )
            {
            if ( ! in_array( $_oValue, $arValidOptions[ $_sKey ][ 'valid' ] ) )
            throw new CException( Yii::t( 'CjqGridWidget', '"{x}" must be one of: "{y}"', array( '{x}' => $_sKey, '{y}' => implode( ', ', $arValidOptions[ $_sKey ][ 'valid' ] ) ) ) );
            }

            if ( ( $_sType == 'array' ) && array_key_exists( 'elements', $arValidOptions[ $_sKey ] ) )
self::checkOptions( $_oValue, $arValidOptions[ $_sKey ][ 'elements' ] );
}
  }
  }

  /**
    *
    * @param array $value user's callbacks
    * @param array $validCallbacks valid callbacks
    */
  protected static function checkCallbacks( $arCallbacks, $arValidCallbacks )
  {
if ( ! empty( $arValidCallbacks ) )
{
foreach ( $arCallbacks as $_sKey => $_oValue )
{
if ( ! in_array( $_sKey, $arValidCallbacks ) )
throw new CException( Yii::t( 'CjqGridWidget', '"{x}" must be one of: {y}', array( '{x}' => $_sKey, '{y}' => implode( ', ', $arValidCallbacks ) ) ) );
}
}
}

/**
* Generates the javascript code for the widget
*
* @return string
*/
protected function generateJavascript( $sId = 'list' )
{
$_arOptions = $this->makeOptions();

$_sScript =<<<CODE
jQuery("#{$sId}").jqGrid( {$_arOptions} );
CODE;
return( $_sScript );
}

/**
* Generates the javascript code for the widget
*
* @return string
*/
protected function generateHtml( $sId = 'list', $sPagerId = 'jqPager' )
{
$_sHtml =<<<CODE
<table id="{$sId}" class="scroll"></table>
<div id="{$sPagerId}" class="scroll" style="text-align:center;"></div>
CODE;
return( $_sHtml );
}

/**
* Generates the options for the widget
*
* @return string
*/
protected function makeOptions()
{
$_arOptions = array();

foreach ( $this->m_arCallbacks as $_sKey => $_oValue )
$_arOptions[ "cb_{$_sKey}" ] = $_sKey;

$_sEncodedOptions = CJavaScript::encode( array_merge( $_arOptions, $this->m_arOptions ) );

// Fix up the pager...
$_sEncodedOptions = str_replace( "'pagerId':'{$this->m_arOptions['pagerId']}'", "'pager': jQuery('#{$this->m_arOptions['pagerId']}')", $_sEncodedOptions );

foreach ( $this->m_arCallbacks as $_sKey => $_oValue )
$_sEncodedOptions = str_replace( "'cb_{$_sKey}':'{$_sKey}'", "'{$_sKey}': {$_oValue}", $_sEncodedOptions );

return( $_sEncodedOptions );
}
}


Here is the method for your controller to generate the needed XML. Obviously you'll need to change the column names and whatnot:


/**
* Returns Xml data suitable for jqGrid
*
*/
public function actionXmlData()
{
$_iPage = 1;
$_iLimit = 25;
$_iSortCol = 1;
$_sSortOrder = 'asc';

// Get any passed in arguments
if ( isset( $_REQUEST[ 'page' ] ) )
$_iPage = $_REQUEST[ 'page' ];

if ( isset( $_REQUEST[ 'rows' ] ) )
$_iLimit = $_REQUEST[ 'rows' ];

if ( isset( $_REQUEST[ 'sidx' ] ) )
$_iSortCol = $_REQUEST[ 'sidx' ];

if ( isset( $_REQUEST[ 'sord' ] ) )
$_sSortOrder = $_REQUEST[ 'sord' ];

// Get a count of rows for this result set
$_dbc = new CDbCriteria();
$_dbc->condition = 'user_uid = :user_uid';
$_dbc->params = array( ':user_uid' => Yii::app()->user->id );
$_iRowCount = InventoryItem::model()->count( $_dbc );

// Calculate paging info
if ( $_iRowCount > 0 )
$_iTotalPages = ceil( $_iRowCount / $_iLimit );
else
$_iTotalPages = 0;

// Sanity check
if ( $_iPage > $_iTotalPages )
$_iPage = $_iTotalPages;

if ( $_iPage < 1 )
$_iPage = 1;

// Calculate starting offset
$_iStart = $_iLimit * $_iPage - $_iLimit;

// Sanity check
if ( $_iStart < 0 )
$_iStart = 0;

// Adjust the criteria for the actual query...
$_dbc->order = "{$_iSortCol} {$_sSortOrder}";
$_dbc->select = "inv_uid, inv_type_uid, name_text, sku_id_text, upc_code_text, qty_on_hand_nbr";
$_dbc->limit = $_iLimit;
$_dbc->offset = $_iStart;
$_oRows = InventoryItem::model()->findAll( $_dbc );

// Set appropriate content type
if ( stristr( $_SERVER[ 'HTTP_ACCEPT' ], "application/xhtml+xml" ) )
header( "Content-type: application/xhtml+xml;charset=utf-8" );
else
header( "Content-type: text/xml;charset=utf-8" );

// Now create the Xml...
$_sOut = "<?xml version='1.0' encoding='utf-8'?>";
$_sOut .= CHtml::openTag( "rows" );
$_sOut .= CHtml::tag( 'page', array(), $_iPage );
$_sOut .= CHtml::tag( 'total', array(), $_iTotalPages );
$_sOut .= CHtml::tag( 'records', array(), $_iRowCount );

if ( $_oRows )
{
// Create the row data...
foreach ( $_oRows as $_oRow )
{
$_sOut .= CHtml::openTag( 'row', array( 'id' => $_oRow->inv_uid ) );
$_sOut .= CHtml::tag( 'cell', array(), CHtml::cdata( $_oRow->inventoryType->type_name_text ) );
$_sOut .= CHtml::tag( 'cell', array(), CHtml::cdata( $_oRow->sku_id_text ) );
$_sOut .= CHtml::tag( 'cell', array(), CHtml::cdata( $_oRow->upc_code_text ) );
$_sOut .= CHtml::tag( 'cell', array(), CHtml::cdata( $_oRow->name_text ) );
$_sOut .= CHtml::tag( 'cell', array(), $_oRow->qty_on_hand_nbr );
$_sOut .= CHtml::closeTag( 'row' );
}
}

// Close our tag...
$_sOut .= CHtml::closeTag( 'rows' );

// Spit it out...
echo $_sOut;
}


Here is the view that creates the grid. Again, you'll need to change the column names to match your needs:


<?php
$_arOptions = array(
'url' => Yii::app()->createUrl( 'InventoryItem/XmlData' ),
'datatype' => 'xml',
'mtype' => 'GET',
'pagerId' => 'jqPager',
'rowNum' => 25,
'rowList' => array( 10, 25, 50, 100 ),
'sortname' => 'name_text',
'sortorder' => 'asc',
'viewrecords' => true,
'theme' => 'steel',
'width' => 800,
'height' => 'auto',
'colNames' => array( "Type", "SKU", "UPC Code", "Name", "Quantity" ),
'colModel' => array(
array( 'name' => 'inv_type_uid', 'index' => 'inv_type_uid', 'width' => 25 ),
array( 'name' => 'sku_id_text', 'index' => 'sku_id_text', 'width' => 30 ),
array( 'name' => 'upc_code_text', 'index' => 'upc_code_text', 'width' => 30 ),
array( 'name' => 'name_text', 'index' => 'name_text', 'width' => 125 ),
array( 'name' => 'qty_on_hand_nbr', 'index' => 'qty_on_hand_nbr', 'width' => 25, 'align' => 'right' ),
),
);

$_arCallbacks = array(
'onselectRow' => 'function( id ) { location.href = "update/id/" + id; }',
);

$this->widget( 'application.extensions.jqGrid.CjqGridWidget', array(
'cssFile' => '/css/grid.css',
'name' => 'list',
'id' => 'list',
'baseUrl' => Yii::app()->baseUrl . '/extra/jqGrid',
'options' => $_arOptions,
'callbacks' => $_arCallbacks,
)
);
?>


I also put in an option to override the default CSS of the grid, it's the cssFile option at the component level.


Please let me know what you think!! Thanks!
1

#2 User is offline   SlowDown 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 27
  • Joined: 18-March 09

Posted 30 March 2009 - 09:02 PM

It would be nice, if there is a demo page.

-majin-
0

#3 User is offline   qiang 

  • Yii Project Lead
  • Yii
  • Group: Yii Dev Team
  • Posts: 5,897
  • Joined: 04-October 08
  • Location:DC, USA

Posted 30 March 2009 - 09:28 PM

Very nice. Could you please share it at http://www.yiiframew...com/extensions/
0

#4 User is offline   lucifurious 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 184
  • Joined: 15-March 09
  • Location:Atlanta, GA.

Posted 31 March 2009 - 01:23 AM

I sure will. I just want to put together a demo page that works with the blog sample app and an install guide.
0

#5 User is offline   lucifurious 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 184
  • Joined: 15-March 09
  • Location:Atlanta, GA.

Posted 31 March 2009 - 01:25 PM

I have a question. I will probably be building many extensions. And I want them all to descend from a common ancestor.

Would it behoove me to put my code under the /framework root so ALL my apps can share it? Or is there a preferred method/best practice for this?
0

#6 User is offline   Indra 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 2
  • Joined: 19-November 09

Posted 29 November 2009 - 12:05 AM

nice tutorial :)

can i get the complete source about this tutorial please, cos i'm a newbie?
0

#7 User is offline   lucifurious 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 184
  • Joined: 15-March 09
  • Location:Atlanta, GA.

Posted 29 November 2009 - 01:24 PM

This and a bunch of other extensions are in my psYiiExtensions extension. Check in the extensions section for the source.
0

#8 User is offline   mshakeel 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 9
  • Joined: 04-January 13

Posted 14 February 2013 - 02:56 AM

Can you provide a json datatype example, I am having problem to send json response from the controller.

Update

Here is the json example for anyone having problem:

Controller action:
public function actionData()
    {
        if (Yii::app()->request->isAjaxRequest)
        {
            $page = (isset($_GET['page']) ? $_GET['page']:'1');
            $limit = (isset($_GET['rows']) ? $_GET['rows']:'50');
            $sidx = (isset($_GET['sidx']) ? $_GET['sidx']:'modified_at'); // get index row - i.e. user click to sort
            $sord = (isset($_GET['sord']) ? $_GET['sord']:'desc'); // get the direction

            $model = Model::model();
            // set any conditions
            $model->property = $pro;

            $dataProvider = $model->getData($page, $limit, $sidx, $sord);

            $count = $dataProvider->totalItemCount;
            $total_pages = ($count) ? ceil($count/$limit) : 0;

            // prepare json data for jqGrid
            $response = new stdClass();

            $response->page = $page;
            $response->total = $total_pages;
            $response->records = $count;

            $data=$dataProvider->getData();

            foreach($data as $row)
            {
                $response->rows[]=array(
                    'id'=>$row->id,
                    'cell'=>array(
                        $row->modified_at,
                        $row->other_columns,
                    )
                );
            }
            echo CJSON::encode($response);
        }
    }


Model getData() method

public function getData($page, $limit, $sidx, $sord)
    {
        $criteria=new CDbCriteria;

        $criteria->compare('property', $this->property);

        $dataProvider = new CActiveDataProvider('Model', array(
            'criteria'=>$criteria,
            'pagination'=>array(
                'currentPage'=>$page-1, // CActiveDataProvider is zero-based, jqGrid not
                'pageSize'=>$limit,
            ),
            'sort'=>array(
                'defaultOrder'=>"$sidx $sord",
            )
        ));

        return $dataProvider;
    }

0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users