Page 1 of 1
Joining model functionality?
Posted: Wed Jan 10, 2007 9:17 am
by John Cartwright
Couldn't think of a great title, so apologies
Anyway, my problem has come into play when developing large systems where certain models will have methods that are very similar across multiple classes. For instance, lets say I want to display news, there will be the news page which it is listed as well as the admin page to modify the information. Obviously we don't want to have duplicate code in our codebase, so how do you guys tackle this problem? I'll give a bit a code in a moment to further illustrate my problem. Just for notes, this is very simplified for those who are wondering.
So we have our init method which is automatically executed for our system startup, making all unnecessary initializations for our application. So in this case, our model is loaded depending on the controller loaded. Application_Controller_Index()'s model becomes Application_Model_Index(). Now since the model is loaded automatically at runtime, I can't determine whether the controller has anymore dependencies, at least not transparently.
<?php
Code: Select all
abstract class Application_Controller extends Zend_Controller_Action
{
function init()
{
$model = $this->formatName($this->_getController());
$this->model = new $model;
}
}
And our controllers which will call $this->model->getNews()
Code: Select all
class Application_Controller_Index extends Application_Controller
{
public function IndexAction()
{
$this->view->paintNews($this->model->getNews(5));
}
}
class Application_Controller_NewsManager
{
public function IndexAction()
{
$this->view->paintNews($this->model->getNews());
}
}
I have two seperate controllers essentially doing the same thing. So to reiterate, how do you guys tackle this problem --or-- do you see anything I can do to improve functionality per this case.
Thanks.
Posted: Wed Jan 10, 2007 9:26 am
by Jenk
Forgive me if I am playing Captain Obvious, but isn't this essentially the core of the reasoning behind OO? To separate functionality into concise objects to avoid repetition/duplication?
Posted: Wed Jan 10, 2007 9:36 am
by John Cartwright
Jenk wrote:Forgive me if I am playing Captain Obvious, but isn't this essentially the core of the reasoning behind OO? To separate functionality into concise objects to avoid repetition/duplication?
Which is exactly what I'm trying to do. Did I not make this clear?
I'm just curious how others have tackled this issue.
Posted: Wed Jan 10, 2007 11:22 am
by Jenk
Oh right.. well, for your examples, I'd say they are fine as is - you have a small, but significant difference between them - the (apparently) optional argument. This warrants "duplication" in my books, or rather declassifies it as duplication.
Posted: Wed Jan 10, 2007 2:31 pm
by John Cartwright
I took another approach and decided to implement a loading function to initialize the external model, but as it is just doesn't "feel right".
Something like
Code: Select all
<?php
class Application_Model_Index extends Application_Model
{
public function getNews($maxCount = false)
{
$listing = $this->getExternalModel('NewsManager');
return $listing->getNews();
}
}
class Application_Model
{
protected $external = array();
protected $db;
public function setConnection($connection)
{
$this->db = $connection;
}
public function getExternalModel($name)
{
if (!is_string($name))
{
throw Zend::exception('Model name must be a string');
}
if (!array_key_exists('db', $this))
{
throw Zend::exception('Failed to obtain database connection');
}
if (!array_key_exists($name, $this->external))
{
$formatted = $this->formatModelName($name);
$this->external[$name] = new $formatted();
$this->external[$name]->setConnection($this->getConnection());
}
return $this->external[$name];
}
protected function getConnection()
{
if (!array_key_exists('db', $this))
{
throw Zend::exception('Failed to get database connection');
}
return $this->db;
}
protected function formatModelName($name)
{
return 'Application_Model_'.ucfirst(strtolower($name));
}
}
?>
and for those of you interested, this is being initialized as
Code: Select all
<?php
abstract class Application_Controller extends Zend_Controller_Action
{
//..
protected function hookModelLayer($object)
{
if (!Zend::isRegistered('database'))
{
throw Zend::exception('Database is not a registered object');
}
$this->model = new $object();
$this->model->setConnection(Zend::registry('database'));
}
}
?>
Does this seem like proper design in the eyes of OOP?
Posted: Wed Jan 10, 2007 2:57 pm
by Christopher
I am a little confused by the code you have posted. It is not clear who is doing what to whom

. First, I really don't think lots of little Actions is a problem. In fact it is sort of the goal. If you have lots of Actions that are small and just include code that is specific to that task then you are getting a lot of code reuse out of your controller architecture. I know there have been a number of comments recently about having lots of action controllers, but I think you need to learn to love them -- especially if they are small and clean.
As far as the Model, I can't tell whether you are trying to do something simple, the hard way. Or if you have some subtle problem that I am missing.
Posted: Wed Jan 10, 2007 3:28 pm
by John Cartwright
Okay maybe I was being a bit too specific with my problem, so let me put this as simple as possible.
I have different model components that I want to reuse, although they belong to different models. Instead of having identical code throughout my application, I would like to be able to use the other model's methods.
What I have done, is in the method that would normally contain duplicate code that another model contains, I made it able to "load" the other model, and supply it with the database connection to return the data to the first model.
Now it has been mentioned these small bits of code are ok to duplicate, but I want to create something similar with my view classes. Such as error displaying. I don't like having the same method, lets say, paintMessage(), which contains several helper methods, across dozens of views. Now that is a small example, but I think it gets the point across.
I fear I may have overcomplicated things.

Posted: Wed Jan 10, 2007 4:09 pm
by Christopher
I think what you are looking for is simply the
Composite/Component pattern which combines multiple class into a single class (without inheritance). I do this with Models pretty regularly because it is simpler to have a single interface for the View to deal with. It also creates a clear Model-Model dependency without involving the Controller or View in what I consider to be none of their business.
Posted: Wed Jan 10, 2007 7:51 pm
by Begby
I think you are looking for composition, but the composite/component pattern is more for creating a nested structure of objects.
Edit: The wikipedia article that was linked by the previous poster appears to have the right name, but its confusing me and the content does not appear to be correct. You are looking to use composition, but not in the way that article shows in the examples (composite/components and leafs and stuff). This may be because I am getting my terminology mixed up.
Anyways, If I am understanding your predicament correctly, I think in this case you want delegates to help with composition. A delegate encapsulates functionality that varies between objects with the same implementation. If you create an abstract object, extend it, then find that a lot of the object implementations are overriding a parent method, then this is a good pattern.
For instance lets say you have an abstract item class with a getPrice() method. Then you find that half of your item types just return a price, 25% return a price based on a sum of parts, and the other 25% return a price based on item weight. If we are talking say 20 or so item type classes then managing getPrice() will become a nightmare of code copy and pasted everywhere. This is where you use delegates.
You can read about it here
http://en.wikipedia.org/wiki/Delegation_pattern <-- I wrote some of the examples on that page. Let me know if you need more splainin', also if you have more questions give more examples of the your code.
Posted: Thu Jan 11, 2007 11:09 am
by John Cartwright
Okay I have been reading both links provided, and believe I kind of understand the PHP implementation of composition and delegation, although I'm having a dificult time applying this to my specific case. I would really appreciate another small example, if possible.
Thanks.
Posted: Thu Jan 11, 2007 9:33 pm
by Begby
I am not sure if I even suggested the correct pattern. I think it would be best if you posted snippets of your code where you are concerned about it repeating before I get too in depth.
Posted: Mon Jan 15, 2007 4:40 pm
by John Cartwright
Sorry for the late reply -- I kind of forgot about this thread
Reguardless, keeping things simple. Heres a simple example where I'd like to avoid repetition. My applications are literally littered with this method. Having 50+ views I think this become a problem when I want to modify something in this view processing.
Code: Select all
class Application_View_Index
{
// ..
public function paintMessage($message)
{
$message = new Application_Template_Helper_Message($message);
return $message->getMessage();
}
// ..
}
Keep in mind, I've kept things very simple, so please avoid saying this is ok to repeat among several locations.
Now, I've began looking at this from another perspective. Instead of loading a view, lets say Application_View_Index, and having that extend my Application_View, I am having my Application_View register Application_View_Index and the controller will dispatch any requests using the __call method. I'll get into why I think this method sucks in a minute. Lets jump into some code.
This is where the call methods are redirect. The __call method intercepts any requests made, then search among the loaded views (the stack) for the available method.
Code: Select all
Application_View extends Zend_View
{
public function __call($request, $args)
{
if (!$this->isStack()) {
throw Zend::exception('No view has been loaded');
}
$found = false;
foreach ($this->stack as $view) {
if (is_callable(array($view, $request))) {
call_user_func_array(array($view, $request), array($args));
$found = true;
}
}
if (!$found) {
throw Zend::exception('Cannot find '. $request);
}
}
}
Now, when I initialize Application_View_Index, I will pass a reference of Application_View to Application_View_Index. This, to me, seems like a horrible way to do things. It is just basically a result of me playing with things until I find something satisfying.
Code: Select all
Application_Core_Loader
{
// ..
protected function hookViewLayer($object)
{
$view = new Application_View(
$this->internalMap->get('controller'),
$this->internalMap->get('action')
);
$view->setScriptPath('Application/Template');
$view->stack($object);
return $view;
}
// ..
}
This seems ok.. I'm still not happy with using this technique, which is why I'm anxiously awaiting furthur responses
