确定PHP

时间:2016-09-27 11:38:41

标签: php

我在PHP 7中编写了一个名为MenuBuilder的类,用于在Web应用程序中构建导航菜单。

该类的工作方式是构建一个输出缓冲区,用于保存菜单的标记。

首先在class MenuBuilder

中声明一个空变量
private $output_buffer = '';

有一种方法可以添加到它:

protected function add_to_output_buffer($input = '') {
    if ($input !== '') {
        $this->output_buffer .= $input;
    }
}

显示菜单标记的方法:

public function render() {
    echo $this->output_buffer;
}

到目前为止非常简单。这就是问题...当我向菜单添加一个链接时,我使用这个方法:

public function add_menu_item($item) {
    $this->menu_items[] = $item;
}

这会构建一个数组($this->menu_items),然后有另一个方法循环其中的项目以将它们添加到输出缓冲区。它很长,但缩短版本就在这里:

public function create_links() {
       foreach ($this->menu_items as $item) {
           $output = '';
           $output = '<a href="'.$item['link'].'">'.$item['text'].'</a>';
           $this->add_to_output_buffer($output);
       }
}

create_links()中有很多逻辑,因为各种类和其他东西都应用于链接,但实际上它必须像这样工作,因为它需要遍历完整的链接数组。

问题: 如果我想在菜单中的任何位置添加随机HTML,我有一个功能:

public function add_misc_html($html = '') {
    $this->output_buffer .= $html;
}

但是在我使用这些功能的脚本中,它并不知道调用事物的顺序。一个例子:

$MenuBuilder = new MenuBuilder;
$MenuBuilder->add_menu_item(['text' => 'Link 1', 'link' => '#']);
$MenuBuilder->add_menu_item(['text' => 'Link 2', 'link' => '#']);
$MenuBuilder->add_misc_html('<h1>testing add_misc_html</h1>');
$MenuBuilder->add_menu_item(['text' => 'Link 3', 'link' => '#']);
$MenuBuilder->create_links();
$MenuBuilder->render();

所以问题是所需的输出是:

<a href="#">Link 1</a>
<a href="#">Link 2</a>
<h1>testing add_misc_html</h1>
<a href="#">Link 3</a>

但这不会起作用,因为当我致电create_links()时,它不知道add_misc_html以及在调用{{1}的整体结构中调用它的位置}}

我可以通过哪些方式获得所需的输出?请注意MenuBuilder需要在add_misc_html()之前的任何地方调用。类中有很多其他东西可以打开菜单并关闭它,因此它需要能够在任何时候修改render()

1 个答案:

答案 0 :(得分:3)

这是因为您的add_misc_html直接写入输出缓冲区,其中add_menu_item修改了对象的结构,您无法混合这些调用。您必须先将修改后的对象状态写入缓冲区,然后才能写入&#34; misc&#34; HTML到缓冲区。这很糟糕。

你可以提出很多解决方案,但我建议你使用的是根本没有输出缓冲区,并且可能用其他几个类来扩展类的逻辑。

class MenuBuilder {

    protected $items = [];

    public function add(MenuItem $item) {
        $this->items[] = $item;
    }

    public function render() {
        return join('', $this->items);
    } 
}

您将拥有不同类型的菜单项,例如您的示例中使用的LinkMisc

interface MenuItem {
    public function __toString();
}

class LinkMenuItem implements MenuItem {

    protected $link;
    protected $text;

    public function __construct($link, $text) {
        $this->link = $link;
        $this->text = $text;
    }

    public function __toString() {
        return '<a href="' . $this->link . '">'. $this->text .'</a>';
    }
}

class MiscMenuItem implements MenuItem {

    protected $html;

    public function __construct($html) {
        $this->html = $html;
    }

    public function __toString() {
        return $this->html;
    }
}

现在你的课将被这样使用。

$builder = new MenuBuilder();
$builder->add(new LinkMenuItem('#', 'Link 1'));
$builder->add(new LinkMenuItem('#', 'Link 2'));
$builder->add(new MiscMenuItem('<h1>Testing html</h1>');
$builder->add(new LinkMenuItem('#', 'Link 3'));

当你想渲染时。

$builder->render();

修改

根据评论中的讨论,我认为您无法将对象呈现为HTML层次结构。你可以通过多种方式解决这个问题,我只是提出一个

您可以添加其他菜单项将扩展的基类

abstract class ParentMenuItem extends MenuItem {

    protected $children = [];

    public function addChild(MenuItem $child) {
        $this->children[] = $child;
    }

    public function __toString() {
        return '<ul>' . join(null, $this->children) . '</ul>';
    }

}

然后您的LinkMenuItem将会是这样的

class LinkMenuItem extends ParentMenuItem {

    protected $link;
    protected $text;

    public function __construct($link, $text) {
        $this->link = $link;
        $this->text = $text;
    }

    public function __toString() {
        return '<a href="' . $this->link . '">'. $this->text .'</a>' . parent::__toString();
    }
}

当你想要添加孩子时,你会将它们添加到他们所属的菜单项(呃?)

$builder = new MenuBuilder();

$link1 = new LinkMenuItem('#', 'Link 1');
$link1->addChild(new LinkMenuItem('#', 'Child1'));
$link1->addChild(new LinkMenuItem('#', 'Child2'));
$builder->add($link1);

$builder->add(new LinkMenuItem('#', 'Link 2'));
$builder->add(new MiscMenuItem('<h1>Testing html</h1>'));
$builder->add(new LinkMenuItem('#', 'Link 3'));

您还可以启用链接以避免使用变量和各种其他好东西,例如工厂等。