Thursday, February 10, 2011

MVC PHP Frameworks Evaluated

PHP frameworks are my current focus, particularly CodeIgniter. I have extensive experience with AS MVC frameworks, e.g. Cairngorm, RobotLegs, and Mate. Following a recent project, my thoughts on PHP frameworks are starting to crystallize. Here incidentally is IMO one of the best summaries on CI from the community at large, reposted courtesy of Action on this stackoverflow post :
CodeIgniter is, without a doubt, the most well-documented and approachable PHP MVC framework out there. CakePHP's documentation is also very good, but the learning curve is slightly higher.
  • CodeIgniter's models are optional and serve no function other than code separation. There's no integrated CRUD and its "Active Record" class is really just an alternative SQL syntax. It also doesn't support table associations. So, you will be building many large queries essentially from scratch. CakePHP's models are far more advanced and similar to those in Ruby on Rails. It supports table associations, has integrated CRUD, and behaviors. In addition, CakePHP has command line tools (Bake) that allow you to generate all the code for basic CRUD operations. Tweak a few things here and there and you've got a working prototype in minutes.
  • CodeIgniter lacks some essential libraries that are needed in most applications (i.e. Authentication and ACL). You will need to rely on 3rd party libraries in many of your applications. CakePHP has integrated Auth and ACL, but both frameworks integrate with 3rd party libraries easily.
  • Since CodeIgniter lacks much of the automation that CakePHP brings, there are no strict conventions to follow (this can be viewed as good or bad). This makes CodeIgniter a more flexible framework. Furthermore, its lack of features and automation do give it an advantage when it comes to speed. CodeIgniter is one of the fastest PHP MVC frameworks out there. That said, the framework is rarely the bottleneck in your in application...and you should be choosing a framework based on productivity, not its execution speed.
  • Both frameworks have large and helpful communities. CodeIgniter has their official forums and an IRC channel. CakePHP has a google group and an active IRC channel. CakePHP is currently the most popular PHP MVC framework with the largest community behind it.
In conclusion, if you want a fast, flexible, well-documented framework, and don't mind writing extra code and relying on 3rd party libraries, go with CodeIgniter. If you want a powerful, feature-rich framework that does most of the heavy lifting through automation, and you don't mind having to follow strict conventions, go with CakePHP.
As you can see, it is a comparison of CI with another framework, CakePHP, which is now on my shortlist to test out. Here is a Google Trends search of the relative trendiness of these two open-source frameworks (update: now three OS frameworks with symfony).

http://www.google.com/trends?q=codeigniter%2C+cakephp%2C+symfony&ctab=0&geo=all&date=all&sort=0

CakePHP has an earlier footprint. Next, I will examine CakePHP support of php5. I'm curious to know of any more advanced, more OO and generally more hurricane-nestled-in-a-typhoon-wrapped-in-a-tornado PHP frameworks out there. I would prefer a stricter MVC pattern with a higher degree of decoupling.

Thursday, December 9, 2010

AS3 Command Pattern for Rapid Asset Development

You know those little modular swf projects where you just program in some basic functionality in lieu of using the timeline? Well, I have had a few of those lately, so I made a little module manager.

It's based upon a useful tutorial I ran across and tweaked a bit. I created a google code repository to host the result, called as3minimalcommandpattern.

See this demo which uses only this library. All the code from the demo is pasted below ( the demo can be found with source in the repository).

The demo consists of the document class and as many 'scenes' as are required for the project. A scene in this sense is simply a way to encapsulate an intro-outro sequence. It's good for doing the monotonous work for you so you can focus on integrating advanced features.

Document Class:

package
{
import com.bluediesel.utils.managers.scenemanager.SceneManager;
import flash.display.MovieClip;
import scenes.*;

public class Main extends MovieClip
{
public function Main()
{
var sceneManager:SceneManager = new SceneManager();
sceneManager.setScene(new IntroScene(this));
}
}
}

IntroScene (Setup the UI):

package scenes
{
import com.bluediesel.utils.managers.scenemanager.commands.*;
import com.bluediesel.utils.managers.scenemanager.commands.greensock.*;
import com.bluediesel.utils.managers.scenemanager.scenes.*;
import com.greensock.plugins.*;
import fl.transitions.easing.Strong;

public class IntroScene extends AbstractScene implements IScene
{
public function IntroScene(c:Object) {

TweenPlugin.activate([AutoAlphaPlugin]);
super(c);
}

override public function createIntroCommand():Command
{
return new SerialCommand( 0.1,
new TweenMaxTo ( c.backgroundMC, 0.1, { blurFilter:{blurX:20, blurY:20} } ),
new TweenMaxTo ( c.backgroundMC, 0.4, { autoAlpha:1, blurFilter:{blurX:0, blurY:0} } ),
new ParallelCommand( 0,
new TweenMaxTo( [c.uiTopBanner, c.uiBottomBanner, c.buttonPrevious, c.buttonNext], 0.2, { autoAlpha:1 } ),
new TweenMaxFrom( [c.uiTopBanner, c.uiBottomBanner, c.buttonPrevious, c.buttonNext], 0.3, { y:500, easing:Strong.easeOut } )
),
new TweenMaxTo ( [c.appTitle, c.clickForSoundText], 0.2, { autoAlpha:1 } )
);
}

override public function onSceneSet():void {
sceneManager.setScene( new Scene01(c) );
}
}
}

Scene01 (Intro-Outro Sheep):

package scenes
{
import com.bluediesel.utils.managers.scenemanager.commands.*;
import com.bluediesel.utils.managers.scenemanager.commands.events.*;
import com.bluediesel.utils.managers.scenemanager.commands.greensock.*;
import com.bluediesel.utils.managers.scenemanager.commands.utils.SetProperties;
import com.bluediesel.utils.managers.scenemanager.scenes.*;

import fl.transitions.easing.Strong;

import flash.events.MouseEvent;
import flash.media.*;

public class Scene01 extends AbstractScene implements IScene
{
public function Scene01(c:Object)
{
super(c);
}

override public function createIntroCommand():Command
{
return new SerialCommand(0,
new TweenMaxTo ( c.sheepTitle, 0.2, { autoAlpha:1 } ),
new TweenMaxTo ( c.sheepMC, 0.2, { autoAlpha:1 } ),
new TweenMaxTo ( c.bodyCopyBG, 0.2, { height:231, width:470, autoAlpha:1, easing:Strong.easeOut } ),
new TweenMaxTo ( c.sheepBodyCopy, 0.2, { autoAlpha:1 } ),
new SetProperties ( [c.buttonPrevious, c.buttonNext, c.sheepMC, c.horseMC, c.pigMC], {buttonMode:true, useHandCursor:true} ),
new AddEventListener( c.sheepMC, MouseEvent.MOUSE_DOWN, playNoise ),
new AddEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious ),
new AddEventListener( c.buttonNext, MouseEvent.MOUSE_DOWN, handleNext )
);
}

override public function createOutroCommand():Command
{
return new SerialCommand(0,
new RemoveEventListener( c.sheepMC, MouseEvent.MOUSE_DOWN, playNoise ),
new RemoveEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious ),
new RemoveEventListener( c.buttonNext, MouseEvent.MOUSE_DOWN, handleNext ),
new TweenMaxTo ( c.sheepBodyCopy, 0.1, { autoAlpha:0 } ),
new TweenMaxTo ( c.bodyCopyBG, 0.1, { height:0, width:0, autoAlpha:0, easing:Strong.easeOut } ),
new TweenMaxTo ( c.sheepMC, 0.1, { autoAlpha:0 } ),
new TweenMaxTo ( c.sheepTitle, 0.1, { autoAlpha:0 } )
);
}

override public function onSceneSet():void
{
//TODO: implement function
}

/* CUSTOM METHODS */
private function handlePrevious(e:MouseEvent):void
{
trace ("Already At Beginning");
//sceneManager.setScene( new IntroScene( c ) );
}

private function handleNext(e:MouseEvent):void
{
sceneManager.setScene( new Scene02 ( c ) );
}

private function playNoise(e:MouseEvent):void{
var soundChannel:SoundChannel = new SoundChannel();
soundChannel = new SheepNoise().play();
}
/* END CUSTOM METHODS */
}
}

Scene02 (Intro-Outro Horse):

package scenes
{
import com.bluediesel.utils.managers.scenemanager.commands.*;
import com.bluediesel.utils.managers.scenemanager.commands.events.*;
import com.bluediesel.utils.managers.scenemanager.commands.greensock.*;
import com.bluediesel.utils.managers.scenemanager.scenes.*;

import fl.transitions.easing.Strong;
import flash.events.MouseEvent;
import flash.media.*;

public class Scene02 extends AbstractScene implements IScene
{
public function Scene02(c:Object)
{
super(c);
}

override public function createIntroCommand():Command
{
return new SerialCommand(0,
new TweenMaxTo( c.horseTitle, 0.2, { autoAlpha:1 } ),
new TweenMaxTo( c.horseMC, 0.2, { autoAlpha:1 } ),
new TweenMaxTo ( c.bodyCopyBG, 0.2, { height:231, width:470, autoAlpha:1, easing:Strong.easeOut } ),
new TweenMaxTo( c.horseBodyCopy, 0.2, { autoAlpha:1 } ),
new AddEventListener( c.horseMC, MouseEvent.MOUSE_DOWN, playNoise ),
new AddEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious ),
new AddEventListener( c.buttonNext, MouseEvent.MOUSE_DOWN, handleNext )
);
}

override public function createOutroCommand():Command
{
return new SerialCommand(0,
new RemoveEventListener( c.horseMC, MouseEvent.MOUSE_DOWN, playNoise ),
new RemoveEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious ),
new RemoveEventListener( c.buttonNext, MouseEvent.MOUSE_DOWN, handleNext ),
new TweenMaxTo ( c.horseBodyCopy, 0.1, { autoAlpha:0 } ),
new TweenMaxTo ( c.bodyCopyBG, 0.1, { height:0, width:0, autoAlpha:0, easing:Strong.easeOut } ),
new TweenMaxTo ( c.horseMC, 0.1, { autoAlpha:0 } ),
new TweenMaxTo ( c.horseTitle, 0.1, { autoAlpha:0 } )
);
}

override public function onSceneSet():void
{
}

/* CUSTOM METHODS */
private function handlePrevious(e:MouseEvent):void
{
sceneManager.setScene( new Scene01 ( c ) );
}

private function handleNext(e:MouseEvent):void
{
sceneManager.setScene( new Scene03 ( c ) );
}

private function playNoise(e:MouseEvent):void{
var soundChannel:SoundChannel = new SoundChannel();
soundChannel = new HorseNoise().play();
}
/* END CUSTOM METHODS */
}
}

Scene03 (Intro-Outro Pig):

package scenes
{
import com.bluediesel.utils.managers.scenemanager.commands.*;
import com.bluediesel.utils.managers.scenemanager.commands.events.*;
import com.bluediesel.utils.managers.scenemanager.commands.greensock.*;
import com.bluediesel.utils.managers.scenemanager.scenes.*;

import fl.transitions.easing.Strong;
import flash.events.MouseEvent;
import flash.media.*;


public class Scene03 extends AbstractScene implements IScene
{
public function Scene03(c:Object)
{
super(c);
}

override public function createIntroCommand():Command
{
return new SerialCommand(0,
new TweenMaxTo( c.pigTitle, 0.2, { autoAlpha:1 } ),
new TweenMaxTo( c.pigMC, 0.2, { autoAlpha:1 } ),
new TweenMaxTo ( c.bodyCopyBG, 0.2, { height:231, width:470, autoAlpha:1, easing:Strong.easeOut } ),
new TweenMaxTo( c.pigBodyCopy, 0.2, { autoAlpha:1 } ),
new AddEventListener( c.pigMC, MouseEvent.MOUSE_DOWN, playNoise ),
new AddEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious )
);
}

override public function createOutroCommand():Command
{
return new SerialCommand(0,
new RemoveEventListener( c.pigMC, MouseEvent.MOUSE_DOWN, playNoise ),
new RemoveEventListener( c.buttonPrevious, MouseEvent.MOUSE_DOWN, handlePrevious ),
new TweenMaxTo( c.pigBodyCopy, 0.1, { autoAlpha:0 } ),
new TweenMaxTo( c.bodyCopyBG, 0.1, { height:0, width:0, autoAlpha:0, easing:Strong.easeOut } ),
new TweenMaxTo( c.pigMC, 0.1, { autoAlpha:0 } ),
new TweenMaxTo( c.pigTitle, 0.1, { autoAlpha:0 } )
);
}

/* CUSTOM METHODS */
private function handlePrevious(e:MouseEvent):void
{
sceneManager.setScene( new Scene02 ( c ) );
}

private function playNoise(e:MouseEvent):void{
var soundChannel:SoundChannel = new SoundChannel();
soundChannel = new PigNoise().play();
}
/* END CUSTOM METHODS */
}
}
If you notice the repeating signature of a Scene class, the timeline functionality is controlled by createIntroCommand and createOutroCommand. Below those functions are any custom methods that the scene needs. There are ways this could be further packaged up, like an external class that contains all the helper functions. For now, this is a way to build a quick linear module or application without resorting to the timeline or to a more intensive solution such as a fully-fledged framework.

Wednesday, December 8, 2010

A Morsel on Flash Professional Projects in Flash Builder

Flash Builder is so very great to use. Such a time saver for the stuff I work on, especially in conjunction with subversion. Having a repository view of a remote SVN server, being able to update and commit, not to mention the ease of 'diffing' in the Eclipse SDK. Best thing ever.

Lock down FB and SVN and you will find varying project types to choose from: Flex Project, Flex Library Project, or Flash Professional Project. Please join me in considering the good and bad of the Flash Professional Project.

Here are the things I like and don't like about managing a Flash Professional Project from FB.

The Likes:
  • The .project configuration file
  • can be saved to SVN, so you can open all your resources quickly at later date
  • allows you to select .as files from within Flash Pro and have them open in FB for a better coding experience ( still better even thoughh Flash Pro now completes code)
The Dislikes:
  • The .project configuration file
  • because collaborators with whom you share the Flash Professional Project, since the .project file is generally only usable in one particular configuration, will find it distracting
  • Two environments (FB and Flash Pro) means 2x the path configuration. Yes, you have to path to libraries and swcs twice. Once in FB, and once in Flash Pro.
  • Frequent quickly appearing and then disappearing phantom publishing panels, due to the need for FB to remain synchronized with Flash Builder. I'm on Mac and this is one of the more disturbing issues.
  • The fact that you must create and save a .FLA as a precursor to starting a new Flash Professional Project, then associate the .FLA with the new Project, and then add the new document class back to the .FLA. Far better to have the .FLA generated through FB and configured according to initial project values.
Seems like the nays have it this time. I find it works better to use subclipse to manage FLAs without converting them into Flash Professional Projects. The only tiny caveat to this approach is that you must open the associated .as files directly from FB rather than via Flash Pro.

It remains to be seen what Flash Builder "Burrito" adds to this mix.

ASDocr to generate ASDoc HTML documentation

Rather than use the command line tool ASDoc that ships with the Flex SDK, I'm trying out ASDocr released on the Grant Skinner's site, gskinner.com.

It's handy, just be sure to update your Air to the latest 2.x version (presently 2.5.1) and be sure also to grab the latest ASDocr, as several are linked from gskinner.com

Wednesday, November 10, 2010

Practical Explorations of Catalyst Workflow, Pt 2

dFurther into Catalyst, here are some details on its workflow implementation:

Catalyst is procedurally similar to IDE-driven Flash Development. In particular:
  • Imported objects are cut and pasted without regard to underlying code in the process of assembling and producing usable components
  • Graphics in Catalyst are nameless and instance-less and may well be duplicated many times in random fashion in the process of assembling components, resulting in a code-behind of untold and unmanaged complexity (until the developer gets a hold if it, that is)
  • Much like Flash circa v3, you can paint yourself into a corner if your work process is too design-centric. You must have an architectural view of what you are creating and scale it in anticipation of the final structured product. If you do not start with this view, unless it is a very simple project, you may get into a jam.
  • While Catalyst is a presentation tool, development and usability duties fall within its boundaries as well. One example is the need to create transparent hit-areas for buttons; this is a matter of function, not display.
Following on this, Catalyst in its current 1.0 state is more hybrid tool than design platform. The workflow role that it encapsulates is akin to that of a Flash web designer in a pure IDE environment.

The user must have an awareness of the developers' needs as well as graphical manipulation and timeline skills. A team member utilizing Catalyst would need equal measures of feedback from both dev and creative teams to be effective. Furthermore, upon the Catalyst user is placed the expectation of creating hierarchical, logical structures almost entirely from the mouse and clipboard.

As version 2.0 aka Panini moves to release, it will be interesting to see what's next.

Monday, November 8, 2010

Flash Catalyst and Flash Builder workflow

More workflow explorations incorporating Adobe Flash Catalyst. Among the hopeful tests and discoveries, a stark word of caution is a rare gem. Here is one from an article authored by Adobe's Andrew Shorten (italics mine):

It is not possible to re-open a Flex project in Flash Catalyst once you've imported it into Flash Builder. (The product teams will investigate this option for a future release, but it will not be available in the first release of Flash Catalyst.) To overcome this limitation, consider the other workflows in this article, in particular workflow 3, which extends the approach used here to support iterative development.
Workflow 3 incidentally outlines the use of 'Compare Project With Version' > [previous version] as a means of reconciling different code versions. This represents great hardship in a round-trip and, in light of just how different the versions would be, is IMO not a viable solution.

Further on this, a detailed blog entry regarding 'catalyst jailbreak for flex developers' is exploratory and non-committal in tone as it explores Catalyst integration. Here is a further obstacle to a Catalyst workflow that extends from a different angle on the quote above:

An important note: There isn't a place to set the ID on Flash Catalyst components. When a designer converts artwork to a component and then exports a custom component with multiple text inputs, say a registration form, none of those text inputs contain ID's.
And further:

...when the design changes and it does, the one way Flash Catalyst generated code has no knowledge of your so called ID's. The design has changed and all or nearly all the components are anonymous again. So the developer has to manually find and add the ID to each text input, radio button, button, each time the design is updated etc.
While there are workarounds, there is not yet a clean workflow to utilize.

Flash Catalyst Panini, you are up next.

Friday, November 5, 2010

Flash Builder 4 Workflow from Catalyst to Flex

Skin vs SparkSkin Classes in the Flash Builder 4.1 SDK

Seems the difference between the two is that SparkSkin adds some extensions that would likely not be used in the creation of a custom Spark Skin. Therefore, you can pretty much just use the Skin class if you are developing a custom implementation.

Here are a couple of resources with limited info on the topic:
http://forums.adobe.com/thread/465734?tstart=0
http://unitedmindset.com/jonbcampos/2010/06/02/the-difference-between-skin-and-sparkskin/