Route Deja Vu

I just had one of those odd moments that happen when you are stuck and are trawling through Google and you come across something you have written. At the moment I have a blessed respite from the usual rubbish at work of sorting out and extending other peoples bad code, and I am writing a pretty big website CMS using CakePHP. (I haven’t touched it for about a month and I had quite forgotten how pleasant it can be to work with.)

I’ve got a bit of a routing issue – well kind of – I have a situation like this:

I have a controller called Products based on a table called products that also uses a table called product_items (products is actually a table of product categories – but this is all in the name of nice urls, product_items contains the actual products)

For example:

  • http://www.somewebsite.com/products – shows you a list of product categories e.g. ‘Pet Foods’
  • http://www.somewebsite.com/products/pet_foods – shows you all the products within the ‘Pet Foods’ category
  • http://www.somewebsite.com/products/pet_foods/chappie – shows you the detail page for chappie

This all works fine – the way I have done it is to do the following, I have set up the following route:


$Route->connect('/products/(.*)', array('controller' => 'products', 'action' => 'index'));

So anything gets pointed at the index method in the controller, my code within the index method looks something like this:


function index($product_cat_url = null, $product_item_url = null) {

		
		
	if($product_cat_url)
			{
                           // check the product cat is valid
                        }

        if($product_item_url)
			{
                           // check the product item is valid
                        }

        etc.

This lets me pass all the actions through a single method – works great – but now I want to add locations so for instance I might have a url like:

  • http://www.somewebsite.com/products/london/pet_foods – shows you all the products within the ‘Pet Foods’ category in london

Obviously I can just add more code in the index method, but then this in itself might get really unwieldy – but it has got me thinking is there a better way of doing this? I was Googling and found the following thread where I used a beforeFilter() to pass it to a different controller – but then I might just end up duplicating loads of code in two methods…

A simple PHP calendar function

Recently I needed a calendar to display on a site. I looked around for something I could just cut and paste and drop in but I couldn’t find anything, but picked up something I had started about six months earlier and which had then fallen by the wayside. Now calendars aren’t actually very complicated, but they are a bit of a fiddle. The aim of this calendar function is therefore just to produce a skeleton calendar that I can drop into anything, you can stick anything you want in a calendar cell (day) – it doesn’t matter because all the code for content lives outside the calendar. There are css hooks for all the various cells, columns etc. so you can format it exactly how you want.

Calendar Screenshot

I wrote this as a CakePHP helper, but at the end of the day it is just a function and there is nothing Cake specific about it.


';
			
			$str .= '';
			
			$str .= 'prev' . ucfirst($month) . ' ' . $year . 'next';
			
			
			
			$str .= '';
				for($i = 0; $i < 7;$i++)
					{
						$str .= '' . $day_list[$i] . '';
					}
			$str .= '';
			
			$str .= '';
			
			// get the first day of the month
			
			
			$str .= '';
		
			
				
				while($day < $days_in_month)
					{
						$str .= '';
						
						
								
								for($i = 0; $i < 7; $i ++)
									{
									
										$cell = ' ';
										
										if(isset($data[$day]))
											{
												$cell = $data[$day];
											}
											
										$class = '';
										
										if($i > 4)
											{
												$class = ' class="cell-weekend" ';
											}
											
										
										if($day == $today)
											{
												$class = ' class="cell-today" ';
											}
									
										if(($first_day_in_month == $day_list[$i] || $day > 1) && ($day < $days_in_month))
											{
												$str .= '
' . $day . '
' . $cell . '
'; $day++; } else { $str .= ' '; } } $str .= ''; } $str .= ''; $str .= ''; return $str; } ?>

The function parameters are as follows:

  • $year – expects a year in 4 digit e.g. 2007
  • $month – expects a month in english e.g. january
  • $data – an array containing the data for each day of the month e.g.

    $data[2] = ‘This is an entry for the 2nd day of the month’;
    $data[24] = ‘A link for the 24th of the month‘;

    The data is any HTML you want – it is up to you to generate it yourself before you hand it to the calendar.

  • $base_url – the url to send the back / foward links on to i.e. the address of page (the calendar expects to be in a mod re-written situation e.g. www.flipflops.org/calendar/2008/june)

CSS


/* calendar CSS */

table.calendar {width: auto; border: 1px solid #cccccc; border-collapse: collapse; margin: 0px; padding: 0px; background-color: #ffffff;}
table.calendar th {background-color: #eeeeee; text-transform: none; color: #444444; padding: 4px; text-align: center; border: 1px solid #eeeeee;}

table.calendar th.cell-prev {text-align: left;}
table.calendar th.cell-next {text-align: right;}
table.calendar th.cell-header {width: 70px; border-bottom: 1px solid #cccccc;}
table.calendar td.cell-today {background-color: #e2e8f6;} /* today in the current month */
table.calendar td.cell-weekend {background-color: #F3F5EB;}
table.calendar td {border: 1px solid #cccccc;}
					
table.calendar td div.cell-number {text-align: right; font-size: 8px; color: #444444; display: block;}
table.calendar td div {display: block; font-size: 10px; text-align: left;}
table.calendar thead th {border: 1px solid #cccccc;}

I use this as a CakePHP helper – if anyone is interested I’ll put up the supporting code showing how it might be used in a view / controller situation.

CakePHP : clear app/tmp/model when you set DEBUG 0

I’m about to hand over a CakePHP based CRM system to the clients. Happy that everything was working as it should, I set DEBUG to 0. I immediately noticed that almost all the data had disapeared from the edit views and the detail views (although not the index views curriously eneough).

It took me quite a while to find the solution. It turns out that you have to delete all the files in the /app/tmp/model/ directory.

See CakePHP group Setting debug = 0 kills my app?

CakePHP : form field order problem when saving related models

On Friday afternoon I spent the last hour of my day pulling my hair out. I’ve been building a CRM system for a recruitment agency. We had just had a meeting and they asked me to change one of the relationships from Client hasAndBelongsToMany Location and Client hasMany Job to Client hasMany Job and Job hasAndBelongsToMany Location.

This should have been a quick simple change, but nothing was working. Nothing was erroring either though. This morning I came in and examined the $data POST arrays.

This is what I discovered:

This array saves the data:


Array
(
    [Candidate] => Array
        (
            [skill_1] => Swiming Instructor
            [skill_2] => French
            [skill_3] => 
            [skill_4] => 
            [skill_5] => 
            [title_1] => Mrs
            [forename_1] => Celine
            [surname_1] => Rogers
            [address] => 
            [postcode] => 
            [phone] => 
            [mobile] => 
            [notes] => 
            [email] => 
            [active] => 1
            [id] => 13
        )

    [Location] => Array
        (
            [Location] => Array
                (
                    [0] => 
                    [1] => 8
                    [2] => 11
                )

        )

)

This array doesn’t save the data:


Array
(
    [Location] => Array
        (
            [Location] => Array
                (
                    [0] => 
                    [1] => 10
                    [2] => 2
                )

        )

    [Job] => Array
        (
            [client_id] => 2
            [name] => Web Designer
            [summary] => Web Designer in Taunton
            [active] => 1
            [id] => 3
        )

    

)

The difference is in the working example the ‘key’ model in the relationship is listed first and the related model data (Location) is posted afterward.

I had a quick look at the views and ended up moving a hidden field (data[Job][client_id]) with the so that part of the ‘key’ model was posted first. Sure eneough the layout of the $data POST array had changed – and guess what the data saved correctly.


Array
(
    [Job] => Array
        (
            [client_id] => 2
            [name] => Web Designer
            [summary] => Web Designer in Taunton
            [active] => 1
            [id] => 3
        )

    [Location] => Array
        (
            [Location] => Array
                (
                    [0] => 
                    [1] => 10
                    [2] => 2
                )

        )

)

Now that I’ve got it working I can see that there is a certain sense to this – I can understand why it happens, but I certainly can’t remember reading anything about this.

The moral of the story? Maybe we need to think about the logical layout of forms more instead of shunting bits about until they look good (which is what I do in a rush). This isn’t necessarily a bad thing though, one of the benefits of using frameworks is that by following a set of rules we benefit from greater consistency of method in our code and therefore produce things that are more robust and bug free.