PHP CMS Project Part V: The Factory Method

cms5A Simple Factory Method CMS

In deciding which design pattern to apply to this CMS, two came to mind. Because PHP is creating a Web page, creational patterns appeared to be the most appropriate. Both the Factory Method and the Abstract Factory were considered. The Factory Method is the simplest of the two and it can be expanded, and so I settled on it. The Abstract Factory would be more appropriate if the main goal was to select different Web page styles. This CMS is to update and use data from a MySQL table. Since a lot of files make up this CMS, it’s probably a good idea to download them first:
Download

To get started, take a look at Figure 1. In Part IV of this series you saw how to put data into and retrieve it from a database and use in a Web page. That is pretty much what has been done here except the request is through the Creator interface to insure loose coupling. The PageCreator implements the factoryMethod() method which creates a requested product specified by the Client. As a result the Client is only loosely bound to the concrete product it requests.

Figure 1: File Diagram for Factory Method CMS

Figure 1: File Diagram for Factory Method CMS

When the concrete creator object (PageCreator) passes the request for the concrete Product object (PageProduct1()), it does so through the deliverWeb() method—the method implemented through the Product interface.

Multiple Inheritance (Sort of)

Some languages like Java and Python support multiple inheritance. Strictly speaking, PHP does not, but you will find workarounds and you can combine inheritance and implementation in a single child class. In the declaration of the PageProduct1 class, you can see how this is done:

<?php
include_once('UniversalConnect.php');
include_once('Product.php');
include_once('ProductTools.php');
class PageProduct1 extends ProductTools implements Product
{
    function deliverWeb()
    {      
        $this->tableMaster="blogette";
        $this->hookup=UniversalConnect::doConnect();
        $this->loadContent();
        $this->content = array("css" => $this->css,
                         "title" => $this->title,
                         "header1" => $this->header1,
                         "header2" => $this->header2,
                         "body1" => $this->body1,
                         "body2" => $this->body2,
                         "image1" => $this->image1,
                         "image2" => $this->image2,
                         "caption1" => $this->caption1,
                         "caption2" => $this->caption2
                         );   
        return $this->content;
    }
 
    private function loadContent()
    {
        //Create Query Statement
	$this->sql ="SELECT * FROM $this->tableMaster WHERE id = 1";
 
	if ($result = $this->hookup->query($this->sql))
	{
	    while ($row = $result->fetch_assoc()) 
	    {
                $this->css = $row['css'];
                $this->title = $row['title'];
                $this->header1 = $row['header1'];
                $this->header2 = $row['header2'];
                $this->body1 = $row['body1'] . "<p />";
                $this->body2 = $row['body2'] . "<p />";
                $this->image1 = "images/" . $row['image1'];
                $this->image2 = "images/" . $row['image2'];
                $this->caption1 = $row['caption1'];
                $this->caption2 = $row['caption2'];
	    }
            $result->close();
	}
        $this->hookup->close(); 
    }
}
?>

The declaration line:

class PageProduct1 extends ProductTools implements Product

imbues PageProduct1 with both the properties of the ProductTools and the interface of the Product. By having the abstract class ProductTools provide the essential properties of a Product child, the properties need not be declared in the concrete product child classes and add some uniformity. At the same time the Product child implementations have the common Product interface. If you’re thinking that a single abstract class could have handled both the method and the properties, you’d be right. However, but having a separate class for properties, the same design could easily be adapted to include a whole new set of properties if need be.

You probably recognize PageProduct1 as the RetrieveBlog class from Part IV of the CMS series. About the only difference is that I created an abstract class ProductTools to store common product properties for use by any other product classes that the developer may care to add. It reduces clutter and makes it easier to make small changes. For example, one strategy considered is to create several different instances of PageProduct1, such as PageProduct2, PageProduct3 and so on. Each could simply identify a different table ID number to be called from the Client class. On the other hand, new data could be added simply by using an UPDATE SQL command to change the content of the record with an ID selected by the class.

Figure 2 shows an added page delivered as a concrete product upon a Client request, and as you can see, it looks just like the others in this series, but the content has changed:

Figure 2: Page created with Factory Method CMS

Figure 2: Page created with Factory Method CMS

The nature of the page has not changed, but the way in which it is requested has. The request is through the Creator interface; so next we’ll take a look at the concrete creator class and the new Client class.

Request to the Creator

The real “un-coupler” participant in the Factory Method design pattern is the Creator and its implementations. In this instance, the single concrete class passes on the request from the Client for a Product. In the following listing are both the Creator abstract class and its child class, PageCreator: (Note: Product type hinting is used so that additional concrete product implementations may be called through the same interface. This is an example of the design pattern principle of program to the interface (Product) and not the implementation (PageProduct1).

<?php
//Creator.php
abstract class Creator
{
	protected $webProduct;
	abstract public function factoryMethod(Product $pageNow);
}
?>
 
?php
//ConcreteCreator.php
include_once('Creator.php');
 
class PageCreator extends Creator
{
	public function factoryMethod(Product $pageNow)
	{
		$this->webProduct=new $pageNow();
		return($this->webProduct->deliverWeb());
	}
}
?>

At this point, you may wonder where the content of the $pageNow parameter comes from. In this case, and all along in this series actually, the Web page has been the client. It is requesting content for the HEREDOC page. So, we might as well put it where it belongs—in the Client class as shown:

<?php
include_once('PageProduct1.php');
include_once('PageCreator.php');
 
class Client
{
    private $content;
    private $document;
    private $hd;
 
    function __construct()
    {
        $this->content=new PageCreator();
        $this->hd = $this->content->factoryMethod(new PageProduct1());
        $this->document = <<<CMSTEMPLATE
        <!DOCTYPE html>
        <html>
        <head>
        <link rel="stylesheet" type="text/css" href={$this->hd["css"]}>
        <meta charset="UTF-8">
        <title>{$this->hd["title"]}</title>
        </head>
        <body>
        <header>
            <h1>{$this->hd["header1"]}</h1>
        </header>
            <article>
                <header>
                <h2>{$this->hd["header2"]}</h2>
                </header>
                {$this->hd["body1"]}
                {$this->hd["body2"]}
                <section class="doRight" >
                    <img src={$this->hd["image1"]}>
                    <caption ><span class="pixcaption"><br />{$this->hd["caption1"]}</span> </caption>
                </section>
                <section class="doLeft">
                    <img src={$this->hd["image2"]}>
                    <caption><span class="pixcaption"><br />{$this->hd["caption2"]}</span> </caption>
                </section>
            </article>
        </body>
        </html>
 
CMSTEMPLATE;
    echo $this->document;
    }
}
$worker = new Client();
?>

The two lines in the Client that show it to be part of a Factory Method design pattern are:

$this->content=new PageCreator();
$this->hd = $this->content->factoryMethod(new PageProduct1());

The Client makes the request through the PageCreator class and then invokes the factoryMethod() to specify the product, PageProduct1. Now suppose later that you want to make more products—implementations of the Product interface. Because the request in the Creator specifies through type hinting is for the Product and not any specific implementation of the Product interface, it can handle any Product implementation.

You may want to review other implementations of the Factory Method on this blog for a review in understanding how everything works together. However, it comes down to Request -> Creator -> Product.

The CMS Co-Stars

In wrapping up this series on creating a CMS using a design pattern in PHP, you will find a folder full of some of the tools you’ll need for creating and maintaining the system in a folder named “Administrative.” At some point soon, I hope to implement it for the http://www.sandlight.com site, and I’d be very interested in what others have done or ideas for making it better. In the meantime enjoy exploring its possibilities.

Share

Copyright © 2013 William Sanders. All Rights Reserved.

0 Responses to “PHP CMS Project Part V: The Factory Method”


  • No Comments

Leave a Reply