Currently Browsing: Yii

PHP 7 from Docker container complete with Yii 2.0

Pretty cool, a PHP 7.0 (RC4) with Apache environment on Mac from scratch in a few minutes via Docker container:

https://gist.github.com/lgelfan/b4921393d41f345a15e9

Includes installation for minimal requirements to support Yii 2.0

Uses Vagrant + VirtualBox on Mac (or similar) or instead you can use any existing Docker host.

Multiple databases in Yii: MySql + SQLite

If you need two (or more) completely different db connections in the same app (e.g, MySql + SQLite), using this method works better than trying to dynamically change the static variable CActiveRecord::db. (If you are using multiple MySql dbs you could get away with a more simple solution like that, which could even take advantage of cross-db queries, which this method won’t.)

In this example, we want to read/write press release data to SQLite for the Press model:

  • main config file:
    'components'=>array(
      ....
    	'dbpress' => array(
    		'connectionString'	=> 'sqlite:' . '/path/to/press.db',
    		'charset' => 'utf8',
    		'class' => 'CDbConnection'
    	),
  • Press model:
    class Press extends CActiveRecord
    {
    // second db support:
        private static $dbpress = null;
     
        protected static function getPressDbConnection()
        {
            if (self::$dbpress !== null)
                return self::$dbpress;
            else {
                self::$dbpress = Yii::app()->dbpress;
                    self::$dbpress->setActive(true);
                    return self::$dbpress;
            }
        }
    
        public function getDbConnection()
        {
            return self::getPressDbConnection();
        }
    //
  • that’s it!

    Yii CArrayDataProvider add case-sensitive option

    In Yii if you use CArrayDataProvider for a data set, it will use PHP’s array_multisort for sorting, which is normally case sensitive so if you have any items that aren’t capitalized, it will mess up the order and is inconsistent with how MySql sorts.

    And now that I realize this extra sorting step is happening (and it certainly makes sense to do it that way, Yii doesn’t know where my data originally came from) — I may re-think where I’m using this to see if it can be done via a database (CActiveDataProvider) search since I’m usually fetching the data from the db anyway and it would be more efficient to have MySql sort it at the time it’s requested.

    In any case – if for whatever reason you are not using a db connection for the data or really do have to do some pre-processing before handing off to CArrayDataProvider, here is how you can add a case-sensitive switch:

    Click here to view code

    Override CArrayDataProvider to provide a custom sortData method and add an additional property to toggle the custom bit:

    class myCArrayDataProvider extends CArrayDataProvider {
    	public $casei = false;
     
    	protected function sortData($directions)
    	{
    		if(empty($directions))
    			return;
    		$args=array();
    		$dummy=array();
    		foreach($directions as $name=>$descending)
    		{
    			$column=array();
    			foreach($this->rawData as $index=>$data)
    				$column[$index]=is_object($data) ? $data->$name : $data[$name];
    			$args[]=&$column;
    			$dummy[]=&$column;
    			unset($column);
    			$direction=$descending ? SORT_DESC : SORT_ASC;
    			$args[]=&$direction;
    			$dummy[]=&$direction;
    			unset($direction);
    		}
     
    		if ($this->casei) { 
    			for ($i = 0; $i < count($args); $i++) {
    				$args[$i] = (is_array($args[$i])) ? 
                                              array_map('strtolower', $args[$i]) : $args[$i];
    			}
    		}
     
    		$args[]=&$this->rawData;
    		call_user_func_array('array_multisort', $args);
    	}
    }

    Then when creating your dataProvider:

    $dataProvider = new myCArrayDataProvider($proj_result, array(
      [rest of your params...]
    ));
    $dataProvider->casei = true;

    If you always wanted it case insensitive you could leave out the "casei" param.

    Yii: use ajax function via htmlOptions attribute

    If you want to trigger an ajax call on a Yii form element onchange, you can pass an array of ajax attributes as one of the “htmlOptions” like so:

    <?php echo $form->textField($model,'my_input_field',array(
    	'ajax' => array(
    		'type'=>'POST', //request type
    		'url'=>$this->createUrl('site/ajaxAction'), // url to call controller action
    		'success'=>' function(data) { $(\'#my_output_field\').val(data) }',// function to call onsuccess 
                 // "data" is returned data and function can be regular js or jQuery
                 // here we are are updating the value of another field that has id "my_output_field"
    	),
    	'size'=>60,
    	'maxlength'=>128,
    )); ?>

    and the (site) controller action would look something like:

    public function actionAjaxAction() {
      [check/filter inputs...]
      [data functions...]
      echo $my_val_string;
      Yii::app()->end()
    }

    Yii custom update for GridView widget

    Hooking into the ajax functions in Yii’s GridView and ListView widgets can be a bit tricky and the documentation isn’t always that clear. To do a simple custom filter, you can use something like this:

    <?php echo CHtml::dropDownList('Holidays[date]', $model->date, 
      array(
        '2011'=>2011,
        '2012'=>2012,
      ), array('onchange'=>"$.fn.yiiGridView.update('holidays-grid', {data: $(this).serialize()});") ); ?>

    Where “Holiday” is the name of the model used to populate the widget and “holidays-grid” is the html ID of the widget. (In real code I would fetch the drop-down values dynamically, but this shows how simple it can be.)

    One somewhat undocumented issue is that you must have a particular URL manager rule set or you will get unpredictable results when combining this with sorting and paging:

    see: www.yiiframework.com/…/19738-filter-after-sorting-problem-at-gridview/
    According to Qiang, “for gridview to work properly in ajax mode, you have to add a URL rule like the following if you use “path” format:”

    <controller:\w+>/<action:\w+>' => '<controller>/<action>

    This will put all filter conditions into the query string part instead of the pathinfo part.

    I spent a bit of time coming up with regex work-around until I found that :/

    « Previous Entries