PHP設計模式之組合模式 Composite
組合模式(Composite Pattern),又叫部分整體模式,是用於把一組相似的物件當作一個單一的物件。組合模式依據樹形結構來組合物件,用來表示部分以
及整體層次。這種型別的設計模式屬於結構型模式,它建立了物件組的樹形結構。
使用過
laravel-admin
這個快速後臺的同學,應該都使用過它的表單類Form
建立表單的時候,只需要new Form
,然後新增對應的輸出的表單
元素就可以了,很快捷,很方便,其實它就是使用了 組合模式根據以上的例子,首先需要定義一個輸出的元素(組合元素)的介面
Renderable
它裡面也之定義了一個方法render():string
型別的返回引數
interface Renderable
{
public function render():string;
}
- 建立一個根元素
Form
類,實現介面Renderable
,在render(): string
這個方法中,需要返回已經組建好的完整表單元素,在這個方法中我們稍微
做一下改進,我們需要定義兩個成員變數,都是陣列,一個是我們事先定義好的表單元素陣列類,提供給我們最後呼叫表單方法的時候使用,還有一個提前定義
好的空陣列,這個是我們最後元件完整表單的時候使用。最後再寫一個魔術方法,呼叫我們的具體表單元素。
class Form implements Renderable
{
private $action = './index.php';
private $method = 'get';
protected $elements = [
'text' => Text::class,
'email' => Email::class,
'password' => Password::class,
'file' => File::class,
'radio' => Radio::class,
'number' => Number::class,
'hidden' => Hidden::class,
];
protected $items = [];
public function addElements(string $formName, Renderable $renderable)
{
if (!isset($this->elements[$formName])) {
$this->elements[$formName] = $renderable;
}
}
public function setAction(string $action)
{
$this->action = $action;
return $this;
}
public function getAction()
{
return $this->action;
}
public function setMethod(string $method)
{
$this->method = $method;
return $this;
}
public function getMethod()
{
return $this->method;
}
public function render(): string
{
// TODO: Implement render() method.
$formString = <<<ERT
<form action="{$this->getAction()}" method="{$this->getMethod()}">
ERT;
foreach ($this->items as $item) {
$formString .= $item;
}
$formString .= <<<TRE
</form>
TRE;
return $formString;
}
public function pushField(string $fields)
{
$this->items[] = $fields;
return $this;
}
public function __call($name, $arguments)
{
// TODO: Implement __call() method.
if (!isset($this->elements[$name])) {
$message = sprintf('this is method %s not exists!', $name);
throw new RunException($message);
}
$class = $this->elements[$name];
$class = new $class;
if (is_array($arguments)) {
$field = $arguments[0];
} else {
$field = (string) mt_rand(100, 999);
}
$formText = $class->setField($field)->render();
$this->pushField($formText);
return $this;
}
}
- 建立一些表單元素,也需要實現介面
Renderable
,在各自的render(): string
方法中,返回當前建立的子表單,比如這裡列舉建立的text, email, password
class Email extends Field implements Renderable
{
public function render(): string
{
// TODO: Implement render() method.
return <<<ERT
<div class="form-group">
<label for="{$this->getFiled()}">{$this->getFiled()}</label>
<input type="email" name="{$this->getFiled()}">
</div>
ERT;
}
}
class Text extends Field implements Renderable
{
public function render(): string
{
// TODO: Implement render() method.
return <<<ERT
<div class="form-group">
<label for="{$this->getFiled()}">{$this->getFiled()}</label>
<input type="text" id="{$this->getFiled()}" name="{$this->getFiled()}">
</div>
ERT;
}
}
class Password extends Field implements Renderable
{
public function render(): string
{
// TODO: Implement render() method.
return <<<ERT
<div class="form-group">
<label for="{$this->getFiled()}">{$this->getFiled()}</label>
<input type="password" name="{$this->getFiled()}">
</div>
ERT;
}
}
- 測試,我們首先
new Form()
類,然後新增正常的表單元素,最後輸出到頁面上,得到一個完整的表單
require '../vendor/autoload.php';
use Composite\Form;
//http://local.test.abc/
$form = new Form();
$form->text('username');
$form->password('password');
$form->email('email');
$form->number('number');
$form->file('file');
$form->hidden('hidden');
$style = <<<ERT
<style>
.form-group {
padding-bottom: 20px;
}
</style>
ERT;
echo $style;
echo $form->render();
/**
* <form action="./index.php" method="get"><div class="form-group">
<label for="username">username</label>
<input type="text" id="username" name="username">
</div><div class="form-group">
<label for="password">password</label>
<input type="password" name="password">
</div><div class="form-group">
<label for="email">email</label>
<input type="email" name="email">
</div><div class="form-group">
<label for="number">number</label>
<input type="number" name="number">
</div><div class="form-group">
<label for="file">file</label>
<input type="email" name="file">
</div><div class="form-group">
<input type="hidden" name="hidden">
</div></form>
*/
本作品採用《CC 協議》,轉載必須註明作者和本文連結
LIYi ---- github地址