Getting Projects Done in 2008
Tags: Business, CakePHP | 2 weeks, 4 days ago
I have decided to pour all of my resources and energy into getting four rather demanding projects done before the end of the year. This big end-of-year push has had a lot of thought behind it, which I wanted to share with you and document here on my blog.

The Projects
Of the four projects I am focusing on getting done, two are consulting jobs and two are my own start-up companies. With the start-ups, I have partnered with businesses to help define the products and they will sell them when the products are released to the public.
The Plan
My focus in November will be on consulting. Consulting is a great way to get temporary cash flow, through which I will be using to hire people to work on my startup companies. I will have a total of 4 contractors working for me in November.
The Logistics
UI skills are the most important for my consulting work, and CakePHP skills are most important for the startups. In case you are wondering who is working with me, here is a list of them and brief description of each:
- A developer that is a highly seasoned professional and core developer of CakePHP, he will be working on a startup in CakePHP.
- A developer that is a master of databases and does a lot of work with CakePHP, he will be working on a startup in CakePHP and on documentation
- A developer that is great at UI and CakePHP, however he will be consulting with me focusing on UI (jQuery/JavaScript/HTML/CSS) with me and has been for quite some time.
- A developer/designer who is good at a diverse amount of things, working with UI, straight PHP and some CakePHP. He will be working with me on UI consulting projects.
Though the above isn't 100% finalized, I am confident they will be joining me in these last two important months of the year - all of the projects fun to work on.
The Goal
It is my goal to have a company with products, partnerships and consulting. So far it has been working out brilliant and I only expect the great work to continue.
This will be a wild two months that I'm hoping to propel those working with me into a great 2009.
Top Commenters Page
Tags: My Work, jQuery, CakePHP, MySQL | on 19/8/08
An addition to my website is the top commenters page. Even though the page doesn't look that complex, there is still a bit going on behind the scenes.
To get the top commenter count I have to thank Ryan Peterson in helping me write this custom MySQL query. I used Group By to lump the results together based on the commenter's email address. Then use count(*) to count the number of records in the group. Also used the NOT function in MySQL to filter my email address.
Mysql:
SELECT `Comment`.`author`, `Comment`.`id`, `Comment`.`url`, count(*) AS `count` FROM `cake_comments` AS `Comment` WHERE 1 = 1 AND NOT(`Comment`.`email`=\'m@marcgrabanski.com\') GROUP BY `Comment`.`email` ORDER BY `count` DESC LIMIT 0, 10Since I didn't want to load all of the related comments at once, I decied to use a little jQuery and Ajax to show comments that they have made.
First, I put a
spantag around the comment count, because without JavaScript you won't see this functionality. On page load I swapped the spans into links with$(this).replaceWith('<a>' + $(this).html() + '</a>');Instead adding behavior later after append, I used a jQuery object inside
replaceWithso I can attach behavior to the link and I like how the code looks.JavaScript:
$(this).replaceWith( $('<a>' + $(this).html() + '</a>').click(function(){ //code here }) );Using CakePHP's JavaScript object generator,
$javascript->object($data);it was easy to send JSON back to the client and parse with jQuery. Here is the full source of the JavaScript file.JavaScript:
$(document).ready(function(){ $('.get_comments').each(function(){ $(this).replaceWith( $('<a>'+$(this).html()+'</a>').click(function(){ link = $(this); $.post('comments/get/comments', { 'data[Comment][id]': $(this).siblings('.author').attr('id') }, function(data){ out = ''; for (i in data) { prefix = data[i].Article.type ? 'article/' : 'answers/'; out += '<li><a href="' + prefix + data[i].Article.slug + '#c' + data[i].Comment.id + '">' + data[i].Article.title + '</a>' + data[i].Comment.created + '</li>'; } $('<ol>' + out + '</ol>').hide().appendTo(link.parents('li:first')).slideDown(); $(link).replaceWith( $(link).html() ); }, 'json'); }) ); }); });
Update: I think I'll post the CakePHP code just in case someone is interested. Here is the controller, I use the RequestHandler component
var $components = array('RequestHandler');and the Time helpervar $helpers = array('Time');in the top of the controller.
PHP:
function get($type = null) { if ($this->RequestHandler->isAjax()) { Configure::write('debug', 0); if ($type == 'comments') { 'fields' => 'Article.title, Article.slug, Article.type, Comment.id, Comment.created' )); } } }I also turn debug off with
Configure::write('debug', 0);. Also, I only use$typeso that I can setup my code to get types of data if I want later - more of a design pattern I typically follow.Then in my view I use the time helper and output a JSON object.
PHP:
<?php if ($result): $results[$key]['Comment']['created'] = $time->timeAgoInWords($result['Comment']['created']); endif; endforeach; ?>
To see in action, click the comments count next to someone's name on the top commenters page.
Q: What is the Best Image Uploader?
Tags: JavaScript, PHP | on 3/8/08
I desperately need and am looking for a solid way to upload and attach photos to my web applications. I have no problem coding uploading one at a time - that is simple - but what about uploading and managing multiple photos? SWFUpload has been my uploader of choice, but I have found it to be fairly shaky at times. Is there something better than SWFUpload, or is it the best?
Please keep in mind that I use PHP as my server-side technology, if that matters.
I'm giving away $10 via PayPal to the best answer.
Update: I'm looking for any method to upload images to web applications. This could be picasa, flickr, air application, normal forms with ajax ... whatever is easiest for people to get their pictures on web applications. Even though your ideas may not work for every case, I (and the community) need to hear your ideas and any methods used for uploading images.
Yahoo! Search BOSS Integration with CakePHP
Tags: CakePHP, Yahoo, API | on 17/7/08
![]()
I implemented full text search with the Yahoo! Search BOSS API on top of CakePHP in one night, ending at 5AM - lol. It has auto-suggest for spelling errors, and I added paging as well. The BOSS API impressed me, it was very clean and I was able to implement seamless full text search into my website.
The full source is at the bottom of the article, but let me walk you through the basic parts.
Start by gathering the results. I'm sure there is a better way to do this in CakePHP, but for now I'm using straight PHP5. I got most of this off Webmaster-Source, but I added a few things including: setting the site search context
site:domain.comas well as getting the total results foundtotalhits. You can see all the xml data fields that you get back on the search response fields page.Here is the code for gathering the search results, make sure you get
$search_termfrom the params, then switch out the BOSS API keyyour_API_keyanddomain.comwith yours. You get the API key by registering on the Yahoo! BOSS website.PHP:
//Gather data and prepare query $yhost = 'http://boss.yahooapis.com'; $apikey = 'your_API_key'; $site = 'site:yourdomain.com'; $url = "$yhost/ysearch/web/v1/$thequery+$site?appid=$apikey&format=xml"; // if (!empty($start)) $url .= "&start=$start"; Add this to provide paging //Get the search results $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $data = curl_exec($ch); curl_close($ch); $results = new SimpleXmlElement($data, LIBXML_NOCDATA); $total = $results->resultset_web->attributes(); $total = $total['totalhits']; $results = $results->resultset_web->result; $yhost = 'http://boss.yahooapis.com'; $url = "$yhost/ysearch/spelling/v1/$thequery?appid=$apikey&format=xml";We can loop through our results in our view.
$resultswill containtitle,dispurland abstract. I use the shortcut,<?which is shorthand for<?php echo.PHP:
<li> <span class="count">#<?= $i++; ?>:</span> <a class="search_result" title="<?= $title; ?>" href="<?= $result->clickurl; ?>"> <?= $title; ?> </a><br /> <span class="abstract"> $result->abstract ); ?> </span><br /> </li> <? endif; endforeach; ?>Next, we want to get any suggestions in case they spelled something wrong. This is obviously optional, but I think it is a key to search. People are going to misspell things (including me). Using the spelling API, look up the suggested search term.
PHP:
//Get the suggested term $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $data = curl_exec($ch); curl_close($ch); $suggest = new SimpleXmlElement($data, LIBXML_NOCDATA); $suggest_term = $suggest->resultset_spell->result->suggestion;Then it is easy to display the suggestion in our view:
PHP:
<strong>Did you mean: <?= $html->link($suggestTerm, '/searches?q=' . $suggestTerm); ?></strong> <? endif; ?>I also implemented paging in the view, this is probably pretty crappy code... written very close to 5AM so you may want to do this your own way. It basically uses the
$startand$totalvariables in the URL to calculate the pages, and what page you are on.PHP:
<?php if ($start > 0): if ($start == 10) $startParam = ''; else $startParam = "&start=" . ($start - 10); endif; $page = 0; for ($i=0; $i<$total; $i=$i+10) { $page++; if ($i > ($start-50) && $i < ($start+50)) { if ($i == $start) { } else if ($i === 0) { } else { } } else if ($i == ($start-60)) { } else if ($i == ($start+60)) { } } endif; endif; ?>Ok, putting it all together here is the full source.
Full source of controllers/searches_controller.php
PHP:
<?php class SearchesController extends AppController { var $name = 'Searches'; public function index() { params['url']['q']) ? $this->params['url']['q'] : null; //Gather data and prepare query $yhost = 'http://boss.yahooapis.com'; $apikey = 'your_API_key'; $site = 'site:yourdomain.com'; $url = "$yhost/ysearch/web/v1/$thequery+$site?appid=$apikey&format=xml"; //Get the search results $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $data = curl_exec($ch); curl_close($ch); $results = new SimpleXmlElement($data, LIBXML_NOCDATA); $total = $results->resultset_web->attributes(); $total = $total['totalhits']; $results = $results->resultset_web->result; $yhost = 'http://boss.yahooapis.com'; $url = "$yhost/ysearch/spelling/v1/$thequery?appid=$apikey&format=xml"; //Get the suggested term $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $data = curl_exec($ch); curl_close($ch); $suggest = new SimpleXmlElement($data, LIBXML_NOCDATA); $suggest_term = $suggest->resultset_spell->result->suggestion; $next = $start + 10; } } } ?>Full source of views/searches/index.ctp
PHP:
<?php $this->pageTitle = "Search results for, \"$searchTerm\""; ?> <h2>Search results for, "<?= $searchTerm; ?>"</h2> <strong>Did you mean: <?= $html->link($suggestTerm, '/searches?q=' . $suggestTerm); ?></strong> <? endif; ?> <div class="container search_results"> No results found for "<?= $searchTerm; ?>". <? else: ?> <ul> <li> <span class="count">#<?= $i++; ?>:</span> <a class="search_result" title="<?= $title; ?>" href="<?= $result->clickurl; ?>"> <?= $title; ?> </a><br /> <span class="abstract"> $result->abstract ); ?> </span><br /> </li> <? endif; endforeach; ?> </ul> <? if ($total > 10): if ($start > 0): if ($start == 10) $startParam = ''; else $startParam = "&start=" . ($start - 10); endif; $page = 0; for ($i=0; $i<$total; $i=$i+10) { $page++; if ($i > ($start-50) && $i < ($start+50)) { if ($i == $start) { } else if ($i === 0) { } else { } } else if ($i == ($start-60)) { } else if ($i == ($start+60)) { } } endif; endif; ?> <? endif; ?> </div> <?php endif; ?>Tutorial - CakePHP Ajax "Quick Save" with jQuery
Tags: CakePHP, jQuery | on 15/7/08
![]()
When you are in an administration panel, sometimes you want a "quick save" feature that allows you to save without leaving the page. Here is how to accomplish this with CakePHP and jQuery.
To start, download jQuery and the jQuery Form Plugin JavaScript. Include them in your view with the JavaScript helper:
PHP:
Include the
RequestHandlerin your controller detect an Ajax save attempt. Also include the JavaScript helper if you haven't already.PHP:
Next we want to override our save function with the ajax quick save. Put this right before your
$this->Model->save($this->data)in your save action.PHP:
if ($this->RequestHandler->isAjax()) { if ($this->Article->save($this->data)) { } Configure::write('debug', 0); $this->autoRender = false; }This detects if the request is ajax, then saves the data. Then it sends back a simple, "success" message to let you know things went fine. It also writes debug to 0 and doesn't render anything, then exits.
Lastly, lets create and include a JavaScript file that performs the quick save.
JavaScript:
jQuery(function($){ $('<input type="button" value="Quick Save" />') .click(function(){ $(this).parents("form:first").ajaxSubmit({ success: function(responseText, responseCode) { $('#ajax-save-message').hide().html(responseText).fadeIn(); setTimeout(function(){ $('#ajax-save-message').fadeOut(); }, 5000); } }); return false; }) .appendTo('form div.submit'); });This adds a button called, "Quick Save" to each form on the page where a
divwithclass="submit"exists (you may want to switch this to the id of the form you want to add quick save to). Then It also attaches a click event to the button that submits the form via the jQuery Form Plugin.In a few simple steps, we've created a quick save feature that saves your data whenever you want without leaving the page.
Version 2 of MarcGrabanski.com Launched!
Tags: My Work, CakePHP, jQuery | on 25/6/08
![]()
![]()
Note on June 27th: The website is now usable on IE6 and IE7.
Rebuilt from scratch on top of CakePHP and jQuery, version 2 of MarcGrabanski.com has been launched!
The first version was on top of PHP and HTML, which turned into a mess to maintain. This time I really thought long and hard on how to code the website from the ground-up. It is a psuedo CMS, blog and takes advantage of much of CakePHP's strengths. The code is 100% hand written in a text editor, as well as the design was done in illustrator. I purchased the computer/face, then heavily modified it and illustrated all of the items around the monitor and keyboard. I enjoy illustrating but don't get to do it very often (too much coding).
Finally I feel like I have a website to be pleased about - despite the compliments on the old website by far and wide audiences, I was never too happy with it. But, this one I enjoy and am pleased with. Which is ultimately what matters. With this website I will be able to post more (hopefully) and definately have a much easier time maintaining.
Enjoy the new website, I know I will!
P.S. Please report any bugs you find in the new site to m@marcgrabanski.com
CakePHP on Media Temple (dv) 3.5
Tags: CakePHP, PHP, Linux, Media Temple | on 18/6/08
![]()
A good thing to do when deploying CakePHP websites is to load one copy of the CakePHP core files onto your server, and point all of your domains to that core directory.
Media Temple's Plesk default configuration does not let PHP access files outside the website httpdocs. So we need to configure the domain's settings to have access to the root /cake folder, or wherever you happened to put the CakePHP root files.
First, let's make a new config file in our domain. Make sure you replace domain.com with your domain.
Bash:
cd /var/www/vhosts/domain.com/conf/ vi vhost.confThen basically we are over riding the default settings of Plesk with our own. Setting
open_basedirto allow the /cake path.Apache:
<Directory /var/www/vhosts/domain.com/subdomains/tools/httpdocs> <IfModule sapi_apache2.c> php_admin_flag engine on php_admin_flag safe_mode off php_admin_value open_basedir "/cake:/var/www/vhosts/domain.com/subdomains/tools/httpdocs:/tmp" </IfModule> <IfModule mod_php5.c> php_admin_flag engine on php_admin_flag safe_mode off php_admin_value open_basedir "/cake:/var/www/vhosts/domain.com/subdomains/tools/httpdocs:/tmp" </IfModule> </Directory>Now we need to reload the vhost to include that loads our new config file.
Bash:
/usr/local/psa/admin/sbin/websrvmng --reconfigure-vhost --vhost-name=domain.com service httpd gracefulGreat! Now we can have a central set of cake files and use it for each domain.
CakePHP Email Encoding
Tags: CakePHP | on 12/2/08
My email was not getting sent using the email component in CakePHP. I debugged inside the core and found out that my subject was getting changed to something like, "=?UTF-8?B?V2VsY29tZ...".
In talking to Larry (core CakePHP team), he educated me that this allows for characters other than the English language to be sent via email (UTF-8 encoding). Even though it apparently was correct, it was still not being received at my personal email or a Yahoo account. Gmail got it, the others didn't.
The Alternate Email Encoding (English only)
Larry showed me the alternative way to encode that works just fine for English characters. If you are having trouble with receiving these UTF-8 encoded emails, try sending with iso-8859-15 (only English characters). You can try the English encoding using this line of code:
PHP:
$this->Email->charset = 'iso-8859-15';This works for English-only applications, but if anyone can offer up a solution as to why Yahoo and Media Temple email accounts are not getting the UTF-8 emails please let me know. It is strange to me that gmail gets it just fine. Until then, I am only planning on sending English emails anyway so there is no issue until I build a multi-lingual application.
CakePHP Asset Mapper
Tags: CakePHP, PHP | on 26/12/07
Released my CakePHP Asset Mapper. I've always wanted an easier way to control my JavaScript and CSS includes and this is the solution I came up with. Controls the entire web application's includes in one file!
Here is an example of a mapping rule:
PHP:
$this->AssetRule->create(); $this->AssetRule->runRule();Accross the entire site, this would compact the site.css and global.css files together and run them through CSS Tidy. jQuery.js and ui.datepicker.js would be compacted together and then ran through JSMin.
Great, this simplifies a complex process of compacting and minimizing - while still giving you the freedom to include scripts normally. Compacting lowers the amount of http requests you website makes, increasing website performance. JSMin and CSS Tidy strip comments and whitespace leaving your files much smaller in size.
You can create a rule to include files in a specific controller and/or action with AssetRule->map and AssetRule->controller:
PHP:
$this->AssetRule->create(); $this->AssetRule->map->controller = 'posts'; $this->AssetRule->map->action = 'admin_add'; $this->AssetRule->runRule();This would only include your 'user.css' file in the, 'posts' controller - with the action, 'admin_add'. The css file would be compacted with the others.
Some scripts like TinyMCE, include other files (so it can't be compacted with the others) and requires a codeblock to be initialized. This configuration satisfies TinyMCE:
PHP:
$this->AssetRule->create(); $this->AssetRule->map->action = 'admin_edit'; $this->AssetRule->codeblock = 'tinyMCE.init({ mode : "textareas", theme : "advanced", plugins : "media", media_external_list_url : "media/list.js", theme_advanced_buttons1 : "bold,italic,underline,separator,strikethrough,justifyleft,justifycenter,justifyright, justifyfull,bullist,numlist,undo,outdent,indent,redo,link,unlink", theme_advanced_buttons2 : "", theme_advanced_buttons3 : "", theme_advanced_resizing : true, theme_advanced_toolbar_location : "top", theme_advanced_toolbar_align : "left", theme_advanced_statusbar_location : "bottom", extended_valid_elements : "a[name|href|target|title|onclick],img[class|src|border=0|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name],hr[class|width|size|noshade],font[face|size|color|style],span[class|align|style]" });'; $this->AssetRule->runRule();Coming from a heavy UI developer background, I can afford to be incredibly anal when managing my CSS and JavaScript files. I hope this saves you time, frustration and makes your developer-life better.
PHP Development in Eclipse
Tags: Free Tools, PHP | on 5/5/07
I have been writing a CakePHP application and quickly realized I need something better for PHP development then Dreamweaver. I found PDT (Eclipse with PHP Plugins) and I am amazed at the helper tools they have for PHP developers. It has auto complete, tree view to browse functions and syntax highlighting. I probably haven't scratched the surface for its features.
I was blown away with PDT I am going to use it from now on for PHP development. I had to do one setup for PDT to recognize CakePHP's thtml files:
- Window->Preferences
- General->Content Types->PHP Content Type
- Add in *.thml
Thats it! I'm looking forward to better application development with Eclipse.
Edit CakePHP's THML / CTP files with Dreamweaver
Tags: CakePHP | on 5/5/07
Note on July 15th, 2008: I haven't used dreamweaver for a long time, this article was written back in cake 1.1 days. I suggest Text Mate for Mac OS X, and E Text Editor for Windows.
I needed Dreamweaver to recognize a new file extension called, "thtml" for CakePHP. Here is how I got Dreamweaver to recognize new code extensions and highlight them correctly:
- Edit your Extensions.txt file: C:\\Documents and Settings\\User\\Application Data\\Macromedia\\Dreamweaver 8\\Configuration
- Add in the new file extension into its type: HTM,HTML,HTA,HTC,XHTML,THTML:HTML Documents
- Now Dreamweaver recognizes the new file types but to get code highlighting you need to edit your MMDocumentTypes.xml file: C:\\Program Files\\Macromedia\\Dreamweaver 8\\Configuration\\DocumentTypes
- Add in the new file extension to HTML winfileextension.
Here is the full article by Adobe: Adobe: Changing and adding file extensions recognized by Dreamweaver
5 PHP Book Purchases
Tags: Books, PHP | on 5/5/07
I purchased 5 PHP books from Bookpool this morning. I am really excited to learn PHP security and check out the cookbook PHP examples. A friend suggested the SAMS book and Programming PHP, 2nd Edition was the #1 best selling book on bookpool. I will let you know what I think of them when I get a chance to dig through them.
- Essential PHP Security - $18.50
- PHP and MySQL Web Development, 3rd Edition - $31.95
- PHP Cookbook, 2nd Edition - $28.50
- PHP Solutions: Dynamic Web Design Made Easy - $21.95
- Programming PHP, 2nd Edition - $24.95
Create a Blog from Scratch
Tags: PHP, MySQL | on 5/5/07
Note on June 23, 2008: The post you are reading is years old, but shows the trail of the beginnings of MarcGrabanski.com. Thank you for reading!
Without much prior PHP knowledge, I managed to program this blog in less than a week. Here is how I did it. Note: This is an overview and not actual code examples.
First, I went through Code Grrl's Build a Blog Tutorials
Sure, I could have stopped there but I really didn't like how the blog was programmed. I like this tutorial though because I dug through the code and learned basic PHP, but I wanted much more functionality.
I wanted clean URLs so I found Making "clean" URLs with Apache
I customized the url so I can add keywords inside it if I want. I love these clean URLs. Apache also has documentation on mod_rewrite.
Then I added user authentication
This was the most basic, yet effective login script I found. Of course I modded it to make it a little more secure.
I rewrote the code to use Object Oriented PHP
My blog was basically built but I didn't like how the code looked. I thought PHP looked horrible until I ran into some object oriented code. Makes things look much nicer.
Created Tag Cloud Navigation
I started with ByteMyCode's Tag Cloud code and then moved to selecting from the database with the "LIKE" MySQL commmand.
Mysql:
$sql = "SELECT * FROM php_blog WHERE tags LIKE '%$tag%' ORDER BY timestamp DESC";I also had to count the tags so I used the array_count_values PHP function.
PHP:
Wow gotta love those tags!
Added More Features
I added an image uploader to my admin page, RSS 2.0 and many minor tweaks until I had my very own customized blog built from scratch!
Some functions I found very useful on php.net:
PHP:
I now have my own custom blog! You can too! I heavily modified each example and made them my own. It feels so nice to have a custom blog!










