RSS
 

Posts Tagged ‘Helper’

Helper? Component? Lib? CakePHP2

03 Apr

Note: For 1.3 see the old article on this subject.

I want to outline some ideas on how to chose the appropriate class type if you want to add some additional feature to your cake app.
For a beginner it might be difficult to decide what to use in which case. Hope this article helps to clarify things.
Feel free to comment on this below.

Overview

The following generic levels and types for extending functionality are available, each for its own domain inside MVC:

  • bootstrap functions (most generic methods – no class context)
  • Lib (most generic class)
  • Helper (view level)
  • Component (controller level)
  • Behavior (model level)

And of course the base classes: Datasource, Model and Controller (and View) which you can extend (and provide further functionality this way).

Level of Independence

We need to ask ourselves if this feature needs to interact with other cake elements, like controller, some components, models, …

If it needs to save to the session, or if it needs some controller functionality, it will have to be a component.
With initialize(Controller $controllerReference) and startup(Controller $controllerReference) this is very easy to accomplish.

With Cake13 libs have been introduced. Not every piece of "controller" code necessarily needs to be component anymore.
So if you retrieve an RSS feed or get the weather from a weather channel web service you could just make a clean and independent lib class. No need to extend the cake object or even pass the controller reference. Less memory and dependency is a good thing. And its easier to test, anyway.

Helpers are used if the result is in relation to the view – in other words if its directly related to output or generation of markup (HTML usually). If you want to retrieve some web service information and save it to the database use a component instead.

Database related?

Often times we need to adjust some model data, we either use a component first and then pass it to the model or we use beforeValidate() and beforeSave() in the model. Same goes for the other direction (from model to controller): afterFind() or a component call afterwards.
This is fine for custom changes. As soon as it could be something useful for several models, it might make sense to build a behavior. The code gets cleaner and your models more powerful.

Examples would be:
"Last Editor/Last Change" (see my WhoDidBehavior), "Geocoding" (see my GeocoderBehavior), "Auto-Capitalize first letter of name/title", "Format/Localize Date/Time" (see my DecimalInput or NumberFormat behaviors), "Working with passwords", "Slugging", "Soft Delete", "Serializable input/output", …

Extending base classes

Once you decided what part of MVC is relevant for the functionality you can then proceed with coding such a helper class or method.
Sometimes, it does’nt have to a new class, it can also just be part of the App base class (AppHelper, AppController, AppModel, AppShell, …). And if it is generic enough
to be of use to more than this single application, consider putting it into a generic My base class and let your App base classes extend those:

class MyHelper extends Helper {}
// and
class AppHelper extends MyHelper {}

Don’t forget the appropriate App::uses() statements, though.

This is mainly useful, if your functionality is of use to all extending classes or if it overwrites/extends existing methods.

Reducing code redundancy

Now that we have a vague understanding where to use what type of tool, we should think about cutting down the redundancy.
Lets say we use the vendor class "phpThump". We would have to write two wrappers. one for the helper (display images in the view) and one for the component (uploading images and resizing), maybe even for some behavior (validating + uploading + resizing). This wrapper handles default values from Configure::read() and other cake related settings.
In this scenario we should build one single library in /Lib, maybe called PhpthumbLib.php.
Here we put our wrapper with our custom functions.
Then we build a helper (view side) as well as a component or a behavior (controller side). They will import and use the library file. This is a cleaner approach because changes in the library class will be available in all files it is used in.
Bonus: The main library file is easier to test. And therefore testing the other classes afterwards is easier, too.

Generally speaking, all web services should be some kind of library file (some would make a datasource out of it). It doesn’t matter then if we use it in components or helpers, because it will fit either way.

A helper in a controller, though, is not really a nice thing.
With Cake2.1 there is no need to use helpers in the controller or model code anymore. All text/number/time helper methods have been moved to the Utility package and can now by used from anywhere within your application in a dry and clean way:

App::uses('CakeNumber', 'Utility');
$myValueInPercent = CakeNumber::toPercentage(45.12, 0); // or dynamically - but statically is easier in this case

Plugin or not?

If your feature is not site-specific but very generic it probably makes sense to build a plugin.
This way all other apps can easily use the same plugin. Additionally, test cases, assets etc are all combined in one folder – clean and extendable.

Examples:
A bookmark helper usually can be used in several apps, whereas a custom helper with two functions for this one app will not be very useful anywhere else.

Usage of those resources

For components, add it to the controller in order to use it in the corresponding actions:

public $components = array('MyC'); # file is in /APP/Controller/Component named MyCComponent.php

And in one of the controller’s actions:

$this->MyC->foo();

For helpers, add it to the controller in order to use it in the corresponding views:

public $helpers = array('MyH'); # file is in /APP/View/Helper named MyHHelper.php

And in one of the controller’s views:

$this->MyH->foo();

Libs can be used everywhere – include them at runtime:

App::uses('MyL', 'Lib'); # file is in /APP/Lib/ named MyL.php
$MyL = new MyL();
$result = $MyL->foo($input);

Possible in controllers, components, behaviors, view, helpers, elements and everything else.
They are the most generic classes you can create.

Sidenote: I like to keep those files appended with a suffix, as well (Lib to avoid collisions with other classes or core classes):

App::uses('MyLLib', 'Lib'); # file is in /APP/Lib/ named MyLLib.php
$MyL = new MyLLib();

But that is just my personal convention.

Also note that you are encouraged in 2.x to group your lib classes in packages. So if you have some Utility Helper of your own, you might want to create a subfolder for it (you can use the core folder names or your own naming scheme):

App::uses('MyUtilityLib', 'Utility'); # file is in /APP/Lib/Utility/ named MyUtilityLib.php
$MyL = new MyLLib();

Same with plugins:

App::uses('UrlCacheManagerLib', 'Tools.Routing'); # file is in /APP/Plugin/Tools/Lib/Routing/ named UrlCacheManagerLib.php
$UrlCacheManager = new UrlCacheManagerLib();

Behaviors and other elements are used similar to the above.

For Plugins simply add the plugin name: "Text" becomes "Plugin.Text" etc

bootstrap functions:
If those functions are so generic that you want to use them like h() or env() you can define them in your bootstrap. But be aware that this can lead to chaos if you do that for too many things.

Hacks for special use cases

Sometimes we need to break MVC in order to avoid redundance (and stay DRY).
If a helper has to be available in a controller we need to manually include it then at runtime:

App::uses('MySpecialHelper', 'Helper');
App::uses('View', 'View');
$MySpecialHelper = new MySpecialHelper(new View(null));
$myText = $MySpecialHelper->foo($myText);

I want to emphasize that this should only be done if not possible any other way.
What I do in this case: Move the functionality to a Lib class and use it inside the helper. This way we can use the Lib in the controller/model level and the helper is still the convenience access for the view level.

 
2 Comments

Posted in CakePHP

 

Gravatar CakePHP Helper

23 Aug

An independent Avatar-Generator

90% of all blogs out there use gravatar. Therefore you can easily use it with most of your users’ email addresses.
But even if you don’t want your users to use their gravatar image you can still use the funny images this service provides. Instead of an email address you could hash the user_id of the member and get a unique image to display as avatar in the profiles or even throughout the website.

Resources:
Gravatar Helper and its Test Case.

Example usage

# using the email of the user
echo $this->Gravatar->image($email, array('default'=>'retro', 'title'=>__('Avatar', true)));
# using an unique identifier - in this case the user_id
echo $this->Gravatar->image($uid, array('size'=>'20', 'default'=>'monsterid', 'title'=>__('Avatar', true)));

The first example will try to find the real image the user has submitted to gravatar and will fallback to some colorful and unique retro image.
The second example uses little monster figures for all users (since the user_id cannot be an email and therefore there cannot be any custom user image – except for collisions with the md5 hash maybe).

Tip: check out the test case for all possible image types, options and scenarios.
If you open my test case with the browser (…/test.php?case=helpers%5Cgravatar.test.php&plugin=tools) you will see the images as I echoed them out for testing purposes.

The main part of this helper is the work of Graham Weldon. I simply added some enhancements.
Hava fun.

 
1 Comment

Posted in CakePHP

 

GoogleMapsV3 CakePHP Helper

21 Dec

This is a helper to generate GoogleMap maps (dynamic and static ones) in your CakePHP views.

Note: Google Maps API V2 is marked "deprecated". API V3 is supposed to be faster and more compatible to mobile browsers. Details
Also new: A googlemaps key is not necessary anymore.

Preamble

You can either dynamically collect the geo data, or you can use my behavior to retreive and store them persistently. I prefer storing them in a table using lat/lng fields as it will make displaying the markers faster (no need to fetch at runtime) – it also makes it possible to make distance queries using radius and such.

Usage

I wanted to keep it as simple as possible (otherwise you could use the maps with plain js right away).

You may set up configs/defaults in your config file:

$config['Google'] = array(
	'zoom' => 6,
	'lat' => 51.1,
	'lng' => 11.2,
	'type' => 'H', // Roadmap, Satellite, Hybrid, Terrain
	'size' => array('width'=>'100%', 'height'=>400),
	'staticSize' => '500x450',
);

Please include the Tools Plugin in your bootstrap with CakePlugin::loadAll() or CakePlugin::load('Tools').

Now you need to add the helper to the controller (or the action):

// globally
public $helpers = array(..., 'Tools.GoogleMapV3');
// OR in the action:
public function map() {
	$this->helpers[] = 'Tools.GoogleMapV3';
	// rest of your code       
}

Since there are many possible ways to include the required javascript file I formed an own method for it:

<?php
// include jQuery >= 1.4
echo $this->Html->script('jquery'); // or wherever it is in your js folder
// include the Google js code
echo $this->Html->script($this->GoogleMapV3->apiUrl());
// OR include it manually without cake (or use your own asset stuff)
echo '<script type="text/javascript" src="'.$this->GoogleMapV3->apiUrl().'"></script>';

You may also let the helper include it automatically – just pass the option "autoScript" => true to the map() method in the next step.

Now we can use it.

Interactive Maps

// init map (prints container)
echo $this->GoogleMapV3->map(array('div'=>array('height'=>'400', 'width'=>'100%')));
// add markers
$options = array(
	'lat' => 48.95145,
	'lng' => 11.6981,
	'icon'=> 'url_to_icon', // optional
	'title' => 'Some title', // optional
	'content' => '<b>HTML</b> Content for the Bubble/InfoWindow' // optional
);
// tip: use it inside a for loop for multiple markers
$this->GoogleMapV3->addMarker($options);
// print js
echo $this->GoogleMapV3->script();

You can also add windows and (custom) events. See the code and its tests for details on that.

Buffering scripts instead of outputting them directly

With 2.x you can also write the JS to the buffer and output it combined anywhere you want in your layout.
Just call

$this->GoogleMapV3->finalize(); // replaces script()

instead of echoing the script() then. The script() call must not be used then. Don’t mix those two methods.

Also make sure you got echo $this->Js->writeBuffer(array('inline' => true)); somewhere in your layout then.
Most developers put CSS in the head and JS at the very bottom (before the closing </body> tag). This way, the layout renders very fast and the JavaScript will render last and does not impede the page loading process.
See the next chapter for details.

Directions (with or without additional text)

If you want to print directions from point A to point B, you can do so with:

echo $this->GoogleMapV3->map();
$from = 'Munich'; // needs to be geocoded at runtime
$to =  array('lat' => 50.51, 'lng' => 13.40);
$this->GoogleMapV3->addDirections($from, $to);
$this->GoogleMapV3->finalize();

You can either pass in a string to be automatically geocoded or pass an array with lat/lng coordinates.

Note: Geocoding on demand is possible, but slightly slower and less resourceful. It is recommended to geocode in the backend – using PHP and the Plugin classes listed below – and output the coordinates here directly.

Static Maps

These are just images. Very handy if you don’t need the js overhead.

// markers
$markers = array(
	array('lat'=>48.2, 'lng'=>11.1),
	array('lat'=>48.1, 'lng'=>11.2, 'color' => 'green', ...)
);
// paths
$paths = array(
	array(
		'path' => array('Berlin', 'Stuttgart'),
		'color' => 'green',
	),
	array(
		'path' => array('44.2,11.1', '43.1,12.2', '44.3,11.3', '43.3,12.3'),
	),
	array(
		'path' => array(array('lat'=>'48.1','lng'=>'11.1'), array('lat'=>'48.4','lng'=>'11.2')), //'Frankfurt'
		'color' => 'red',
		'weight' => 10
	)
);
// some options and image attributes
$options = array(
	'size' => '500x400',
	'center' => true,
	'markers' => $this->GoogleMapV3->staticMarkers($markers),
	'paths' => $this->GoogleMapV3->staticPaths($paths), 
);
$attr = array(
	'title'=>'Yeah'
);
// now display the map image
echo $this->GoogleMapV3->staticMap($options, $attr);
// you can even add an url to click on
$attr['url'] = $this->GoogleMapV3->mapUrl(array('to'=>'Munich, Germany'));
echo $this->GoogleMapV3->staticMap($options, $attr);

As you can see, we can now mix lat/lng and normal addresses (which get automatically geocoded in the background).

Map Links

If you want to redirect to maps.google.com (for directions etc) you can use this method.

// leave "from" empty for the user
$url = $this->GoogleMapV3->mapUrl(array('to'=>'Munich, Germany'));
echo $this->Html->link('Visit Me', $url, array('target'=>'_blank')
// coming from a posted form or whatever
$url = $this->GoogleMapV3->mapUrl(array('to'=>'Munich, Germany', 'from'=>$from));
echo $this->Html->link('Directions to me', $url, array('target'=>'_blank')

Last but not least

Other updates:

  • multiple paths and markers
  • visible scope
  • custom icons possible

For more examples check out the test case. It contains several more sophisticated examples.

Helper Code

The code can be found in my github rep:
Tools Plugin GoogleMapV3 helper

Update 2012-02

All urls/links are now HTTPS sensitive. So if you display the map on a secure site (https://...) it will also use the same connection for all the google stuff (images, js, …). This is necessary for HTTPS to be valid.

Update 2012-09: GoogleMapsV3Helper v1.3 – not backwards compatible

The helper is now E_STRICT compliant. The methods url() and link() are now mapUrl() and mapLink().

Update 2013-02

directions() has been added to print directions from point A to B. Also basic geocoding capabilities have been added for this method as well as addMarker().

Update 2013-10

The "open" option for markers allows you to display their infoWindow opened at loading. This was a feature request on github. It is available for single and multi windows mode. Note that for single window mode (default), only one marker can be shown as open at once (the last one declared).

 
106 Comments

Posted in CakePHP

 

Helper? Component? Lib?

26 Jun

Some ideas what to use if you want to add some additional feature.
Feel free to comment on this below.

Level of Independence

We need to ask ourselves if this feature needs to interact with other cake elements, like controller, some components, models, …

If it needs to save to the session, or if it needs some controller functionality, it will have to be a component.
With initialize(&$controllerReference) and startup(&$controllerReference) this is very easy to accomplish.

But with Cake13 libs have been introduced. Not every piece of "controller" code necessarily needs to be component anymore.
So if you retrieve an RSS feed or get the weather from a weather channel web service you could just make a clean and independent lib class. No need to extend the cake object or even pass the controller reference. Less memory and dependency is a good thing. And its easier to test, anyway.

Helpers are used if the result is in relation to the view – in other words if it gets printed/echoed right away. If you want to retrieve some web service information and save it to the database use a component instead.

Database related?

Often times we need to adjust some model data, we either use a component first and then pass it to the model or we use beforeValidate() and beforeSave() in the model. Same goes for the other direction (from model to controller): afterFind() or a component call afterwards.
This is fine for custom changes. As soon as it could be something useful for several models, it might make sense to build a behavior. The code gets cleaner and your models more powerful.

Examples would be:
"Last Editor/Last Change", "Geocoding", "Auto-Capitalize first letter of name/title", "Format/Localize Date/Time", …

Reducing code redundancy

Now that we have a vague understanding where to use what type of tool, we should think about cutting down the redundancy.
Lets say we use the vendor class "phpThump". We would have to write two wrappers. one for the helper (display images in the view) and one for the component (uploading images and resizing), maybe even for some behavior (validating + uploading + resizing). This wrapper handles default values from Configure::read() and other cake related settings.
In this scenario we should build one single library in /libs, maybe called "phpthumb_lib.php".
Here we put our wrapper with our custom functions.
Then we build a helper (view side) as well as a component or a behavior (controller side). They will import and use the library file. This is a cleaner approach because changes in the library class will be available in all files it is used in.
Bonus: The main library file is easier to test. And therefore testing the other classes afterwards is easier, too.

Generally speaking, all web services should be some kind of library file (some would make a data source out of it). It doesn’t matter then if we use it in components or helpers, because it will fit either way.
A helper in a controller, though, is not really a nice thing.

Thats – by the way – something i don’t like about the core helpers. They have functionality which is often useful in the controller (replacing a timestamp with localized date in session flash messages).
So there should be a library called "Time" or whatever which is then extended or used in the helper.
But, if needed, it can be used in the controller, as well. Same goes for "Text" and "Number".

Plugin or not?

If your feature is not site-specific but very generic it probably makes sense to build a plugin.
This way all other apps can easily use the same plugin. Additionally, test cases, assets etc are all combined in one folder – clean and extendable.

Examples:
A bookmark helper usually can be used in several apps, whereas a custom helper with two functions for this one app will not be very useful anywhere else.

Usage of those resources

For components, add it to the controller in order to use it in the corresponding actions:

var $components = array('MyC'); # file is in /app/controllers/components named my_c.php

And in one of the controller’s actions:

$this->MyC->foo();

For helpers, add it to the controller in order to use it in the corresponding views:

var $helpers = array('MyH'); # file is in /app/views/helpers named my_h.php

And in one of the controller’s views:

$this->MyH->foo();

Libs can be used everywhere – include them at runtime:

App::import('Lib', 'MyL'); # file is in /app/libs named my_l.php
$myL = new MyL();
$myL->foo();

Possible in controllers, components, behaviors, view, helpers, elements and everything else.

Behaviors and other elements are used similar to the above.

For Plugins simply add the plugin name: "Text" becomes "Plugin.Text" etc

Hacks for special use cases

Sometimes we need to break MVC in order to avoid redundance (and stay DRY).
A typical szenario is when we need a core helper in the controller (e.g. TextHelper).
We need to manually include it then at runtime:

App::import('Helper', 'Text');
$text = new TextHelper();
$myText = $text->truncate($myText);

I want to emphasize that this should only be done if not possible any other way.
You would also have to manually start and attach all helpers which are used inside the helper. Pretty annoying 🙂

I proposed a while ago that most of the core helpers should actually extend or at least use libs which contain the relevant functionality.
This way we can use the libs in the controller and we can use their methods in the view via helper wrappers.
But as of right now this is not yet planned for Cake2.0 or higher.

Update 2012-04-03

For Cake2.0 and above please see the updated article on this topic!

 
No Comments

Posted in CakePHP