I'm following a tutorial which I think is written by someone who doesn't know what he's doing (already caught 2 obvious mistakes and the rest of code is messy). But I don't want to discredit the guy completely, so I'm asking here about something else that I don't understand.
First of all, I will send 100 brownie points, my 2 pets, and a box of chocolate to whoever can explain to me what is going on with this code.
He's using module-based architecture. Module name is frontmodule
. Module has MVC. And module has an internal library
of its own.
/modules/
/frontmodule/
/models/
/views/
/controllers/ -- the /module controller is here (undestandable)
/library/
/Controller/ -- the /module/library controller is here (why?!)
/Action/
First comes the confusing part. Why each module has an internal library, and why that intenal library has its own controllers
and actions
. Is this a best practice? I'm thinking this library could be moved to a plugin that the module can use. Not sure..
Now comes the interesting part.... in addition to each module having its own internal library, there's also a Common library shared by all modules (see it below at the same folder level as /modules
) and that Common library also has its own controllers and actions (just like each internal libraries have their own controllers and actions)
/modules
/library/
/Common/
/Controller/ -- the /common/library controller is here (why?!)
/Action/
/Helper/
/Plugin/
So we have 3 controllers:
Now here's the insane part that I think is over-complicating life
He says: A module controller extends the module’s library parent controller which also extends the Common library controller.
class IndexController
extends Frontoffice_Library_Controller_Action_Abstract { ... }
abstract class Frontoffice_Library_Controller_Action_Abstract
extends Custom_Controller_Action_Abstract { ... }
So I guess:
IndexController
Frontoffice_Library_Controller_Action_Abstract
Custom_Controller_Action_Abstract
where module controller
extends module internal library's controller
and module internal library's controller
extends common library's controller
Has anyone seen anything like this before? My guess is that this code won't be easy to maintain, but maybe those more experienced with zend can tell me what this guy is trying to achieve. The app structure is a little too messy. I think he's abusing MVC instead of using it to simplifying the app and its maintainability.
The great thing about Zend Framework is that it is use-at-will which means you can use a single component or you can use them all. Most components are also very flexible either via configuration or extension (inheritance or composition, with ZF favoring the latter).
Zend Framework MVC is extremely flexible...even to the point where many have accused it of being over-engineered or bloated itself. This is subjective.
Sure, you probably won't want to use Zend Framework for a simple contact form; however, there is nothing stopping you from utilizing just Zend_Mail and Zend_Form without Zend MVC/Application. With flexibility in mind, there is currently no single methodology which is touted as the best in terms of organizing an application into modules. This is a task best left up to the implementer.
This brings us to the tutorial at hand. The tutorial writer has come up with a strategy for re-use. This is a good thing; however, there are some flaws with his approach.
A library per module. This is not necessarily bad; however, it is in most cases not necessary. Lets explore what options we have just in case such a structure is needed for some reason.
a. Build a general library (you most likely already do this) namespaced (pseudo if < 5.3 or actual namespaces if >= 5.3).
b. Utilize the intrinsic "Resource Autoloader" http://framework.zend.com/manual/en/zend.loader.autoloader-resource.html
NOTE: I personally haven't used the resource autoloading much. The one time that I did use it, I found that I could have just moved those items to my library. That being said, there are uses for this. It seems to shine when you expect to mix and match modules across projects or for distribution. ZF2 will address this in a less hacky way IMHO.
Base controllers for re-use. Again, reuse is great; however, Zend Framework provides better alternatives to sub-classing (inheritance) controllers. First, here are some reasons NOT to use controller inheritance:
a. Keeping things DRY is defeated when you have multiple modules that differ enough to need a base-class per module but functionality is copied/pasted across each module's base controller class.
b. It becomes hard to manage inherited properties as it is harder to visualize what inherited functionality is being utilized by each controller/action
c. With PHP allowing only single class inheritance, you blow your one chance at inheritance here -- use this only if there are no other options left
Alternatives:
a. Front-Controller Plug-ins Use these when the functionality/logic needs to run on every request regardless of module/controller/action
b. Action helpers As mentioned by the ZF project lead, "They're a built-in mechanism in Zend Framework to allow you to extend your action controllers in a way that uses composition instead of inheritance." There isn't anything that you can do in a controller that you can't do via an action helper. Use these when the functionality needs to happen on a per controller and/or action basis.
So, was the example in the tutorial over-engineered? Not necessarily; however, it is certainly a candidate for incorrectly-engineered as it relates to best practices and provisions given by Zend Framework.
I need to digress for a moment and discuss the terms over-engineered and bloated for just a moment
When someone tells you that something is over-engineered and/or bloated without stating a context please take it with a grain of salt.
The Wikipedia article - http://en.wikipedia.org/wiki/Overengineering reads in part "...when a product is more robust or complicated than necessary for its application...".
So, when referring to something as Over-engineered/bloated one should be careful to qualify the context or application at hand. Blanket statements should be taken with a grain of salt and in most cases, not taken at all. This is akin to saying something like "I'd never use a 'Circular Saw' for woodworking since it has way too many features and those features confuse me". Sure, this tool may be over-kill for small home/side projects; however, since it is super flexible you will be happy you have this tool when you find yourself in situations where you never thought you'd find yourself.
Yes, most web frameworks are over-kill for a simple CRUD application such as a contact page or even a simple blogging application. It is unfortunate that most web frameworks use the blog example as their introductory example - go figure.
Extra Info:
If you wanted to switch layouts based on the module/controller/action, you could write a Front-Controller Plug-in. Just call "Zend_Layout::startMvc" and pass it a layout name and a path.
Switching the actual view script based on the Accept header (or URL parameter or X-HTTP-METHOD-OVERRIDE header) can be done with the context switch action helper (intrinsic to Zend Framework) - http://framework.zend.com/manual/en/zend.controller.actionhelpers.html
Feel free to pass a model instance to an action helper. You can also configure your action helpers with configuration from the bootstrap. This way, there is no need to store things in the global registry which is just a glorified global variable. This is a hidden dependency that you don't need. For the best re-use, you can create your own custom plug-in resources by extending Zend_Application_Resource_ResourceAbstract - http://framework.zend.com/manual/en/zend.application.core-functionality.html#zend.application.core-functionality.resource-resourceabstract
This is insane. You're making web pages, right? This isn't hard. I'd say the stuff you posted is the very definition of overengineering:
http://en.wikipedia.org/wiki/Overengineering
Not insane at all. Perhaps badly or overly engineered, but it could be a useful setup.
It's just two "extra" levels of inheritance, which in some cases might make perfect sense.
But generally, this suggests packing an awful lot of logic into the controllers, which is generally bad design.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With