Reading and saving properties in INI files.

I just wonder to store some of the setting outside Db and keep them in flat files (eg. properties ini files). There is already support for handling such file in php with standard parse_ini_file function. But what are you think about building some model for that purpose (e.g. extending CFormModel). Any solutions? Ideas?

Good idea, but has no business being in the Yii core. Better suited for Zii or just a standalone extension.

Here you are my first approach to this matter. This solutions is quite specific to what I need at the moment, but could be easly tailored to any other ;).

From functional point of view we need:

  • Retrieve setting stored in external file by its name

  • Editing settings values from website level

From technical point following tasks needs to be done:

  • Reading and parsing given Ini file

  • Fetching all settings set

  • Retrieving setting stored in fetched set

  • Creating form to change settings values and save them in the same ini file.

So, lets to the work.

First lets extend CFormModel, put class attributes, validation rules and meaningful labels. Next lets implement load method for reading INI file and save for update it. Finally make this class implementing singleton pattern (optionally, but recommended).


<?php

class EasySettingsForm extends CFormModel {


	/**

	* According to CFormModel convention all public attributes will be treated as modelled one.

	*/

	public $pageTitle;

	public $itemsPerPage;

	public $topMenu; // 1 - will be displayed, 0 - will not

	public $footer; // 1 - will be displayed, 0 - will not

	

	private static $instance;

	

	public function getInstance(){

		Yii::trace('getInstance() request','org.maziarz.test');

		if (is_null(EasySettingsForm::$instance)) {

			EasySettingsForm::$instance = new EasySettingsForm;

			EasySettingsForm::$instance->load(); 

			Yii::trace('New instance has been created', 'org.maziarz.test');

		}

		return EasySettingsForm::$instance;

	}

	

	public function rules(){

		return array(

			array('pageTitle, itemsPerPage', 'required'),

			array('itemsPerPage, topMenu, footer' , 'CNumberValidator'),

		);

	}


	public function attributeLabels(){

		return array(

			'itemsPerPage' => 'Items per page',

			'topMenu' => 'Display top menu',

		 	'footer' => 'Display footer items',

		);

	}

	

	public function load(){

		$ini_array = parse_ini_file(Yii::getPathOfAlias('webroot')."/protected/config/site.ini");

		$this->setAttributes($ini_array);	

	}

	

	public function save(){

		$configFile = Yii::getPathOfAlias('webroot')."/protected/config/site.ini";

		file_put_contents($configFile, ";config.ini");

		foreach ($this->getAttributes() as $name=>$value) {

			file_put_contents($configFile, "\n".$name." = ".$value, FILE_APPEND);

		}

		

	}


}

Now lets create some action for displaying and editing settings:




	public function actionSettings(){

		$settings = new EasySettingsForm;

		$settings->load();

		if (isset($_POST['EasySettingsForm'])){

			$settings->attributes=$_POST['EasySettingsForm'];

			if ($settings->validate()){

				$settings->save();

				Yii::app()->user->setFlash('settings', 'Site settings has been successfully saved.');

			}

		}

		

		$this->render('easySettings', array('settings'=>$settings));

	}



and relevant view:




<?php if(Yii::app()->user->hasFlash('settings')): ?>

<div class="confirmation">

	<?php echo Yii::app()->user->getFlash('settings'); ?>

</div>

<?php endif;?>


<?php echo CHtml::errorSummary($settings); ?>


<?php echo CHtml::beginForm('','post',array('enctype'=>'multipart/form-data')); ?>


<div class="row">

	<?php echo CHtml::activeLabel($settings,'pageTitle'); ?>

	<?php echo CHtml::activeTextField($settings,'pageTitle'); ?>

</div>


<div class="row">

	<?php echo CHtml::activeLabel($settings,'itemsPerPage'); ?>

	<?php echo CHtml::activeTextField($settings,'itemsPerPage'); ?>

	<?php echo CHtml::error($settings,'itemsPerPage'); ?>

</div>


<div class="row">

	<?php echo CHtml::activeLabel($settings,'topMenu'); ?> <br></br>

	<?php echo CHtml::activeRadioButtonList($settings, 'topMenu', array("1"=>"Yes", "0"=>"No"));?>

	<?php echo CHtml::error($settings,'topMenu'); ?>

</div>


<div class="row">

	<?php echo CHtml::activeLabel($settings,'footer'); ?> <br></br>

	<?php echo CHtml::activeRadioButtonList($settings, 'footer', array("1"=>"Yes", "0"=>"No"));?>

	<?php echo CHtml::error($settings,'footer'); ?>

</div>


<div class="row">

	<?php echo CHtml::submitButton('Save'); ?>

</div>


<?php echo CHtml::endForm(); ?>



Finally we can use our settings object wherever we want. Almost wherever ;) - at least we can use it in view files -> in particular layout view is good example where you will can easily disabling or enabling different stuff (menus, portlets, stylesheets, etc.).

Example:




..

<title><?php echo EasySettingsForm::getInstance()->pageTitle ?></title>

</head>


<body>

	<div id="container" class="c">

		<div id="header" class="c is_container">

			<div id="headerTop"></div>

			

			<div id="headerBody"></div>

			<?php if (EasySettingsForm::getInstance()->topMenu): ?>		

			<div id="headerMenu">

				<?php $this->widget('EWTopMenu'); ?>

			</div>

			<?php endif; ?>

		</div>

..



I hope some of you found this solution helpful or even interesting. I am also sure some will find here a field for enhancements - you welcome.

I’m not sure about that. If you need to integrate other applications with Yii, then a IConfig interface might be nice. This might allow a developer to create multiple config files in config/, allowing a third-party application to use a format other than PHP arrays. Then third-party developers can handle the integration using a common methodology by writing abstract classes for the various configuration formats (ini, xml, yaml, db, etc.). There is already an effort underway to store params in a database by Jonah and Konstantin.

Edit: The Claw framework stored on Tigris.org includes an IConfig interface with two abstract classes for supporting two different formats. If anyone is interested, Checkout the Claw version in SVN, not the most recent download.

This would probably make a good cookbook article.

this is Good class for simple application config.

Here is a more modern version of the code for Yii 1.1.14 from 2013 year :)

this is code:

model:




<?php


/**

 * Iniconf class file.

 *

 * @author		Krivtsov Artur <gwartur@gmail.com> | Made in Russia

 * @copyright	No copyright, Public domain

 * @link		wartur.ru

 * @license		Public domain

 */


/**

 * @property string $filename finename

 */

class Iniconf extends CFormModel {

	const FILENAME = '/data/config.ini';

	

	/**

	 * @var string admin email

	 */

	public $adminEmail = 'my@email.ru';	// this is public property and default value


	/**

	 * @return string ini file name

	 */

	public function getFilename()

	{

		// i think what config has only static files, all dynamic put into /data

		return Yii::getPathOfAlias('application').self::FILENAME;

	}


	/**

	 * Yii init

	 */

	public function init()

	{

		// on init only try load

		$this->load();

	}

	

	/**

	 * Yii rules

	 * @return type

	 */

	public function rules(){

		return array(

			array('adminEmail', 'required'),

			array('adminEmail', 'email'),

		);

	}

	

	/**

	 * Yii attr labels

	 * @return type

	 */

	public function attributeLabels(){

		return array(

			'adminEmail' => 'Admin Email',

		);

	}


	/**

	 * load ini

	 */

	public function load(){

		// has file?

		if(is_file($this->filename))

		{

			$ini_array = parse_ini_file($this->filename);

			

			// has any data?

			if(!empty($ini_array))

				$this->setAttributes($ini_array);

		}

	}


	/**

	 * Buffered save

	 * @return boolean

	 */

	public function save(array $data){

		$this->attributes = $data;

		

		if(!$this->validate())

			return false;

		

		$writeBuffer = ';config.ini'.PHP_EOL;

		

		foreach ($this->getAttributes() as $name=>$value)

			$writeBuffer .= "$name = $value".PHP_EOL;

		

		// return number, it can >= 12, need check result in other code

		return file_put_contents($this->filename, $writeBuffer);	// rewrite

	}

}



controller:




<?php


class SettingsController extends BaseAdminController

{

	

	/**

	 * Index

	 */

	public function actionIndex()

	{	

		$data = new Iniconf();

		

		if(Yii::app()->request->isPostRequest)

		{

			if($_POST['Iniconf'])

			{

				if($data->save($_POST['Iniconf']))

				{

					Yii::app()->user->setFlash('success', 'ini save');

					$this->redirectHimself();		// e.g $this->redirect('/settings/index');

				}

			}

		}

		

		$this->render('index', $data);

	}

}



view:




<!DOCTYPE html>

<html lang="ru">

<head>

	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

	<title>{this->pageTitle}</title>

</head>

<body>

	do not worry this part not layout.

	this is THP templater if you need =) thp.wartur.ru

	also i'm build application with bootstrap www.cniska.net/yii-bootstrap

<!--// BEGIN --><!--<? $this->layout = 'base_admin' ?>-->


<div class="row">

	<div class="span11">

		<h2>Config</h2>

		<?php $this->widget('bootstrap.widgets.TbAlert', array(

			'block'=>true, // display a larger alert block?

			'fade'=>true, // use transitions?

			'closeText'=>'&times;', // close link text - if set to false, no close link is displayed

		)); ?>

		<?php $form = $this->beginWidget('bootstrap.widgets.TbActiveForm', array(

			'type' => 'horizontal',

			'htmlOptions' => array(

				'class'=>'well'

			),

		)); ?>


			<?php echo $form->textFieldRow($data, 'adminEmail', array(

				'class'=>'span3',

				'placeholder' => 'my@email.ru'

			)); ?>


			<div class="form-actions">

				<button class="btn btn-danger" type="submit">Save</button>

			</div>

		<?php $this->endWidget(); ?>

	</div>

</div>


<!--\\ END -->

</body>

</html>



I warn you that the data storage in the ini file is not safe

I wish you all good luck :rolleyes: