Changelogs: Forum v2.2

A new version of Forum has been released, version 2.2. Please download the new tag or view the documentation. If you have any questions, be sure to send me an email or comment on this post. If you run into any problems, be sure to report an issue on the Github repository.

Version: 2.2
Tested On: PHP 5.3, CakePHP 1.3.8
Requires: PHP 5.2, CakePHP 1.3
Commit Hash: 2a779098aae9e701a2502dd6140986074cbe4b28
Changes:

  • Added a subscriptions system for topics and forums (Issue #10)
  • Added a UpgradeShell which can be executed via console to handle complex version upgrades
  • Added a SubscriptionShell that can be setup via cron jobs to send user subscriptions at specific intervals
  • Added the AjaxHandlerComponent and TypeConverter to manage AJAX requests
  • Added topic subscriptions to the users dashboard
  • Refactored the InstallShell to use ConnectionManager::getDataSource() for all database queries instead of a Model, which fixes association errors (Issue #11)
  • Fixed any minor issues

Changelogs: Uploader v3.1

A new version of Uploader has been released, version 3.1. Please download the new tag or view the documentation. If you have any questions, be sure to send me an email or comment on this post. If you run into any problems, be sure to report an issue on the Github repository.

Version: 3.1
Tested On: PHP 5.3, CakePHP 2.0.3
Requires: PHP 5.2, CakePHP 2.0
Commit Hash: a0d0cbf3ac1062a31facab1b021969dcb39df49d
Changes:

  • Added append and prepend support to Uploader::upload() and AttachmentBehavior
  • Added support for slashes within append and prepend options to allow for folder organization
  • Fixed a bug with FileValidationBehavior::extension()

Changelogs: Forum v2.1

A new version of Forum has been released, version 2.1. Please download the new tag or view the documentation. If you have any questions, be sure to send me an email or comment on this post. If you run into any problems, be sure to report an issue on the Github repository.

Version: 2.1
Tested On: PHP 5.3, CakePHP 1.3.8
Requires: PHP 5.2, CakePHP 1.3
Commit Hash: 93c192eebf704a997eb1a90ff1a394b28d8dacfb
Changes:

  • Added a users dashboard
  • Added Decoda validation during topic and post creation
  • Fixed the problem with using $this->here for form action URLs (Issue #9)
  • Updated Decoda to v3.1

Codebase Upgrades: Upgrading Uploader v2.8 to v3

The Uploader plugin has upgraded major versions from 2.8 (CakePHP 1.3) to 3.0 (CakePHP 2). Since this is not a backwards compatible change, the 3.0 version can only be used in CakePHP 2.

No more UploaderComponent

During the conversion process, the primary change is that the UploaderComponent was removed and turned into a stand-alone vendor class. This was done to further encourage the use of the AttachmentBehavior. Because of this change, you will need to manually initialize the Uploader in your Controller::beforeFilter() (only if you were using the component). The property settings are now passed as an array through the constructor.

// Before
public function beforeFilter() {
	$this->Uploader->tempDir = TMP;
}
// After
public function beforeFilter() {
	$this->Uploader = new Uploader(array('tempDir' => TMP));
}

By instantiating the class to $this->Uploader, all of the old code should still work as normal. This change also allows the possibility to use the Uploader anywhere in the application.

No more S3TransferComponent

The S3TransferComponent was trying to do something it shouldn't have. The component was removed in favor of the Vendor/S3 class that provides far more functionality. The AttachmentBehavior has also been updated to use the S3 class instead of the component.

Certain methods are now static

The bytes(), addMimeType(), checkMimeType(), mimeType() and ext() methods are now static.

Those should be the only changes you would need to make to support the new version. There are quite a few other changes, so be sure to check out the changelog and to also check out the updated documentation.

Codebase Upgrades: Upgrading AutoLogin v2.2 to v3

The AutoLogin component has upgraded major versions from 2.2 (CakePHP 1.3) to 3.0 (CakePHP 2), now 3.1. Since this is not a backwards compatible change, the 2.0 version can only be used in CakePHP 2. During the conversion process, many things had to change to support the new AuthComponent.

No more automatic username/password field detection

Previously AutoLogin would detect the custom name of your username and password fields by accessing Auth::$fields. Since that property has been removed in favor of Auth::$authenticate, the AutoLogin component received a new property called $fields which mimicked what the Auth's version did. I didn't want to magically guess what the username and password is from Auth::$authenticate and be wrong, so I made it an optional property (you should only need to change it if you are using custom fields).

$this->AutoLogin->fields = array('username' => 'user', 'password' => 'pass');

Seeing as how that was the only major change that may require backwards support, the following minor changes have also occurred: CakeRequest has been implemented anywhere $params were being referenced, the user model is detected via Auth::$authenticate (all first, then Form) in place of the deprecated Auth::$userModel and cookie support has been implemented for localhost environments (may not work in all environments).

If you have any questions, or run into any problems, let me know!

Codebase Upgrades: Upgrading AjaxHandler v1.6 to v2

The AjaxHandlerComponent has upgraded major versions from 1.6 (CakePHP 1.3) to 2.0 (CakePHP 2). Since this is not a backwards compatible change, the 2.0 version can only be used in CakePHP 2. During the conversion process, many other features were refactored or removed, here is a quick round-up.

No more automatic data gathering

In the older versions, any request data was gathered and merged into the Controller::$data property within AjaxHandler::startup(). This is no longer the case as I highly suggest using the new CakeRequest class to manipulate data in your AJAX action. This class can be accessed at Controller::$request.

No more content type restrictions

Previously you could only respond() an action using JSON, HTML, XML or text. Now you can respond using any content type defined in CakeResponse. Automatic JSON/XML formatting is still enabled.

Removed the valid() method

The valid() method was a very abstract and useless method that simply checked that strings or integers were not empty. The AjaxHandler should not be dealing with any validation (low level ones even more so), so it was removed. Please integrate your own data validation.

Besides the major changes listed above, any private class properties were converted to protected visibility, to allow class extension and the RequestHandler was replaced with CakeRequest in most situations, ala is('ajax') compared to isAjax().

If you have any questions, let me know!

Docsinated

I'm quite ashamed. I work constantly on all my PHP codebases for myself and everyone that uses them... but... I rarely update my actual documentation. After multiple emails from frustrated developers letting me know my docs make no sense and are quite outdated, I went on an update spree. I've spent the last few months updating all my codebases to a final stable release so that I may cease development on all of them (I need to free up some time). During this process I closed any outstanding issues and bugs, tagged new versions and have been recently converting all my CakePHP scripts to use the latest 2.0 version. Alongside that, I took the time to update all the documentation and changelogs on this site; the docs will always reference the latest version and any old docs have been removed.

I've always had a place in my heart for my stand-alone PHP scripts as they are very simplistic implementations of what I needed back in the day. It's quite heartening to get emails from new developers letting me know these scripts have taught them. Here's the current list:

As for my CakePHP codebases, I will constantly keep those updated if any bugs come up, but will not be adding any new features for the most part. Since CakePHP 2 was released, I had to spend some time updated all my projects. I created new branches for the old 1.3 codebase and turned the master branch into the new 2.0 repository. By doing this, it allows me to keep both CakePHP versions up to date in parallel. It also allowed me to tag new major versions for all my projects. Below are my current projects (with possibly more to come):

I hope all of you using my documentation find it useful. If you ever find something incorrect or not easily explained, be sure to shoot me an email and I will tackle it as soon as possible! Thanks for using my code :]

Refactoring is fun

For over a year now I have been eager to redo the backend (built on CakePHP) of this site. I kept putting it off, until last week when I was fixing bugs that I realized it would be more beneficial to just rebuild the whole site. I wanted to redo both the PHP and the database tables, as the old tables were from a previous installation. Here's just a quick list of things I wanted to change:

  • Separate blog tags into its own table and setup a HABTM (was a column in each entry)
  • Remove ENUM fields from the database tables and use class constants
  • Use slugs for all dynamic URLs
  • Use the model's counterCache system instead of a count find()
  • Add a blog series system (example)
  • Fix the bugs in my comments and contact forms
  • Rebuild the code/script section and remove the old "versioning" system (since I use Github now)
  • Build an admin panel

So to begin this huge task, I created new database tables based on the architecture I wanted. Once done, I created all the models for each of these new tables (and made sure to keep the old models for importing). The next step was to create CakePHP shells that use the old models to generate the new data structure and save it into the new tables (while keeping legacy IDs intact). This database changed fixed the dislikes I had with the old table columns (by removing ENUMs), added the slug and ID fields where necessary, and removed the old and useless tables I don't have a need for. First task complete.

Now that the fun step was over, it was time to refactor all the old controllers and views. Most of the old controllers were re-usable (like blog, pages, comments and feeds), all I simply had to do was make sure the new column names were being used and add support for new features (blog series, etc). The most time consuming part in this process was splitting up the old resources controller into two new controllers: code and snippets. Since the code section of my site was re-built from the ground up, these controllers also had to be rebuilt. The new code structure only uses 3 tables compared to the previous 5, win! However, I still had a problem with old legacy URLs. The solution I went with, was to allow the old URLs to redirect to the new ones using a jump controller (which also powers my tiny URL system), as well as allowing the URLs to work with a slug or ID (very important). Example, all of these links lead to the same place.

http://milesj.me/code/cakephp/forum
http://milesj.me/resources/script/forum-plugin
http://milesj.me/c/13

At this point, I was extremely pleased with the refactoring process. The next and last step was to create an admin panel system for all types of content on the site (I didn't have one before). I decided to place all of this code within a plugin, as I didn't want to litter my controllers with admin_ methods and it gave me more control of security and management as it was self-contained. I was expecting this process to take weeks to finish, but I completed it in less than 8 hours, win again! I used the technique of having a single view template for both the add() and edit() methods and was able to re-use a lot of code (DRY for the win). I highly suggest this approach for anyone who needs an admin system.

All in all, the process wasn't as back breaking and time consuming as estimated. I basically rebuilt the whole site in under 2 weeks, working about 1 hour a day. If you are interested, here's a quick list of all the changes.

  • Importing of old data into new database tables
  • Refactor of old models, controllers and views
  • Moving tags into a HATBM table and model
  • Adding slugs to all URLs
  • New blog archiving system for date ranges, tags and topics
  • New blog series feature
  • Adding counterCache for comments and tags
  • Adding a jump controller to deal with legacy URLs and tiny URLs
  • Splitting of old resources controller into code and snippets
  • Rebuilding the code packages and versioning system

I wouldn't doubt it if I forgot something! But whats next you ask? The worst part of all, updating my documentation. Now that will take some time.

The end of the HtmlHelper

For the past two years, I have gotten pretty close with the HtmlHelper. It has been there for me, making my life easier than before. But that time has come to an end, so sorry HtmlHelper, you are just too much of a burden now a days. The HtmlHelper has been an amazing convenience by automatically building my anchor links, by creating images with the correct path, or linking my CSS from within separate views, and much much more. But why do all this when you can simply write the HTML yourself?

I am not sure why I didn't notice this sooner; I was probably just stoked on developing with CakePHP so I wanted to do everything the CakePHP way. Lately however, I have noticed that the HtmlHelper really isn't needed that much. I can only think of a few cases where it is needed: linking stylesheets/javascript dynamically, building breadcrumbs and creating routes. Everything else is just consuming PHP logic and processing time to render HTML, which you can simply write yourself in the first place and bypass the PHP interpreter.

Linking Routes

The primary use of the helper, but why not just use url() instead? By doing that you don't have to deal with the hassle of nesting your array of attributes, or escaping variables into the method call. You also don't have to worry about Cake being over zealous and escaping all your data. Take these examples, they deliver the same result.

// With the helper
<?php echo $this->Html->link('Anchor Link', array('controller' => 'news', 'action' => 'index'), array('title' => 'Anchor Title')); ?>
// Without the helper
<a href="<?php echo $this->Html->url(array('controller' => 'news', 'action' => 'index')); ?>" title="Anchor Title">Anchor Link</a>
// Or pure HTML if your routes never change
<a href="/news" title="Anchor Title">Anchor Link</a>
Doctypes and meta tags

Another example of using PHP to render HTML, when you can just write HTML. This gets even easier with HTML5.

<?php echo $html->docType('xhtml-trans');
echo $html->meta('keywords', 'miles johnson php mysql design code developement developer web production creation coding functions tutorials methods scripts packages open source cakephp cake bake controller component model behavior view helper'); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<meta name="keywords" content="miles johnson php mysql design code developement developer web production creation coding functions tutorials methods scripts packages open source cakephp cake bake controller component model behavior view helper" />
Images

This one I am still 50/50 on. It helps by determining the base URL, detecting asset timestamps and appending the alt attribute. But this of course can still be written normally as well. I will let you decide.

<?php echo $this->Html->image('logo.png'); ?>
<img src="/img/logo.png" alt="" />
Divs, paragraphs, tables, lists, script blocks

All of these should never be used in the view or layout. Using PHP to render basic HTML tags like this is absurd in my opinion.

So when should the helper be used?

Like I stated above, there are only a few times when the helper should be used. The first is asset linking (stylesheets and javascript). This allows you to include an asset from within any view, which is then output within $scripts_for_layout. Why is this so awesome? Simple, you can have a specific stylesheet for a specific page, without having to include it on all pages.

Secondly is building breadcrumbs. From within your view you can define the "top level" or "trailing" crumb, and within your layout you can define the base crumbs. This allows you to add multiple levels of crumbs within different layers of views. A quick example, which would give you the trail of: Blog -> Archives -> Blog Title.

// In the view
$this->Html->addCrumb('Archive', array('controller' => 'blog', 'action' => 'archive');
$this->Html->addCrumb($blog['Blog']['title'], array('controller' => 'blog', 'action' => 'read', $blog['Blog']['id']);
// In the layout
$this->Html->addCrumb('Blog', array('controller' => 'blog', 'action' => 'index');

Lastly, the primary reason that the HtmlHelper has all these convenience methods, is so that you can use them within other helpers. Since it's impossible to render HTML within PHP without string concatenation, the HtmlHelper gives other helpers the ability to render HTML easily without all the hardship. That is the primary reason of this helper.

Now all of this is a personal opinion of course, but since I didn't realize most of this for a while, I thought some of you might not have either. This is neither belittling the CakePHP dev team as they have done an awesome job so far, so thank you! So take it how you wish and code how you like. Enjoy!

Using the session within models

This is something that everyone wants to do, but are afraid it breaks the MVC paradigm. Theoretically, the session should be a model, seeing as how it represents data and manages adds, edits, deletes, etc. Regardless, it's a much easier approach to use the session within the model directly, instead of having to pass it as an argument within each method call. Other developers who have attempted this task either try to import the SessionComponent or to use $_SESSION directly.

If you use the component, then you are using the class outside of its scope (a controller helper). If you use the $_SESSION global, then you don't have the fancy Cake dot notation access (Auth.User.id, etc) as well as its session management and security. But don't worry, Cake comes packaged with this powerful class called CakeSession, which both the SessionComponent and helper extend. Merely instantiate this class within your AppModel and you are set.

// Import the class
App::import('Core', 'CakeSession');
// Instantiate in constructor
public function __construct($id = false, $table = null, $ds = null) {
	parent::__construct($id, $table, $ds);
	$this->Session = new CakeSession();
}
// Using it within another model
$user_id = $this->Session->read('Auth.User.id');

Now you have control of the session within the model, bundled with Cake's awesome session management.