The Situation: I am building a custom PHP application framework. I have implemented a composite pattern so I can build a object tree representing the page to be rendered. Example:

abstract class \Block\BlockAbstract {
     protected _blocks = array();
     public function __construct($properties);
     public function getBlocks();
     public function addBlock($block);

     ...

     public function render();

}

All of my "block" object inherit from this abstract class which is working out great and rendering has been a snap with the help of a little recursion.

The Problem: I need a way to validate what classes of blocks can be added to other blocks. For example I have 4 distinct block types I have to consider. Keep in mind that ALL blocks inherit from a single block abstract, these are classifications of behavior of the concrete classes, not to be confused with additional abstracts:

  • Generic - Generics can have any other block type added to it

  • Final - Can not have any child blocks added to it.

  • Parent - A parent is a block that can only have a specific type child added to it, but can be added to any Generic

  • Child - Can only be added to a specific parent. This block can also share the properties of a Generic, Parent or Final

To sum the goal up: Given a concrete block class name, generate a list of all concrete block classes that can be added to it.

Solutions Proposed So Far:

Add a property called "blockClassification" to the block abstract that defines it as a Generic, Final, Parent or Child and hard code the logic into a validate function that gets called in the from the addBlock($block) method.

I don't like this solution for a number of reasons, but mainly it still doesn't give me a clear path for defining what the concrete parents and children are that can be allowed. For instance, I have a concrete class \Block\Tabs that is a parent. It can only have \Block\Tabs\Panel blocks added to it, and \Block\Tabs\Panel can only be added to \Block\Tabs. Additional properties would have to be added to build those relationships, and those properties could be examined to derive the "block classification" making this approach impractical.

Add allowedChildTypes property to the block that defines a list of concrete classes that can be added. For ease of use 2 values have specific meaning, a * that represents all block types, and NULL value that means no blocks can be added. For all other cases a coma delimited string of class names is provided and only those classes or classes that inherit from them are allowed

I'm leaning towards this direction because PHP gives me the InstaceOf operator. I can use this to do the checks, when I have new concrete classes that extend the base set, that check will return the same results as its parent class. My only hesitation is that while this method definitely solves the problem of "Given a concrete block class name, generate a list of", I feel like it limits me in the future in that I can only do the search down the tree and not back up. I.e. "Given a child block class name, generate a list of parent blocks it can be added to"

I'm sure that I am not the only one to ever run into the problem, so my question to the masses is: What is the best way to approach/solve this problem?

Additional Information:

If you where wondering how I'm getting a list of all the classes in the first place, the framework implements PSR-0 so I am scanning the directory tree to derive all of the class names, then dynamically instantiating them.

$allClassNames = scanDirectories(); // Returns array of strings ex: array('\Block\Classname1','\Block\Classname2'); 
foreach($allClassNames as $className){
  $object = new $className();
}
  • 2
    Fee Fye Foe Fum, I smell the blood of a type systum.... PHP doesn't have a very static one, hand implementing your own over top of typeless languages ending up with a type system that is only validated at runtime (aka you only find out the type errors when they happen) is a well worn path to insanity. Now you're bolstering your accidental run-time errors from handing a turtle to a method that calls quack with accidental run-time errors from getting a type slightly wrong or the type hierarchy slightly wrong – Jimmy Hoffa May 16 '14 at 21:05
  • Unfortunately I think this is the nature of the problem that I am trying to solve. The framework essentially wraps foundation 5 UI components w/ PHP Objects. Because some UI components should only be added to other types (ex: a column in a grid, or a panel in a tab group, or a menu item in a menu bar) I have to account for that. Sure I could allow the end developer to add what ever block to what ever block, but it wouldn't render correctly and I would like to provide some level of error reporting. – Steve Zurek May 16 '14 at 22:54

I don't really know PHP, but my understanding from the other comments is that it's not strongly typed. If I were trying to solve this problem in C# or Java, my preference would be to use inheritance over composition pattern. "Is a" instead of "has a."

What I would do is have a base class for each of your block classes; generic, final, parent, child. I would additionally create a helper class to do your checking for the types of concrete classes that can be added to each of the block classes. For example

public class GenericBlock implements BlockAbstract { _helper = new HelperClass();

  public function AddBlock(block)
  {
      _helper.CheckBlockType(block);
  }

}

In the helper class, I would have helper methods for each of the block types you need. The benefit is that if you need to change or add more rules, it's easily extensible through a helper class. If I were dealing with a typed language, my rules for what classes could be added to the block class would be based on the type. It sounds like your rules are more open, so you might want to just have lists of class names in your helper methods. Not elegant, but will get the job done.

Composite often goes hand in hand with Builder pattern to address exactly the problem you have mentioned here. You can create a separate class, call it PageBuilder (or whatever suits your fancy) and move all the building logic into that class. Although I don't know the internals of your code but considering a very generic composite/builder duo here is some sample code for your ref that can get you started.

class PageEelement()
{
    //properties & methods of basic composite element 
    public add(PageEelementInterface $element){

    }
    public remove(PageEelementInterface $element){

    }

    ....
    ....
}

/* Next comes the Builder */

class PageBuilder(){

    $protected $page;

    public construct(PageEelementInterface $page){
        $this->page = $page;
    }         
    public addBlock($params){
        //block creation with rules
        if($param == x)
            $block =  new PageElement($paramas);
        } else {
            $block =  new PageElement($paramas);
        }
        $this->page->add($block);

    }

    public function addPanel($params){
        //panel building conditions and validation 
    }

    public render(){
        $this->page->render();
    }
}

and you can call it like

$page = new PageElement('title', 'width', 'height');
$pageBuilder = new PageBuilder($page);
$pageBuilder->addBlock('title', 'width', 'height');
$pageBuilder->addPanel(params);

$pageBuilder->render();

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.