Php Articles

RSS Feed - Tag Php

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.

Push Forward

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:

  1. A developer that is a highly seasoned professional and core developer of CakePHP, he will be working on a startup in CakePHP.
  2. 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
  3. 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.
  4. 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:
  1. 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, 10

Since 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 span tag 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 replaceWith  so I can attach behavior to the link and I like how the code looks.

JavaScript:
  1. $(this).replaceWith(
  2.     $('<a>' + $(this).html() + '</a>').click(function(){
  3.         //code here
  4.     })
  5. );

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:
  1. $(document).ready(function(){
  2.     $('.get_comments').each(function(){
  3.         $(this).replaceWith(
  4.             $('<a>'+$(this).html()+'</a>').click(function(){
  5.                 link = $(this);
  6.                 $.post('comments/get/comments', {
  7.                     'data[Comment][id]': $(this).siblings('.author').attr('id')
  8.                 }, function(data){
  9.                     out = '';
  10.                     for (i in data) {
  11.                         prefix = data[i].Article.type ? 'article/' : 'answers/';
  12.                         out += '<li><a href="' + prefix + data[i].Article.slug + '#c' + data[i].Comment.id + '">' + data[i].Article.title + '</a>' +
  13.                             data[i].Comment.created + '</li>';
  14.                     }
  15.                     $('<ol>' + out + '</ol>').hide().appendTo(link.parents('li:first')).slideDown();
  16.                     $(link).replaceWith( $(link).html() );
  17.                 }, 'json');
  18.             })
  19.         );
  20.     });
  21. });

 

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 helper var $helpers = array('Time'); in the top of the controller.

PHP:
  1. function get($type = null)
  2. {
  3.     if ($this->RequestHandler->isAjax()) {
  4.         Configure::write('debug', 0);
  5.         if ($type == 'comments') {
  6.             $comment = $this->Comment->read(array('Comment.email'), $this->data['Comment']['id']);
  7.             $results = $this->Comment->find('all', array(
  8.                 'conditions' => array('email' => $comment['Comment']['email']),
  9.                 'fields' => 'Article.title, Article.slug, Article.type, Comment.id, Comment.created'
  10.             ));
  11.             $this->set(compact('results'));
  12.         }
  13.     }
  14. }

I also turn debug off with Configure::write('debug', 0);. Also, I only use $type  so 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:
  1. <?php
  2. if ($result):
  3.         if (isset($result['Comment']['created'])):
  4.             $results[$key]['Comment']['created'] =
  5.                 $time->timeAgoInWords($result['Comment']['created']);
  6.         endif;
  7.     endforeach;
  8.     echo  $javascript->object($results);
  9. ?>

 

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

CakePHP Logo Yahoo Search Boss Description

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.com as well as getting the total results found totalhits.  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_term from the params, then switch out the BOSS API key your_API_key and domain.com with yours. You get the API key by registering on the Yahoo! BOSS website.

PHP:
  1. //Gather data and prepare query
  2. $thequery = urlencode($search_term);
  3. $yhost = 'http://boss.yahooapis.com';
  4. $apikey = 'your_API_key';
  5. $site = 'site:yourdomain.com';
  6. $url = "$yhost/ysearch/web/v1/$thequery+$site?appid=$apikey&format=xml";
  7. // if (!empty($start)) $url .= "&start=$start"; Add this to provide paging
  8.  
  9. //Get the search results
  10. $ch = curl_init($url);
  11. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  12. curl_setopt($ch, CURLOPT_HEADER, 0);
  13. $data = curl_exec($ch);
  14. curl_close($ch);
  15. $results = new SimpleXmlElement($data, LIBXML_NOCDATA);
  16. $total = $results->resultset_web->attributes();
  17. $total = $total['totalhits'];
  18. $results = $results->resultset_web->result;
  19.  
  20. $yhost = 'http://boss.yahooapis.com';
  21. $url = "$yhost/ysearch/spelling/v1/$thequery?appid=$apikey&format=xml";

We can loop through our results in our view. $results will contain title, dispurl and abstract. I use the shortcut, <? which is shorthand for <?php echo.

PHP:
  1. <? $i=1; foreach($results as $result): if(!empty($result->title)): ?>
  2.     <li>
  3.         <span class="count">#<?= $i++; ?>:</span>
  4.         <? $title = str_replace('"', '"', $result->title); ?>
  5.         <a class="search_result" title="<?= $title; ?>" href="<?= $result->clickurl; ?>">
  6.             <?= $title; ?>
  7.         </a><br />
  8.         <span class="abstract">
  9.             <?= str_replace(
  10.                 array('<b>yourdomain.com</b>', '<b>YourDomain.com</b>', '<b>...</b>'),
  11.                 array('yourdomain.com', 'YourDomain.com', '...'),
  12.                 $result->abstract
  13.             ); ?>
  14.         </span><br />
  15.         <div class="url"><?= strip_tags($result->dispurl); ?></div>
  16.     </li>
  17. <? 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:
  1. //Get the suggested term
  2. $ch = curl_init($url);
  3. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  4. curl_setopt($ch, CURLOPT_HEADER, 0);
  5. $data = curl_exec($ch);
  6. curl_close($ch);
  7. $suggest = new SimpleXmlElement($data, LIBXML_NOCDATA);
  8. $suggest_term = $suggest->resultset_spell->result->suggestion;

Then it is easy to display the suggestion in our view:

PHP:
  1. <? if(!empty($suggestTerm)): ?>
  2.     <strong>Did you mean: <?= $html->link($suggestTerm, '/searches?q=' . $suggestTerm); ?></strong>
  3. <? 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 $start and $total variables in the URL to calculate the pages, and what page you are on.

PHP:
  1. <?php
  2.     if ($start > 0):
  3.  
  4.         if ($start == 10)
  5.             $startParam = '';
  6.         else
  7.             $startParam = "&start=" . ($start - 10);
  8.        
  9.         echo $html->link('Prev', $self . "?q=$searchTerm" . $startParam) . "&nbsp;&nbsp; ";
  10.     endif;
  11.  
  12.     $page = 0;
  13.     for ($i=0; $i<$total; $i=$i+10) {
  14.         $page++;
  15.    
  16.         if ($i > ($start-50) && $i < ($start+50)) {
  17.             if ($i == $start) {
  18.                 echo " $page ";
  19.             } else if ($i === 0) {
  20.                 echo $html->link($page, $self . "?q=$searchTerm");
  21.             } else {
  22.                 echo $html->link($page, $self . "?q=$searchTerm&start=$i");
  23.             }
  24.         } else if ($i == ($start-60)) {
  25.             echo '... ';
  26.         } else if ($i == ($start+60)) {
  27.             echo '... '; break;
  28.         }
  29.     }
  30.     echo " &nbsp;&nbsp;";
  31.  
  32.     if (count( $results ) === 10):
  33.         echo $html->link('Next', $self . "?q=$searchTerm&start=$next");
  34.     endif;
  35.  
  36.     $countPages = ceil($total / 10);
  37.     echo "&nbsp;&nbsp; ($total results on $countPages pages)";
  38. endif;
  39. ?>

Ok, putting it all together here is the full source.

Full source of controllers/searches_controller.php

PHP:
  1. <?php
  2. class SearchesController extends AppController {
  3.     var $name = 'Searches';
  4.     var $uses = array();
  5.  
  6.         public function index() {
  7.        
  8.         params['url']['q']) ? $this->params['url']['q'] : null;
  9.         $start = isset($this->params['url']['start']) ? $this->params['url']['start'] : 0;
  10.        
  11.         if (!empty($search_term)) {
  12.        
  13.             //Gather data and prepare query
  14.             $thequery = urlencode($search_term);
  15.             $yhost = 'http://boss.yahooapis.com';
  16.             $apikey = 'your_API_key';
  17.             $site = 'site:yourdomain.com';
  18.             $url = "$yhost/ysearch/web/v1/$thequery+$site?appid=$apikey&format=xml";
  19.             if (!empty($start)) $url .= "&start=$start";
  20.  
  21.             //Get the search results
  22.             $ch = curl_init($url);
  23.             curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  24.             curl_setopt($ch, CURLOPT_HEADER, 0);
  25.             $data = curl_exec($ch);
  26.             curl_close($ch);
  27.             $results = new SimpleXmlElement($data, LIBXML_NOCDATA);
  28.             $total = $results->resultset_web->attributes();
  29.             $total = $total['totalhits'];
  30.             $results = $results->resultset_web->result;
  31.            
  32.             $yhost = 'http://boss.yahooapis.com';
  33.             $url = "$yhost/ysearch/spelling/v1/$thequery?appid=$apikey&format=xml";
  34.            
  35.             //Get the suggested term
  36.             $ch = curl_init($url);
  37.             curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  38.             curl_setopt($ch, CURLOPT_HEADER, 0);
  39.             $data = curl_exec($ch);
  40.             curl_close($ch);
  41.             $suggest = new SimpleXmlElement($data, LIBXML_NOCDATA);
  42.             $suggest_term = $suggest->resultset_spell->result->suggestion;
  43.            
  44.             $next = $start + 10;
  45.             $this->set(compact('search_term', 'results', 'suggest_term', 'start', 'next', 'total'));
  46.            
  47.         }
  48.     }
  49.    
  50. }
  51.  
  52. ?>

Full source of views/searches/index.ctp

PHP:
  1. <?php if (!empty($searchTerm)): ?>
  2.    
  3. <?php
  4.     $this->pageTitle = "Search results for, \"$searchTerm\"";
  5. ?>
  6.  
  7.     <h2>Search results for, "<?= $searchTerm; ?>"</h2>
  8.     <? if(!empty($suggestTerm)): ?>
  9.         <strong>Did you mean: <?= $html->link($suggestTerm, '/searches?q=' . $suggestTerm); ?></strong>
  10.     <? endif; ?>
  11.     <div class="container search_results">
  12.         <? if(empty($results)): ?>
  13.             No results found for "<?= $searchTerm; ?>".
  14.         <? else: ?>
  15.             <h4>Results <?= $start+1 . '-' . (count($results)+$start) . ' of ' . $total; ?></h4>
  16.             <ul>
  17.                 <? $i=$start+1; foreach($results as $result): if(!empty($result->title)): ?>
  18.                     <li>
  19.                         <span class="count">#<?= $i++; ?>:</span>
  20.                         <? $title = str_replace('"', '&quot;', $result->title); ?>
  21.                         <a class="search_result" title="<?= $title; ?>" href="<?= $result->clickurl; ?>">
  22.                             <?= $title; ?>
  23.                         </a><br />
  24.                         <span class="abstract">
  25.                             <?= str_replace(
  26.                                 array('<b>yourdomain.com</b>', '<b>YourDomain.com</b>', '<b>...</b>'),
  27.                                 array('yourdomain.com', 'YourDomain.com', '...'),
  28.                                 $result->abstract
  29.                             ); ?>
  30.                         </span><br />
  31.                         <div class="url"><?= strip_tags($result->dispurl); ?></div>
  32.                     </li>
  33.                 <? endif; endforeach; ?>
  34.             </ul>
  35.            
  36.             <?
  37.            
  38.             if ($total > 10):
  39.                 if ($start > 0):
  40.                
  41.                     if ($start == 10)
  42.                         $startParam = '';
  43.                     else
  44.                         $startParam = "&start=" . ($start - 10);
  45.                        
  46.                     echo $html->link('Prev', $self . "?q=$searchTerm" . $startParam) . "&nbsp;&nbsp; ";
  47.                 endif;
  48.                
  49.                 $page = 0;
  50.                 for ($i=0; $i<$total; $i=$i+10) {
  51.                     $page++;
  52.                    
  53.                     if ($i > ($start-50) && $i < ($start+50)) {
  54.                         if ($i == $start) {
  55.                             echo " $page ";
  56.                         } else if ($i === 0) {
  57.                             echo $html->link($page, $self . "?q=$searchTerm");
  58.                         } else {
  59.                             echo $html->link($page, $self . "?q=$searchTerm&start=$i");
  60.                         }
  61.                     } else if ($i == ($start-60)) {
  62.                         echo '... ';
  63.                     } else if ($i == ($start+60)) {
  64.                         echo '... '; break;
  65.                     }
  66.                 }
  67.                 echo " &nbsp;&nbsp;";
  68.                
  69.                 if (count( $results ) === 10):
  70.                     echo $html->link('Next', $self . "?q=$searchTerm&start=$next");
  71.                 endif;
  72.                
  73.                 $countPages = ceil($total / 10);
  74.                 echo "&nbsp;&nbsp; ($total results on $countPages pages)";
  75.             endif;
  76.             ?>
  77.         <? endif; ?>
  78.     </div>
  79.    
  80.    
  81.  
  82. <?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:
  1. $javascript->link(array('jquery', 'form'), false);

Include the RequestHandler in your controller detect an Ajax save attempt.  Also include the JavaScript helper if you haven't already.

PHP:
  1. var $helpers = array('Javascript');
  2. var $components = array('RequestHandler');

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:
  1. if ($this->RequestHandler->isAjax()) {
  2.     if ($this->Article->save($this->data)) {
  3.         echo 'success';
  4.     }
  5.     Configure::write('debug', 0);
  6.     $this->autoRender = false;
  7.     exit();
  8. }

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:
  1. jQuery(function($){
  2.     $('<input type="button" value="Quick Save" />')
  3.         .click(function(){
  4.             $(this).parents("form:first").ajaxSubmit({
  5.                 success: function(responseText, responseCode) {
  6.                     $('#ajax-save-message').hide().html(responseText).fadeIn();
  7.                     setTimeout(function(){
  8.                         $('#ajax-save-message').fadeOut();
  9.                     }, 5000);
  10.                 }
  11.             });
  12.             return false;
  13.         })
  14.         .appendTo('form div.submit');
  15. });

This adds a button called, "Quick Save" to each form on the page where a div with class="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

CakePHP Logo  jQuery Logo 

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:
  1. cd /var/www/vhosts/domain.com/conf/
  2. vi vhost.conf

Then basically we are over riding the default settings of Plesk with our own. Setting open_basedir to allow the /cake path.

Apache:
  1. <Directory /var/www/vhosts/domain.com/subdomains/tools/httpdocs>
  2.         <IfModule sapi_apache2.c>
  3.                 php_admin_flag engine on
  4.                 php_admin_flag safe_mode off
  5.                 php_admin_value open_basedir "/cake:/var/www/vhosts/domain.com/subdomains/tools/httpdocs:/tmp"
  6.         </IfModule>
  7.         <IfModule mod_php5.c>
  8.                 php_admin_flag engine on
  9.                 php_admin_flag safe_mode off
  10.                 php_admin_value open_basedir "/cake:/var/www/vhosts/domain.com/subdomains/tools/httpdocs:/tmp"
  11.         </IfModule>
  12. </Directory>

Now we need to reload the vhost to include that loads our new config file.

Bash:
  1. /usr/local/psa/admin/sbin/websrvmng --reconfigure-vhost --vhost-name=domain.com
  2. service httpd graceful

Great! 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:
  1. $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:
  1. $this->AssetRule->create();
  2. $this->AssetRule->compact->css = array('site','global');
  3. $this->AssetRule->compact->scripts = array('jquery', 'ui.datepicker');
  4. $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:
  1. $this->AssetRule->create();
  2. $this->AssetRule->map->controller = 'posts';
  3. $this->AssetRule->map->action = 'admin_add';
  4. $this->AssetRule->compact->css = array('user');
  5. $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:
  1. $this->AssetRule->create();
  2. $this->AssetRule->map->action = 'admin_edit';
  3. $this->AssetRule->scripts = array('tiny_mce/tiny_mce');
  4. $this->AssetRule->codeblock = 'tinyMCE.init({
  5.    mode : "textareas",
  6.    theme : "advanced",
  7.    plugins : "media",
  8.    media_external_list_url : "media/list.js",
  9.    theme_advanced_buttons1 : "bold,italic,underline,separator,strikethrough,justifyleft,justifycenter,justifyright, justifyfull,bullist,numlist,undo,outdent,indent,redo,link,unlink",
  10.    theme_advanced_buttons2 : "",
  11.    theme_advanced_buttons3 : "",
  12.    theme_advanced_resizing : true,
  13.    theme_advanced_toolbar_location : "top",
  14.    theme_advanced_toolbar_align : "left",
  15.    theme_advanced_statusbar_location : "bottom",
  16.    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]"
  17. });';
  18. $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

Eclipse IDE Auto Complete

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.

Dreamweaver Add THTML

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:

  1. Edit your Extensions.txt file: C:\\Documents and Settings\\User\\Application Data\\Macromedia\\Dreamweaver 8\\Configuration
  2. Add in the new file extension into its type: HTM,HTML,HTA,HTC,XHTML,THTML:HTML Documents
  3. 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
  4. 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

Essential PHP SecurityPHP and MySQL Web DevelopmentPHP CookbookPHP SolutionsProgramming PHP

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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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:
    1. $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:
    1. $tags = array_count_values($DBtags); // a very useful function to count the tags!

    Wow gotta love those tags!

  6. 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:
  1. include(dirname(__FILE__)

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!