Thursday, July 30, 2009

Cake PHP 1.2 - Applying CSS to forms.

Today I will explain in a nutshell how to apply a css style to a form generated by the Form Helper. My form is very simple and serves as a registration form. You can find how to create a model and a controller for such form here: http://planetcakephp.org/aggregator/items/1620-creating-a-community-in-five-minutes-with-cakephp. Therefore, I will focus only on how to apply some css to this form.

My form has only 4 required fields and is generated by the following view:

<div class="users form">	
<?php echo $form->create('User', array('class'=>'form'));?>
<p class="formtitle">Sign Up</p>
<fieldset>
	<legend class="formtitle">Please complete the form below.</legend>
	<?php
		echo $form->input('username',  array( 
			'label' => 'Login', 
			'div'=>'formfield', 
			'error' => array(
				'wrap' => 'div', 
				'class' => 'formerror'
				)
			));
		echo $form->input('password',  array( 
			'label' => 'Password', 
			'div'=>'formfield', 
			'error' => array(
				'wrap' => 'div', 
				'class' => 'formerror'
				)
			));
		echo $form->input('password_confirm', array(
			'label' => 'Confirm password', 
			'type'=>'password', 
			'div'=>'formfield', 
			'error' => array(
				'wrap' => 'div', 
				'class' => 'formerror'
				)
			));
		echo $form->input('name',  array( 
			'label' => 'Name', 
			'div'=>'formfield', 
			'error' => array(
				'wrap' => 'div', 
				'class' => 'formerror'
				)
			));
		echo $form->input('surname',  array( 
			'label' => 'Surname', 
			'div'=>'formfield', 
			'error' => array(
				'wrap' => 'div', 
				'class' => 'formerror'
				)
			));
		echo $form->input('email',  array( 
			'label' => 'E-mail', 
			'div'=>'formfield', 
			'error' => array(
				'wrap' => 'div', 
				'class' => 'formerror'
				)
			));
		echo $form->input('city_id',  array( 
			'label' => 'City', 
			'div'=>'formfield', 
			'error' => array(
				'wrap' => 'div', 
				'class' => 'formerror')));
	?>
</fieldset>	
<?php echo $form->end('Submit');?>
</div>

Some explanations:

  • To add a class to a form tag:
    $form->create('User', array('class'=>'form')) 
    generates:
    <form class="form" id="UserAddForm" method="post" action="/kultura/users/add">
  • To add a class to an input:
    echo $form->input('username',  array( 
    			'label' => 'Login', 
    			'div'=>'formfield', 
    			'error' => array(
    				'wrap' => 'div', 
    				'class' => 'formerror'
    				)
    			));
    generates this:
    <div class="formfield required">
    <label for="UserUsername">Login</label>
    <input name="data[User][username]" type="text" maxlength="20" value="" id="UserUsername" />
    </div>
    An error in this case will appear in:
    <div class=”formerror”></div>

A sample css style may look like this:

.form{
	font-family: Verdana;    
	font-size:1em;
	margin:1em;
	padding:1em;	
}

.form p.formtitle{
	color: #026475;
	font-size:1.3em;
	font-weight: bold;
}

.form fieldset{
	width:40em;
	border:1px solid #FFE545;	
	background-image: url(../img/Contact.png);
	background-position: bottom right;
	background-repeat: no-repeat;
}

.form fieldset legend{
	color: #026475;
}

.formfield{
	width:30em;
	padding:5px;
}

.formfield label{
	display:block;
	float:left;
	width:12em;
	padding:1px;
	color:#C2A34F;
	text-align:right;
}

.formfield input, .formfield  select{
	padding:0.15em;
	width:14em;
	border:1px solid #ddd;
	background:#FEFBAF;
	
	font-family: Verdana;    
	font-size:1em;
	
	-moz-border-radius:0.4em;
	-khtml-border-radius:0.4em;
}

.formfield input:hover, input:focus {
	border-color:#c5c5c5;
	background:#f6f6f6;
} 

.required input {
	border:1px solid #FBB829;
}

.form .submit input{
	font-family: Verdana;    
	font-size:1em;
	margin-top: 0.3em;
}

.formerror{
	position:relative;
	left:12.1em;
	color:#FBB829;
}

The result:

nazwy 

More interesting sources about how to use css with forms:

Very interesting article about forms design patterns:

and, b.t.w., some hint how to do CakePHP forms more secure using Security component:

Enjoy!

Tuesday, July 21, 2009

CakePHP 1.2 - dynamic application’s homepage – another approach.

One of the thing a webmaster has to deal with is to create a main page of a website. It was also my recent problem. I needed to create a prototype of app’s main page that should include some logo, some menu, the newest events (posts with event info added by users), login form and some footer. More or less such prototype:

 1

Some ideas that I found on Internet were:

  • to extend Pages Controller or,
  • replace the default route to point to another page.

More about the above here:
http://teknoid.wordpress.com/2008/11/17/get-yourself-a-new-home-alternative-to-homectp/
http://www.phpfreaks.com/forums/index.php?topic=240370.0
http://www.phpfreaks.com/forums/index.php?topic=240370.0

I chose another solutions: the elements.

First off all we need to have some basic functionality. In my case a basic EventsController and UsersController. First step is to create a layout. It is usual that a website uses at least two layouts. One for a home page and another one (or more) for subpages.

I called my layouts: mainpage.ctp for the main and default.ctp for the rest.

To define a layout for a page we have to go to the page’s controller. In my case I had to change PageController (which by default is in cake\lib\controller folder):

function display() {
    $this->layout = 'mainpage';
    (…)
}

Creating a layout is as easy as creating a website. We can find sample file default.ctp in cake/libs/view/layouts/ and customize it for our needs. We just have to remember about some variables that carry data from controller:

  • $title_for_layout – page title that is defined in controller's functions.
function view($id = null) {
   $this->pageTitle ='Events';
(...)
}
  • $scripts_for_layout – contain external sources for example scripts or css files added by html helper.
  • $content_for_layout - data gathered from controller.

To create the usual html content – meta tags, charset, form etc. we can use html helper. More about it here: http://book.cakephp.org/view/206/Inserting-Well-Formatted-elements

Now is the time to create elements. I needed four. Two for menus that are more or less static and another two dynamic. First one is a login form. Another one displays newest events on the main page. The elements are placed in app/views/elemens/.

menu.ctp:

<?php echo $html->link("home", "/", array(), null, false); ?>
<?php echo $html->link("events", "/events", array(), null, false); ?>

menufooter.ctp

<?php echo $html->link("Contact Us", "/kontakt", array(), null, false); ?>
<?php echo $html->link("Terms of Service", "/zasady", array(), null, false); ?>
<?php echo $html->link("Cooperate", "/wspolpraca", array(), null, false); ?>
<?php echo $html->link("Advertise", "/reklama", array(), null, false); ?>
			
<?php 
echo $html->link(
  $html->image('cake.power.gif', array('alt'=> __("CakePHP: the rapid development php framework", true),   'border'=>"0")),
  'http://www.cakephp.org/',
  array('target'=>'_blank'), null, false);?>

The first two are easy and there is nothing to explain.

For user authentication I used Auth Component (http://book.cakephp.org/view/172/Authentication). The default login action is users/login.

login.ctp

<?php
$session->flash();    
$session->flash('auth');
?>
<?php  
if(!$session->check('Auth.User')){
	echo "<p>Please log in.</p>";
	echo $form->create('User', array('action' => 'login'));    
	echo $form->input('username');    
	echo $form->input('password');    
	echo $form->end('Login');
	}
else{
	echo "<p>Hello ".$session->read('Auth.User.name')."</p>";
	echo $html->link("logout", "/users/logout", array(), null, false);
}
?>

The last element – newest.ctp - must pick up some data from EventsController. For that I created a function in the controller that will provide the data for the element.

function main_show_newest(){
		$events = $this->Event->find('all', array('order' => array('Event.insert_date ASC'),'limit' => 3));
		return $events;
	}

Then in the element we use $this->requestAction('events/main_show_newest') to request data from the controller.

newest.ctp

<?php $events = $this->requestAction('events/main_show_newest');?>

<div id="newest">
<h1><?php echo __("Newest events around......") ?></h1>

<?php foreach($events as $event): ?>
<ul>
  <li>
   <div class="name"><?php echo $event['Event']['name']; ?></div>
   <div class="date"><?php echo $event['Event']['starting_date']; ?>-<?php echo $event['Event']['end_date']; ?></div>
   <?php echo $event['Event']['description']; ?>
   <?php echo $html->link("more", array( 'controller' => 'events', 'action'=>'view', 'id'=>$event['Event']['id']), null, false); ?>
  </li>
</ul>
<?php endforeach; ?>
</div>

There is also another possibility. In controller we use simply $this->paginate() and then in the element $this->requestAction('events/main_show_newest/sort:insert_date/direction:asc/limit:3').

To include an element menu.ctp to the layout use $this->element('menu').

The full mainpage layout:

mainpage.ctp
<?php echo $html->docType(); ?>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<?php echo $html->charset(); ?>
<title>	Kultura.info.pl - <?php echo $title_for_layout; ?></title>
	
<?php
 echo $html->meta('description','Best event tracker ever!');
 echo $html->meta('keywords', 'event, events');
 echo $html->css('kultura.generic');
 echo $scripts_for_layout;
?>
	
</head>
<body>
<div id="container">

<div id="header">
<?php 
echo $html->image('logo.jpg', array('alt'=> __("kultura.info.pl Strona główna", true), 'border'=>"0")); ?>
</div>

<div id="menu">
<?php echo $this->element('menu');?>
</div>
		
<div id="left">
<?php echo $this->element('login');?>
</div>
		
<div id="content">
<?php $session->flash(); ?>
<?php echo $content_for_layout; ?>
</div>
		
<div id="footer">
<?php echo $this->element('menufooter');?>
</div>

</div>
	
</body>
</html>

The default.ctp layout at this moment has only one difference in comparison to mainpage.ctp layout: it has no login form. I use it everywhere but the main page. Because I left the name default.ctp for this layout I don’t need to declare or configure it anywhere.

And that’s it. I hope it gives a basic overview of this concept.

Friday, July 17, 2009

CakePHP 1.2 and “Cannot modify header information” warning. (Not a whitespace problem)

Yesterday I spend too much time trying to solve following problem. While working with CakePHP, writing some login function, I need to use redirection.

But the redirection didn’t work:

Cannot modify header information - headers already sent by (output started at
C:\xampp\htdocs\cool\app\controllers\users_controller.php:1)

Of course I checked all my code and then looked for some solution on Internet. The one, that repeats from page to page was that there is a white space or another character after “?>”.

But not in my case.

In my app everything start to work after I changed the encoding of the files from UTF8 to ANSI. It works also with UTF8 without BOM. It seems to be more or less obvious, but without diving into the encoding issues it is difficult to explain.

And one more thing: the problem is related with PHP in general, not just CakePHP, so Cake stays blessed (so far…).

Small re-organization.

Over last few weeks everything has changed. I didn’t even have time to eat (despite the fact is my another hobby).

So far, I went into the other site of the browser and I’m working on a Web 2.0 project in CakePHP. It is a nice and useful framework, but I’m just a beginner so maybe my opinion will change. Time shrinks, and I still have a lot work to do, so keep fingers for me.