Saturday, February 13, 2010

How to Find Records using MySQL Match Against Query in CakePHP

To use MySQL Match Against Query in CakePHP:

$needle = 'Search String';

$conditions = array( 
 "MATCH(Post.title) 
  AGAINST('$needle' IN BOOLEAN MODE)" 
  );
 $matches = $this->Post->find('all', array('conditions' => $conditions));

It does not need any explanation. I hope.

Friday, February 12, 2010

CakePHP Select Empty

Say, you have a select box displaying dropdown options for categories. You want to give the user liberty to select nothing. In CakePHP it is pretty simple. Use 'empty' options in your view file.


<?php echo $form->input('category_id', array( 'empty' => '(choose one)')); ?>

Hope this helps someone.
Happy baking.

Tuesday, February 9, 2010

CakePHP hasAndBelongsToMany (HABTM) Join

Coming back to one of the most critical basics of Cake  - it is hasAndBelongsToMany (HABTM) relationship. You may know it is a type of relationship between two different database tables (Models) in CakePHP. A case study will explain its importance:

Say, you have Category model and Post model.
Each category has many posts and each post belongs to more than one categories.

Had there been only one category per post, it would be enough to use category_id field in posts table. But we are talking about posts each of which may have more than one categories. In this case besides categories and posts table, you need one extra table. By convention - this table should be named as categories_posts (note the alphabetical order of join tables). The table categories_posts will have following fields : id, category_id, post_id.

In your post model define the relationship.

<?php


class Post extends AppModel {
  var $name = 'Post';
  var $hasAndBelongsToMany = array(
  'Category' =>
  array(
  'className'              => 'Category',
  'joinTable'              => 'categories_posts',
  'foreignKey'             => 'post_id',
  'associationForeignKey'  => 'category_id'
  )
  );
  }


?>

This will show you all categories for a given post when you fire:

$this->Recipe->find();

Now the biggest question - how to SAVE records for HABTM association.

The default CakePHP functioning in this regard is not at all adequate. Say you want to add a new category to an existing post which already has a category defined by HABTM association - by default Cake will delete your existing category records for that post from categories_posts table before inserting the new record. That's an absolute mess if you do not really intend to desire existing records.  However, there is the savior.

ExtendAssociations Behavior.
This behavior allows you to easily add or delete HABTM associations!

A full detail of this Behavior can be found in Bakery!

You can download code for this behavior, and save that code as 'extend_associations.php' under '/app/models/behaviors' folder.

In your Post model, add the following code:


 <?php  
var $actsAs = 'ExtendAssociations'; 
?>  

So, our modified Post model should look like this:


 <?php  
class Post extends AppModel { 
    
var $name = 'Post'; 


var $actsAs = 'ExtendAssociations'; 
    
var $hasAndBelongsToMany = array( 
        
'Category' => array( 
            
'className' => 'Category', 
            
'joinTable' => 'categories_posts', 
            
'foreignKey' => 'post_id', 
            
'associationForeignKey' => 'category_id', 
        
), 
    
); 
} 
?>  

Defining ACTION in PostsController
file: // app/controllers/posts_controller.php
Now in your PostsController - to ADD categories to a post use:


 <?php 
// to add only one category for a given post (say, post_id = 1)
$this->Post->habtmAdd('Category', 1, 1); 
// to add multiple categories 
$this->Post->habtmAdd('Category', 1, array(1, 2, 3)); 
?>  

// to DELETE categories for a given post (say, post_id = 1)


 <?php 
// delete a category 
$this->Post->habtmDelete('Category', 1, 1); 
// to delete multiple categories
$this->Post->habtmDelete('Category', 1, array(1, 3)); 
//  to delete all categories 
$this->Post->habtmDeleteAll('Tag', 1); 
?>   


That's it.
Good night.
Sweet Baking.

Friday, February 5, 2010

How to Remove Mailed-by Header in CakePHP Email Component

It took me quite sometime to fix this issue.

You can send emails with built-in CakePHP Email Component. But if you are sending email to any gmail address (like your-name@gmail.com), you can see an annoying 'mailed-by' header. It shows the name of your server, like mailed-by: dreamhost.com. It discloses your webhost. You may not like it. You may want to remove/hide this information. To do so, you need only one line of CakePHP code.

I have used following sendEmail() function in my /app/app_controller.php

file:// /app/app_controller.php


function sendEmail($subject, $view, $to=null, $from = null, $fromName = null) {
/* This function will be used to send email in my CakePHP application */
$this->Email->to = $to;
$this->Email->subject = env('SERVER_NAME') . ' – ' . $subject;
$this->Email->from = $fromName.' <'. $from.'>';
$this->Email->template = 'default';
$this->Email->additionalParams = '-fnoreply@yourdomain.com';
/* the above line is required to remove 'mailed-by' header' */ 
$this->Email->sendAs = 'both';   // you probably want to use both :)
return $this->Email->send($view);
}


Done.

Note again, whenever you use CakePHP Email Component, just add one extra line:

$this->Email->additionalParams = '-fnoreply@yourdomain.com';

And you can safely remove that annoying 'mailed-by' header.

That's it.

Just a guess, noreply@yourdomain.com is NOT essential. You may change it to anything you want.

Take care.

Responses awaited.

Saturday, January 30, 2010

How to Generate Options for HTML Select Tags in CakePHP

How to generate options for HTML Select Tags in CakePHP?

As such

  1. You can retrieve options from a Model (Database Table).
  2. Or, you can specify options for select tag in view files.
Just keep in mind CakePHP does not support ENUM type field. So, you may not use 'ENUM' datatype in table fields.

To retrieve options from Database Table

Say, you have two Models (tables) - Order and Size; 

Fields in 'sizes' table:
id, size
And fields in 'orders' table:
id, quantity, size_id

In your OrdersController file (/app/controllers/orders_controller.php)
$sizes = $this->Order->Size->find('all');
$this->set('sizes',$sizes);

Now in your view file: (/app/views/orders/add.ctp)

echo $form->create('Order');
echo $form->input('quantity');
echo $form->input('size_id');
echo $form->end('Submit');

And done.

Note the use of variable names. 
It is important to name the variables as per this convention. It will display all sizes available in database when you are adding a new order. To retrieve size field in table 'sizes', you need to specify variable name 'sizes' (note plural form) in your respective controller/action.  Now when you refer to 'size_id' in view file - cake will create a select tag on the fly with options retrieved from database.  

OK. It was pretty simple.

Now, how to display options for HTML Select Tag without any database table:

In this case, we have, say, only 'orders' table with following fields
id, quantity, size

In your view file (/app/views/orders/add.ctp), specify - 

$form->create('Order');
$form->input('quantity');
$sizes = array('s'=>'Small', 'm'=>'Medium', 'l'=>'Large');
echo $form->input('size', array('options'=>$sizes, 'default'=>'m'));
$form->end('submit');

That's it.

Friday, January 29, 2010

Managing Plugin URL in CakePHP

Just now I noticed my urls are getting deformed when I tried to access a plugin controller/action in my brand new CakePHP app. It took me sometime to understand that the problem was due to plugin call. When you are calling plugin controller/action - the url structure is:

 echo $html->link('Plugin Controller/Action Link', array('plugin' => 'plugin_name', 'controller' => 'controller_name', 'action' => 'action_name'));

The output will be:

http://example.com/plugin_name/controller_name/action_name

Make sure to mention 'plugin' => null in remaining urls, if you do not want to show plugin_name in other displayed links.

 echo $html->link('Non Plugin Link', array('plugin' => null, 'controller' => 'controller_name', 'action' => 'action_name'));

Output:

http://example.com/controller_name/action_name

If you do not specify 'plugin' => null, all the displayed links will show:

http://example.com/plugin_name/controller_name_1/action_name_1
http://example.com/plugin_name/controller_name_2/action_name_2
http://example.com/plugin_name/controller_name_3/action_name_3


From this what I get is that it is better to specify 'plugin' => null in case of all non-plugin links which are likely to be displayed in tandem with plugin links.



Or, there may be a short route - who knows?

Cake rocks... still learning each day...

Wednesday, January 27, 2010

How to Pass all URL Arguments to Paginator Functions

Use the following statement in view file for respective controllers/action:

$paginator->options(array('url' => $this->passedArgs));

This will retrieve all passed url arguments.

To specify which params to pass, use:

$paginator->options(array('url' =>  array("0", "1")));

Simple.

Thursday, January 14, 2010

CakePHP Pagination Change Label

Here we can change the displayed label for CakePHP Pagination:

Say, we want to sort results by user_id.

This is how it is done by default:

<?php echo $paginator->sort('user_id');?>


And to change the displayed label:
 <?php echo $paginator->sort('User', 'user_id');?>

Here User is the label which will be displayed.
user_id is the field to be sorted.

Tuesday, December 22, 2009

CakePHP Debug

Set 
Configure::write('debug',2); /* file://app/config/core.php
This will display basic debugging information like SQL dumps etc. If you can see the error message, fix it.


Also you may use this statement in your view file:
pr($this->validationErrors);
It shows all validation errors. This helps a lot in debugging cake.


A very helpful article I found:
CakePHP Tips

Monday, December 21, 2009

CakePHP Form Textarea Input Size

Default cake css defines all text inputs as having 
width 100%, so if you don't change that then the physical width of the 
textbox will not depend on the size attribute. 



Read more here.
Thanks.