I am getting to a point in my application where there seems to be a lot of presentation logic in my models:
users as $user): ?>
Zend Framework uses the decorator pattern for its form elements. Imagine having an input element with type="text". You could 'decorate' this element with a label, divs, fieldsets, etc.
I've implemented a decorator pattern for my php application. Basically it's a wrapper class to a xml configuration file where I define my basic needs. To simplify I use a pizza as an example. Then I have a class for each ingredient and wrap it around the other class. In the end I call the prize method of each class and it gives me the sum of everything.
$order = new pizza($xml_file);
$order = new add_salami($order);
$order = new add_cheese($order);
$order = new add_tomato($order);
$order = $order->prize();
You need to maintain a pointer to the current object in every ingredient class. When you call the php new function you can use it to backup the current object. It's a bit like a linked list (of objects). Then you can call the prize() method of the last object and loop through all other classes. But to decorate you need to add new classes. You can also replace the $xml_file with a start value. I've all my decorator class in one file. A decorator class can looks like this:
class add_salami {
protected $order;
protected $prize;
public function __construct ($order) {
$this->order = $order;
$this->prize = $order->getPrize();
}
public function getPrize() {
return $this->prize + 10;
}
}
I keep many of these tiny decorator classes in a huge file.
I understand your problem. Yes, Decorator pattern can help you to reuse your classes in even different business logic.
I use decorator patter everywhere possible because it can realy help you. For example, lets say you want to get list of records from database and for this you are using simple repository. which will return you all or one record. like here. For more about repositories go here.
$repo=new ClientRepository();
$repo->all();
But, lets say you have business rule that in some places you should log requested data and in other places you should cache request. So, what you will do. Of course majority will create seperate class for CacheClientRepository and LogClientRepository. It may be ok, but what if business rule will ask you Log and Cache at the same time so what will you create seperate class like LogAndCacheClientReposiotry(); If you do like this you will end up with tons of class that repeat code inside.
For this case the best solution is to use DecoratorPattern. You you will need to create one interface and implement it in every decorator. You can decorate your repository as much as you want.
Here is code sample:
<?php
interface RepositoryInterface
{
public function all();
}
class ClientRepository implements RepositoryInterface
{
public function all()
{
// Your code for database goes here
}
}
class CacheRepository implements RepositoryInterface
{
/**
* @var RepositoryInterface
*/
private $repo;
/**
* CacheRepository constructor.
* @param RepositoryInterface $repo
*/
public function __construct(RepositoryInterface $repo)
{
$this->repo = $repo;
}
public function all()
{
//Code for caching goes here
return $this->cache('cache-all',function(){
return $this->repo->all();
});
}
//......
}
class LogRepository implements RepositoryInterface
{
/**
* @var RepositoryInterface
*/
private $repo;
/**
* LogRepository constructor.
* @param RepositoryInterface $repo
*/
public function __construct(RepositoryInterface $repo)
{
$this->repo = $repo;
}
public function all()
{
//Code for logging goes here
return $this->log('log-all',function(){
return $this->repo->all();
});
}
//......
}
//.. Simple Repository
$repo=new ClientRepository();
//.. Cached Repository
$repo=new CacheRepository( new ClientRepository() );
//.. Logging Repository
$repo=new LogRepository( new ClientRepository() ) ;
//.. Logging and Caching at the same time
$repo=new LogRepository( new CacheRepository( new ClientRepository() ) ) ;
I hope it will help you, beacuse decorator pattern is one of best patterns that I know.