Tutorial: Remove index.php on localhost in Yii framework

This is another simple tutorial i find it useful for every Yii framework developer to know. Especially people who really want SEO capability on their Yii project. Without further crap from me, i will demonstrate how this can be done.

Setup Apache

The most important thing to get the thing you want in a local environment is to setup your Apache properly. Luckily setting up Apache for this tutorial wasn't that difficult, all you have to do is two step. Firstly, fire up your httpd.conf and look for the following line.

<Directory />
    Options FollowSymLinks
    AllowOverride None
    Order deny,allow
    Deny from all
</Directory>

Change AllowOverride None to Allow as shown below,

<Directory />
    Options FollowSymLinks
    AllowOverride All
    Order deny,allow
    Deny from all
</Directory>

Next we will have to enable mod_rewrite. Look for the following line,

#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
#LoadModule proxy_http_module modules/mod_proxy_http.so
#LoadModule rewrite_module modules/mod_rewrite.so
LoadModule setenvif_module modules/mod_setenvif.so
#LoadModule speling_module modules/mod_speling.so
#LoadModule ssl_module modules/mod_ssl.so
#LoadModule status_module modules/mod_status.so

And uncomment LoadModule rewrite_module bla bla bla and we should get something like this.

#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
#LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule setenvif_module modules/mod_setenvif.so
#LoadModule speling_module modules/mod_speling.so
#LoadModule ssl_module modules/mod_ssl.so
#LoadModule status_module modules/mod_status.so

Now, save this changes and restart your Apache server and we are done here.

Setup .htaccess

Now, the important thing is to tell Apache how to handle things that we are going to setup on our Yii framework. Hence, we will have to write some .htaccess rule to cater this. Firstly, where to place this?! Simple, we will put this just at the level where the "/protected/" folder is located (not inside the protected folder). Next, we should create a new .htaccess file with the following rules.

Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
RewriteBase /project name/

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# otherwise forward it to index.php
RewriteRule . index.php

The whole world are using this rule so i won't bother to explain but do take note that you will need to rebase your project by changing "project name" to your folder name on this .htaccess file. And we are done here too.

Setup Yii Config

Finally, we can setup our Yii config! Fire up your /config/main.php file and overwrite your UrlManager rules with the following one.

		'urlManager'=>array(
			'urlFormat'=>'path',
			'rules'=>array(
				'<controller:\w+>/<id:\d+>'=>'<controller>/view',
				'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
				'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
			),
			'showScriptName'=>false,
			'caseSensitive'=>false,
		),

Once you do this, you should be gettting your index.php removed permanently!

Tutorial: How To Reuse Yii Contact Form

Here's another tutorial on Yii framework that i find it useful to write it down. I was using Yii framework on a project currently and wanted to reuse Yii built-in contact from into another view. Reusing Yii framework contact form is pretty straight forward until i find my captcha not showing out for some reason. Lucky, i manage to get it work with a little debugging and decides to write it down just in case someone gets stuck as well.

Contact Form Model

In order to reuse Yii contact form, we do not need to rebuild another new model for our contact form. Unless you want to do something extra that will affect your existing contact form model, you can actually reuse the model without creating a new one. In short, we can skip the model šŸ˜€

Contact Form View

Next, the model part will also be pretty straight forward. Since we are using the same model, all you have to do is to copy the content on the contact form view into your own view file. In this case, it will be these:

<?php $form=$this->beginWidget('CActiveForm'); ?>

	<p class="note">Fields with <span class="required">*</span> are required.</p>

	<?php echo $form->errorSummary($model); ?>

	<div class="row">
		<?php echo $form->labelEx($model,'name'); ?>
		<?php echo $form->textField($model,'name'); ?>
	</div>

	<div class="row">
		<?php echo $form->labelEx($model,'email'); ?>
		<?php echo $form->textField($model,'email'); ?>
	</div>

	<div class="row">
		<?php echo $form->labelEx($model,'subject'); ?>
		<?php echo $form->textField($model,'subject',array('size'=>60,'maxlength'=>128)); ?>
	</div>

	<div class="row">
		<?php echo $form->labelEx($model,'body'); ?>
		<?php echo $form->textArea($model,'body',array('rows'=>6, 'cols'=>50)); ?>
	</div>

	<?php if(extension_loaded('gd')): ?>
	<div class="row">
		<?php echo $form->labelEx($model,'verifyCode'); ?>
		<div>
		<?php $this->widget('CCaptcha'); ?>
		<?php echo $form->textField($model,'verifyCode'); ?>
		</div>
		<div class="hint">Please enter the letters as they are shown in the image above.
		<br/>Letters are not case-sensitive.</div>
	</div>
	<?php endif; ?>

	<div class="row buttons">
		<?php echo CHtml::submitButton('Submit'); ?>
	</div>

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

That's it.

Contact Form Controller

The trick to reuse Yii contact form lies on the controller. The main code without saying would be the following one.

		$model=new ContactForm;
		if(isset($_POST['ContactForm']))
		{
			$model->attributes=$_POST['ContactForm'];
			if($model->validate())
			{
				$headers="From: {$model->email}\r\nReply-To: {$model->email}";
				mail(Yii::app()->params['adminEmail'],$model->subject,$model->body,$headers);
				Yii::app()->user->setFlash('contact','Thank you for contacting us. We will respond to you as soon as possible.');
				$this->refresh();
			}
		}
		$this->render('contact',array('model'=>$model));

The render part you would need to change that because it will most likely be your view file instead. Next important things to take note of would be the captcha part.

You would need to add another method called 'action' which will define a need method for your captcha image to be created as shown below,

/**
	 * Declares class-based actions.
	 */
	public function actions()
	{
		return array(
			// captcha action renders the CAPTCHA image displayed on the contact page
			'captcha'=>array(
				'class'=>'CCaptchaAction',
				'backColor'=>0xFFFFFF,
			),
		);
	}

Next, you will need to add captcha action into your permission list in order for it to display out else you would find that your image will always be empty although you did everything correctly. (no permission. Hence, permission denial)

	/**
	 * Specifies the access control rules.
	 * This method is used by the 'accessControl' filter.
	 * @return array access control rules
	 */
	public function accessRules()
	{
		return array(
			array('allow',  // allow all users to perform 'index' and 'view' actions
				'actions'=>array('index','view', 'captcha'),
				'users'=>array('*'),
			),
			array('allow', // allow authenticated user to perform 'create' and 'update' actions
				'actions'=>array('create','update'),
				'users'=>array('@'),
			),
			array('allow', // allow admin user to perform 'admin' and 'delete' actions
				'actions'=>array('admin','delete'),
				'users'=>array('admin'),
			),
			array('deny',  // deny all users
				'users'=>array('*'),
			),
		);
	}

Once this is done, you should get your reused Yii contact form working on your new view file!

Summary

In case you are not interested in all of the tutorial, Here are the summary that you should take note of:

  • Contact Form is also build in MVC
  • You do not need to rebuild the model class unless you wanted something different
  • You will need to placed the code on the contact form controller into your own controller file
  • You will need to copy and paste the contact form view file into your own view file
  • Remmeber to add the action sections in order to use 'captcha' on your contact form.
  • Remember to allow 'captcha' action by users or else the image will never display

Tutorial: Populate 2 Model Information Using 1 Controller And 1 View In CRUD

Recently i have been playing with Yii to get something up for someone. However, i came into a lot of problem because i am very new to Yii framework. Hence, there is a lot of reading and try and error method for me. I faced a problem where i need to populate 2 model information into 1 view. However, the CRUD that produce the codes seems to only cater for 1 to 1 sort of relationship. Furthermore, there wasn't any good information on how to tackle this problem other than the one shown on the cook book. On the cook book, it is demonstrating a 1 to 1 model relationship. In my case, it is a 1 to many relationship. What should i do?

1 to many relationship model

The main problem that i'm facing was the 1 to many relationship between my model. I have a database structured similar to the one shown on my previous article, relational active record tutorial. The scheme is shown below,

CREATE TABLE IF NOT EXISTS `invoice` (
	`invoiceId` MEDIUMINT UNSIGNED NOT NULL,
	`invoiceTotalAmount` DOUBLE NOT NULL,
	PRIMARY KEY (`invoiceId`)
) 

CREATE TABLE IF NOT EXISTS `invoice_item` (
	`invoiceItemId` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
	`invoiceItemLineId` MEDIUMINT UNSIGNED NOT NULL,
	`invoiceId` MEDIUMINT UNSIGNED NOT NULL COMMENT "CONSTRAINT FOREIGN KEY (invoiceId) REFERENCES be_invoice(invoiceId)",
	PRIMARY KEY (`invoiceItemId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

In my situation, i have 1 invoice having multiple line of items. So how do i show it out on my invoice view and manage my InvoiceItem and Invoice model with 1 Invoice controller?

Requirement

You should have the following files for a 1 to many relationship model to populate on 1 view.

  • CRUD Invoice
  • Model InvoiceItem

That's it!

How to create a 1 to many relationship model to populate into 1 view

Firstly, you will have to setup the relationship between Invoice and InvoiceItem as shown on my previous tutorial. Once, you have done that, you shouldn't touch any of your models. The only thing you should be looking at would be the controller and view created by the Invoice CRUD.

Controller Setup

Majority settings will be done on the controller. Hence, it will be quite a challenge for me to explain it clearly without dumping you too much code. Therefore, i will only be focusing on the method actionUpdate which is the update page controller method. You should have a default controller method which only cater for 1 to 1 model controller method as shown below,

	public function actionUpdate()
	{
		$model=$this->loadModel();

		// Uncomment the following line if AJAX validation is needed
		// $this->performAjaxValidation($model);

		if(isset($_POST['Invoice']))
		{
			$model->attributes=$_POST['Invoice'];
			if($model->save())
				$this->redirect(array('view','id'=>$model->invoiceItemId));
		}

		$this->render('update',array(
			'model'=>$model,
		));
	}

Right. At this point, this is not something we want. Hence, we will have to modified it to handle 1 to many relationship models.

	public function actionUpdate()
	{
		$Invoice=$this->loadModel();
		$InvoiceItem=$this->loadManyModel($Invoice);
		// Uncomment the following line if AJAX validation is needed
		// $this->performAjaxValidation($model);

		if(isset($_POST['Invoice']) && isset($_POST['InvoiceItem']))
		{
			$Invoice->attributes=$_POST['Invoice'];
			$valid=$Invoice->validate();
			$i = 0;
			$InvoiceItemLine = new InvoiceItem;
			foreach($_POST['InvoiceItem'] as $item){
				if(isset($InvoiceItem[$i]))
					$InvoiceItemLine = $InvoiceItem[$i];
				$InvoiceItemLine->attributes= $item;
				$valid=$InvoiceItemLine->validate() && $valid;
				if(!$valid){
					$valid = false;
				}
				$i++;
			}

			if($Invoice->save() && $valid){
				$i = 0;
				foreach($_POST['InvoiceItem'] as $item){
					if(isset($InvoiceItem[$i]))
					$InvoiceItemLine = $InvoiceItem[$i];
					$InvoiceItemLine->save();
					$i++;
				}
				$this->redirect(array('view','id'=>$Invoice->invoiceId));
			}
		}

		$this->render('update',array(
			'Invoice'=>$Invoice,
			'InvoiceItem'=>$InvoiceItem,
		));
	}

You will notice that the above method has been modified significantly to cater for 1 to many relationship. In this method, there is one new method named 'loadManyModel'. This method basically uses the lazy loading approach to get the many relationship model record into display and is shown below,

	public function loadManyModel($model)
	{
		if($this->_models===null)
		{
			if(isset($_GET['id']))
				$this->_models=$model->invoiceItems;
			if($this->_models===null)
				throw new CHttpException(404,'The requested page does not exist.');
		}
		return $this->_models;
	}

The method will required to take in the loadModel return value which is the single relationship model object so that we can use the lazy loading approach. If you have no idea what i am talking about, please read my previous tutorial before coming here. This is being done this way to improve the efficiency and reduce the number of SQL query being called by using the eager loading approach. (take note that there is a new global variables called $_models which you would have to declared next to the global variables $_model)

I'm pretty lazy to explain what is the actionUpdate method is trying to do. But i will explain the concept behind this which it probably makes more sense. In my case, there are two model where Invoice only has 1 records but InvoiceItem would have more than 1 records. By using the loadManyModel, i can retrieved these many records object into a variables and passed it into my view for display. Once, the user hits submit, i will have to loop through the items that has been submitted for those records that are in InvoiceItem and perform the same verification and methods as a single record by looping each individual records in InvoiceItem. Just that simple šŸ™‚

Setting up the view

If you think you could use the variables that you just passed through the controller, by editing _form.php, you might be wrong. You would also required to update the corresponding method view files to take in the new variables as shown below,

<?php
$this->breadcrumbs=array(
	'Invoices'=>array('index'),
	$Invoice->invoiceId=>array('view','id'=>$Invoice->invoiceId),
	'Update',
);

$this->menu=array(
	array('label'=>'List Invoice', 'url'=>array('index')),
	array('label'=>'Create Invoice', 'url'=>array('create')),
	array('label'=>'View Invoice', 'url'=>array('view', 'id'=>$Invoice->invoiceId)),
	array('label'=>'Manage Invoice', 'url'=>array('admin')),
);
?>

<h1>Update Invoice <?php echo $Invoice->invoiceId; ?></h1>

<?php echo $this->renderPartial('_form', array('Invoice'=>$Invoice, 'InvoiceItem'=>$InvoiceItem)); ?>

Btw, the above code is took from my view/update.php file where you see at the last sentence i passed in the variables that i have given through my controller. Once you have setup the view, you should be able to used it on the view/_form.php file. In this file, you should only see the code generated for Invoice class. All you have to do is to loop through the InvoiceItem variables that you have just passed through the controller and display it out as shown below,

	<?php echo $form->errorSummary($InvoiceItem); ?>
<?php
foreach($InvoiceItem as $item){
?>
	<div class="row">
		<?php echo $form->labelEx($item,'['.$item->invoiceItemLineId.']invoiceItemLineId'); ?>
		<?php echo $form->textField($item,'['.$item->invoiceItemLineId.']invoiceItemLineId' ); ?>
		<?php echo $form->error($item,'['.$item->invoiceItemLineId.']invoiceItemLineId'); ?>
	</div>

<?php }	?>

Just that simple! And i spend a few days to figure this out! hahaha! This way, you can easily populate 2 modle information using just 1 controller and 1 view. In other words, 1 CRUD and 1 Model. Hope this helps someone else out šŸ™‚

Yii Relational Active Record Tutorial

Yii website has an excellent written document on relational active record. I was confused at first with the example and some of the terms Yii used for their relational active record that really cause me to waste some time on this section. Therefore i decided to write a quick and dirty tutorial on Yii relational active record hoping it will post some benefits for those who are still learning Yii framework.

Yii Relational Active Record Approaches

In Yii there are two approaches mainly the lazy loading approach and the eager loading approach. Both approaches have been documented on Yii relational active record tutorial. But if you are lazy to read, this is how each approach is being use. Lazy loading approach is use when you are dealing with 1 record and eager loading approach comes in handy when there are more than 1 records you wish to access. This is made in this way to reduce the number of join which lead to inefficiency according to Yii document page

Setup Relational Active Record

In order to get relational active record to work, we have to setup the relationship between each model. Assuming we have two table, Invoice and InvoiceItem tables as shown below,

CREATE TABLE IF NOT EXISTS `invoice` (
	`invoiceId` MEDIUMINT UNSIGNED NOT NULL,
	`invoiceTotalAmount` DOUBLE NOT NULL,
	PRIMARY KEY (`invoiceId`)
) 

CREATE TABLE IF NOT EXISTS `invoice_item` (
	`invoiceItemId` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
	`invoiceItemLineId` MEDIUMINT UNSIGNED NOT NULL,
	`invoiceId` MEDIUMINT UNSIGNED NOT NULL COMMENT "CONSTRAINT FOREIGN KEY (invoiceId) REFERENCES be_invoice(invoiceId)",
	PRIMARY KEY (`invoiceItemId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Now, if we use Gii to create our models, we will get 2 models, InvoiceItem and Invoice class model. Now we will need to overwrite the method 'relations' in Invoice class model in order to retrieve Invoice and its items into the same page. In this case, using relational active record is the best option we have. In order to use relational active record, we have to modified the Invoice class model method, 'relations'. Hence, in this method we will declare the following relationship with InvoiceItem.

	/**
	 * @return array relational rules.
	 */
	public function relations()
	{
		// NOTE: you may need to adjust the relation name and the related
		// class name for the relations automatically generated below.
		return array(
			'invoiceItems' => array(self::HAS_MANY, 'InvoiceItem', 'invoiceId', 'together'=>true ),
		);
	}

The above means that i have a 1 to many relationship with InvoiceItem class that share the same key called 'invoiceid' and i will name my relationship 'invoiceitems'. Usually here is the most confusing part for everyone to pick up (to setup a relationship). The format for this relationship can be seen on Yii documentation too but i will show it here just for your conveniences.

'VarName'=>array('RelationType', 'ClassName', 'ForeignKey', ...additional options)

In layman term, my relationship means "My invoice class model has a one to many relationship with InvoiceItem class model and we are tie together with our primary and foreign key which in this case foreign key is InvoiceItems". Hope it helps anyone stucked here to understand how a relationship can be initialized. Let's continue.

Getting The Relational Active Record

Now, we can get our counterpart data after we have setup our relational active record. In my case, I'm confused and stucked here since i am not very familiar with the example given in the document. Anyway, we can now utilized the two approaches mentioned previously to retrieve our relational active records. I will first introduce the lazy loading approach.

Lazy Loading Approach

Please bear in mind that the lazy loading approach will not automatically populate the wanted data to you. You will have to initialize it. And this is the mindset i had and got stuck with because i always though the "lazy" means i will not have to initialize it. Anyhow, assuming you have your crud setup for your invoice table. In this case, there should be a controller called 'InvoiceController.php'. In my example, i want my data for both table to appear on my create invoice page. In order to do that, i will have to go to the view folder and open up '_form.php' where the structure is being made as shown below.

<div class="form">

<?php $form=$this->beginWidget('CActiveForm', array(
	'id'=>'invoice-form',
	'enableAjaxValidation'=>false,
)); 

?>

	<p class="note">Fields with <span class="required">*</span> are required.</p>

	<?php echo $form->errorSummary($model); ?>
	
	<div class="row">
		<?php echo $form->labelEx($model,'invoiceId'); ?>
		<?php echo $form->textField($model,'invoiceId'); ?>
		<?php echo $form->error($model,'invoiceId'); ?>
	</div>
	
	<div class="row">
		<?php echo $form->labelEx($model,'invoiceTotalAmount'); ?>
		<?php echo $form->textField($model,'invoiceTotalAmount'); ?>
		<?php echo $form->error($model,'invoiceTotalAmount'); ?>
	</div>
</div>

Now, we will need to add in the lazy loading approach method.

$items=$model->invoiceItems;
foreach($items as $item){
	echo $item->invoiceItemLineId . "<br/>";
}

Take note that the invoiceItems is my relationship name that i have declared in my relations method on the model. You can also do it this way which is shown on Yii website.

// retrieve a record object
$invoice=Invoice::model()->findByPk(1);
// invoiceItems is the relationship name i have declared
$author=$invoice->invoiceItems;

Once we put this into the _forms structure file, we will have this.

<div class="form">

<?php $form=$this->beginWidget('CActiveForm', array(
	'id'=>'invoice-form',
	'enableAjaxValidation'=>false,
)); 
$items=$model->invoiceItems;
foreach($items as $item){
	echo $item->invoiceItemLineId . "<br/>";
}
?>

	<p class="note">Fields with <span class="required">*</span> are required.</p>

	<?php echo $form->errorSummary($model); ?>
	
	<div class="row">
		<?php echo $form->labelEx($model,'invoiceId'); ?>
		<?php echo $form->textField($model,'invoiceId'); ?>
		<?php echo $form->error($model,'invoiceId'); ?>
	</div>
	
	<div class="row">
		<?php echo $form->labelEx($model,'invoiceTotalAmount'); ?>
		<?php echo $form->textField($model,'invoiceTotalAmount'); ?>
		<?php echo $form->error($model,'invoiceTotalAmount'); ?>
	</div>
</div>

the above should just print out the line number of the items to show you how many items are available in this invoice.

Eager Loading Approach

Eager loading approach will be much MUCH easier. This can be easily figured just by reading what is written on Yii website. The eager loading approach required the word 'with' to join the 2 table together. A simple example which you can is shown below,

$invoices = Invoice::model()->with('invoiceItems')->findAll();

By the way, you can use this sentence anywhere as long as you need the data. What the above is saying in layman term is that "Using the Invoice class model, we fire up model to initial the db call and use the relationship called invoiceItems and show all results to me".

Summary

Hopefully this tutorial can help further explain what has already existed in the Yii documentation for anyone to get the hang of Yii relational active record. The tutorial here is pretty simple and high level. Hopefully to solve starters on Yii on issues on Yii relational active record.

WordPress 3.0 Plugin Activation Error – “Headers already sent”

Well, i have been customizing my WordPress a lot to produce something like the food directory or blogshopping tool which required a lot of hack on to WordPress to make everything work perfectly. Recently i have upgraded my WordPress to the latest version 3.0.1. Everything seems fine until one day i decided to enhance my site. Upon activting my WordPress plugin, an error message occurs stating "The plugin generated 3 characters of unexpected output during activation. If you notice ā€œheaders already sentā€ messages, problems with syndication feeds or other issues, try deactivating or removing this plugin.". Although the plugin successfully activated, it seems like there are some problem with the plugin that is causing this. I search high and low for it but couldn't seems to detect any header being sent explicitly without my knowledge. Ā To make matter worst, this caused all my timthumb (image of the fly) script to malfunction which caused ALL my images to break. Hence, none of the images generated by timthumb were generated on the website. This is disaster!!! Ā Why is my header being sent when there is NOTHING in my code that is sending it?!!! (panic)

I went to alert my hosting company (hostgator) about this and tried to resolved this on the server level as my test environment which is another host were functioning perfectly without causing me a single problem! However, they couldn't find any cause on their server side that may caused this problem and direct me back to the application problem and asked me to check my code. Puzzled by all the mystery that is happening on my test and live environment. I decided to look further into what could have happened. And here are some of the things that i found but doesn't happen to me.

Extra whitespace / Character

Extra whitespace or character before the tag will caused this to happen. This is a comment mistakes made by many new php developers. But in my case, this wasn't the problem.

My Situation

Soon, i found out my mistake. Apparently, my test environment server setting allows Unicode encoded file type to be read normally. However, the one on Hostgator only allows ANSCII to be read. Hence, all the Unicode encoded files were theĀ culprits that is causing all this problems. It seems like the php setting made on the server can caused this to happen as the file type unknown to the php parser seems to bypassed the php output buffer and sent out the plaintext mime type before everything else which caused my timthumb to not work properly (since image sent in jpg mime instead of plaintext). This might be the reason why WordPress is giving you a message of "The plugin generated 3 characters of unexpected output during activation. If you notice ā€œheaders already sentā€ messages, problems with syndication feeds or other issues, try deactivating or removing this plugin." when you try to activate the plugin. It can also be caused by other plugins builder who are unaware that this might happen as their environment works perfectly and yours doesn't. Oh, the reason why anyone would want to change the encoding fromĀ ASCIIĀ to other form of encoding can be due to special character or other languages writing that ASCII doesn't support. Hence, changing the file encoding types allows php to display out the correct message. (WordPress isĀ multilingual, this should happen more often than you think :)). Hope it helps šŸ™‚