RSS
 

Posts Tagged ‘Plugin’

Developing CakePHP 3+ Plugins, it’s fun!

29 Jan

It is fun – and it should be!

The days of CakePHP 2 plugins and how difficult it was to actually develop plugins are over.
Back in the days (OK, I still have to do it once in a while), there was even an app required to test a plugin. Since you didn’t want to have a boilerplate app for each plugin, you usually worked in your actual app. So you had cross contamination from that messing up your tests and stuff. Really annoying.
The only thing I am still missing once in a while is the web test runner, as it speeds up things for some use cases (usually with browser related output).

While most of the concrete examples are about plugin development for CakePHP 3, the main ideas apply to all library code you write. And if you are a developer for other frameworks, the same principles apply, only the concrete implementation might differ. So you could skip the "real story" part.

Well, but now to the fun part I promised in the title.

CakePHP 3 plugin development – real story

My goal was simple: Developing a simple plugin for hashids support in CakePHP in a few hours.

The CakePHP plugin docs mentioned a few basics, but in the following paragraphs I want
to go into more concrete details.

How do you start?
I started by creating a fresh GitHub repo "cakephp-hashid" and cloning it (git clone URL).

Then I added the boilerplate stuff like composer.json and gitignore file. You can either copy and paste from existing ones,
or even bake your plugin code (cake bake plugin Name) and move it to your stand-alone plugin.
Keeping in the app is also possible, but I prefer to keep it outside and develop it test driven until it is in a working state.
This way you are usually faster. TDD – test driven development – actually helps to speed up development, and you get tests with it for free.

Now it was time to set up the behavior code and the constructor setup as well as a test file.
With php phpunit.phar I already got immediate results of the initial work, and could fix those with almost zero overhead.
As soon as I added more use cases, especially with some config options and edge cases, I quickly saw where things were not working as expected.
But while getting those to run, I also saw if I broke the existing already working tests. Big help.

Once I got everything in a beta usable state, I pushed and published the repo.
After some documentation (!) in the README as well as Travis/Packagist enabling, I already composer required it in my Sandbox project and started to use it "for real".
When I got it all working in live mode, I felt convinced enough to release a 0.1 beta tag.

The cool thing that coveralls (or codecov.io etc) told me then:
I got 95% code coverage out of the box without writing additional tests (I did write them parallel to the code as "pseudo app test frame").
Simply because when I wrote the plugin code and tests, I already tried the different options available, running into all possible code lines by design.

So now I added more features over the next 1-2 hours, released 2 more tags and after a few days it is now time for the official 1.0.0 release.

Here is my sandbox demo code for this plugin, by the way: sandbox3.dereuromark.de/sandbox/hashids.

In parallel I started even a 2nd small plugin Markup, which took my only half the time even because now I was already quite fast in setting up the boilerplate stuff. Here I also tried to keep it extensible for use cases of future plugin users.

So overall I invested a few hours total to have two easily maintainable plugins that are open for extension, but by default suit my needs. Try it yourself, you will see that this way it really is not too hard to develop and publish such a plugin.

(Plugin) coding tips

If you develop a plugin for the first time, take a look at the existing ones listed in the awesome-cakephp list.
They might give you some insight in how things can look like. How we add a bootstrap for testing, how a Travis file looks like etc.

For test cases it also never hurts to take a look into the core test cases.

Plugin vs. core feature

This issue comes up every week basically. For me, beginning with CakePHP it was difficult to tell what should be covered by the core and what should stay as community plugin. I had this idea that every use case must be supported by the framework itself. Over time, it become more and more clear to me that a framework itself should stay lean and focus on the majority of the use cases and maybe provide a way to make it extensible for those edge case.

As a logical conclusion some of the CakePHP core functionality has been split off into it’s own repositories, like Acl, Migrations, Bake, Localized.
Not all of the users need those additional tools, in fact almost no one used Acl, and you only need Bake for development.

The positive side-effect here is that the iterations of these plugins can be independent from the core development, making feature development faster as there is no core release for the whole package necessary anymore.

Ask yourself the following questions:

  • Does the majority of devs need this functionality, and if so, this specific implementation of it?
  • Is it beneficial for both core and plugin to keep it standalone for maintenance and extendability reasons?

There will be more questions you can ask if you continue reading the next chapters about more generic principles.

Try to follow coding and package principles.

With CakePHP 3 we can finally adhere more correctly to some very basic coding principles. Most of you might know (or at least heard) about SOLID and Package Principles.
They following tips go into more detail what it means for our CakePHP plugins.

Coding principles (SOLID)

SOLID principles

The 5 well known SOLID principles can help us deciding how to best code our plugins and libraries.

Single responsibility principle (S)

Try to keep your classes simple and short, doing one specific task.
Take a look into my Geocoder behavior.
It does not actually geocode, because that is the task of a specific class. To avoid it doing too much, the behavior only wraps this Geocoder class and forwards calls to it. This way the only responsibility of this behavior is to help the model layer (usually a Table class) to geocode the entity data, while the single responsibility of the Geocoder class is to perform this geocoding task by talking to an API.
The additional advantage is that we can also use the library class as standalone, so we might want to provide a GeocodeShell, for which we most certainly don’t want to use behavior to encode a simple input string.

Open/closed principle (O)

Your code should be open for extension, but closed for modification. You will most likely never be able to guess and support all use cases for your code out of the box. Often times people will have come up with new ways to use your plugin.
So at the one side you do not want to have to change your code for every possible scenario. But if it was possible to support a lot of extensions out of the box, why not doing this?

If we are using dependencies in our classes, we do not want to rely on a specific class dependency, but an interface.
This might be not so important to you as plugin designer, but as soon as people use it, they might want to change maybe how a specific implementation of your dependency works.

Lets take a look at the [Slug plugin][(https://github.com/UseMuffin/Slug). It shows how you can simply set a default for 'slugger' => 'Muffin\Slug\Slugger\CakeSlugger', but it would allow any other slugger implementing the SluggerInterface. Instead of providing and versioning all possible sluggers (and their potential dependencies) itself, it allows another package to contain a user-specific slugger
to use.

So always try to not contain specific switch statements or if/else blocks. Instead, always implement against a generic interface that allows for exchangeability. This is especially important for type-hinting constructors and methods.

Here, the implementing classes just need to provide a slug() method and it will work just fine even with some BinarySlugger 🙂

Liskov substitution principle (L)

Every subclass or derived class should be substitutable for their base/parent class. So make sure you make don’t widen the input/constructors, but keep them the same or narrow them.
You can always become more strict, but not less. Interfaces also help with that, as they make sure that at least those common methods have been provided.

Interface segregation principle (I)

If you create one interface containing too many different method stubs, often times you limit the possibilities of implementation.
Often times those classes can be grouped by API or Non-API, and in either of those cases need only a subset of the interface methods.
In this case it will most likely make sense to have two specific interfaces for each use case, this allows the sub-parts of your code to only rely on those relevant methods they care about.

Dependency inversion principle (D)

Ideally, we always enforce class dependencies via constructor argument commonly known as "Constructor Dependency Injection". When allowing to exchange the used class, we should respect that.

So in our case, we allow a closure to be passed into our plugin library class config:

$geocoderClass = $this->config('provider');
if (is_callable($geocoderClass)) {
	// Use the injected closure
	$this->geocoder = $geocoderClass();
	return;
}
// Manually create class
$this->geocoder = new $geocoderClass();

You never know if the class your plugin users want to use require some constructor dependencies on their own.
The DI principle should be possible for them to use, too, to fully respect the Open/Close Principle from above.

They can now have their own HTTP adapter passed into the Provider class:

$config = [
	'provider' => function () {
		return new \Geocoder\Provider\FreeGeoIp(new \Ivory\HttpAdapter\CakeHttpAdapter());
	}
];
$this->Geocoder = new Geocoder($config);

As with the first principle, it is also important for DI that if you type-hint methods, class properties and alike, always try to use the Interface, not a concrete class.

Note that there is a second way to pass class dependencies usually referred to as "Setter Dependency Injection".
This should be avoided for required classes and only be used for optional parts, if any.

Package principles

Some of the 6 commonly known package principles can also be quite useful for our decisions.

Reuse-release equivalence principle – consider the scope

It is very much possible that whatever plugin you are going to write, the functionality itself could be interesting to other PHP applications.
With 2.x it was hard to provide it both ways, with 3.x this is now as easy as it gets.

A good example is Mark Story’s AssetCompress plugin.
Back in 2.x this was a full blown CakePHP-only dependency.
He realized that most of the code behind it could very well be useful to other PHP projects, though.
With CakePHP 3 it was finally possible to move the code into a standalone lib called MiniAsset.
All that was left of the plugin code was to function as a CakePHP bridge. This enables the actual library to be used by everyone whereas the framework users could use the plugin.
r
So also check your existing (plugin) code for this maybe.

Common-reuse principle (CRP)

Code that is used together, should be ideally in the same package. Micro-splitting each class into it’s own plugin might not always be wise. It also increases maintenance time/costs.

So if you have a Geocoder Library class talking to the API and a Geocoder Behavior using this Library to populate entities, you would always use them together, or just the library class.
Splitting them into own plugins does not sound feasible here, as the default case sees them used together.

Common-closure principle (CCP)

A package should not have more than one reason to change. So when you put too many different classes or even multiple different dependencies into a plugin/lib, you might have to major bump for each of those separately, creating a lot of annoyance for the users only using the unrelated part of code.
Bear that in mind when packaging. Packaging a lot together eases maintenance time/costs, however. So you need to find a good balance here (also regarding CRP principle) for the maintainer(s) as well as the package users.

Most probably know my Tools plugin, which was started way back in 2006 with SVN and no github/composer.
At that time it would have been super painful to support multiple plugins via SVN externals and no release/version locking really.
Thus one monolith class collection. Over the time, with GitHub and more people using it (not just me anymore), it became clear this approach is outdated and troublesome. Most of the time people use only a small subset and have to get all irrelevant class changes on top.
So with CakePHP 3 already becoming alpha/beta I started to split off some of the chunks that really deserve their own plugin namespace.
TinyAuth, Geo, Ajax, Rss and Shim have been created.
Now everyone using TinyAuth or Rss, for example, will most likely use all those classes together, while inside Tools it would have been 1% of all the rest.

I will further split out stuff in the future, wherever this makes sense regarding to these two first packaging principles. But it would also be impractical to have to maintain 150 new plugins for each small stand-alone class used somewhere at some point. So a good balance is the key again.

Package coupling (ADP, SDP, SAP)

  • Prevent cycling dependencies
  • Change is easiest when a package has not many dependencies (stable), so make sure if it does those are also not blocked for change.
  • Stable packages ideally have a lot of abstraction (interface, …) exposed to the depending parts so their stability does not prevent them from being extended.

Note that the term "stability"/"instability" isn’t evaluative. It simply refers to the dependencies of a package here.

A quote about stability of classes:

Typically, but not always, abstract classes and interfaces are not dependent on others and tend to be more stable as against concrete or implementation classes. This is because the abstract classes or interfaces typically represent the high level design and not the implementation, e.g, an interface called Logger will be more stable than classes FileLogger or DBLogger.

ifacethoughts.net/2006/04/15/stable-dependencies-principle

So in both cases (class vs package level) stability means something slightly different but in the end sums up to "less reasons to change".
By trying to create and use packages and classes in the direction of stability usually is the best approach and will keep necessary changes at the endpoint (usually your app) at a minimum, as well as for most parts (plugins) in between.

Releasing

Don’t forget to release your code with tags following "semver".
You can start with 0.x and once you feel comfortable that it is fairly stable, release a 1.0.0.
New functionality usually goes into minor releases, bugfixes in patch-releases. A BC break warrants a major version jump.

Framework Semantic Versioning

With releasing plugins for a CakePHP version strict semver can be somewhat confusing, though (1.x/2.x here is for 3.x there, 3.x+ is for 4.x there, etc).
One more severe problem with that is that once you released a new 3.x framework compatible version you cannot major bump your 2.x code, as there is no number left in between. You have to break semver, or do some other workaround using composer-constraints. Some might even suggest to use a new repository for the 3.x compatible code etc.
Most of the time people are just afraid of major bumps and often use a minor one to introduce larger breaking changes.
It seems like here the design itself does not fit to the use case and misleads the maintainer to do bad things (talking from experience).
I think there is a better approach.

Something that could be considered "framework-semver" is the following approach I have seen occasionally so far:

x.y.z

  • x: CakePHP major version
  • y: Plugin major version
  • z: Plugin minor/patch version

Examples:

  • 2.3.4: CakePHP 2.x, Plugin 3.x for this major, Minor/Patch v5
  • 3.1.0: CakePHP 3.x, Plugin 1.x for this major, First Minor

So the first number of both core and plugin matches.

In my book this makes it way more clear as the plugin itself cannot live without the CakePHP core dependency and at the same time has to be compliant to each of those different major versions.
So in all this makes more z bumps, and occasionally an y bump. But that is OK.
It also requires you to look the y version then "vendor-name/plugin-name": "1.2.*", as every y bump could break BC.

In theory you could also use a 4th digit, the actual patch version: x.y.z.patch, e.g. 2.3.4.1. This would make it fully semver in itself again, but is usually not needed, as new functionality that is BC and bug fixes are both supposed to be z compliant (otherwise it would be major plugin version bump).
Either way it solves the issue of framework dependent plugins by design, and not by workarounds.

A smilar approach was already published here, but this is not composer friendly, and since all non major bumps should be BC, there should be no immediate need for a prefixing that includes the minor version of a framework.

Note: This is just some grounds for discussion so far. If you plan on using this you should really make it clear and documented it in bold visible in the README or something.
Because by default people will most likely assume you are following default "semver". Are there any downsides so far? I would really like to have some feedback here from the whole community.

License

Most people tend to forget that they release packages that are to be used in other peoples’ (proprietary) software. That means that they cannot legally use your code unless you specifically put a license in they know they can use. An MIT license is recommended and always works.
But that requires the file to be present in the root folder and/or in the files itself.
Just having a statement in the README usually does not suffice. Just something to look out for.
It would be sad if lots of projects/companies would like to use your plugin but cannot because of trivial license issues. Get it out of the way. You will benefit from more uses because of more feedback/contributions of them, as well.

Maintaining

One thing you should consider is regular maintenance for your plugins. Make sure CI (e.g. Travis) tests pass, that issues and PRs are taken care of in a reasonable time frame etc.
If that is too much to do, you can always ask for help and co-contributors, or even hand off the repo to someone else entirely.

Ready, set, …

Then take an idea and pluginize it.

You really want to start coding your own awesome plugin now but don’t have an idea? Look into the wiki of that very same repo from above. There you can find a lot of 2.x plugins that have not been upgraded yet. You could take care for any that interest you.
Or you go through your existing code and check those classes (helpers, behaviors, libs, …) for re-usability.
Also don’t forget to add your upgraded or new plugin to that list 🙂

Final notes

Some people might know DIP also as IOC (Inversion of Control).
Maybe also take a look at containerless-dependency-injection-for-services, a new article of @markstory about how most frameworks make their users potentially abuse DIC and
how better code your service classes.

Book Tips / Source Materials / Interesting Videos:

 
2 Comments

Posted in CakePHP

 

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

 

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