在面向对象的PHP中构建复杂的条件表

时间:2012-07-23 19:55:09

标签: php design-patterns

我目前正在开发一个Zend Framework项目,用户通过该应用程序的最终结果将为他们提供一个包含其结果的大表。此表可以包含不同数量的行,其中大多数是作为计算结果生成的。到目前为止,非常好。

第一个;虽然是一个小障碍,但桌子需要水平展示。也就是说,标题位于第1列,每个附加列表示数据实体项。例如:

Forename | James | Richard
Surname  | Jones | Mayfair

同样,这不是问题。当我整齐地构建桌子时,问题就出现了。使用构建器模式,我有三个类:Table,Group和Row。它们可以像这样使用:

$table = new Table($attribs);
$group = new Group(); // Nothing special about groups, they're just there for helping with presentation

$row = new Row();
$row->setTitle('Forename')
    ->setData(array('item1' => 'James', 'item2' => 'Richard'))

$row2 = new Row();
$row2->setTitle('Surname')
     ->setData(array('item1' => 'Jones', 'item2' => 'Mayfair'))

$group->addRow($row)
      ->addRow($row2);

$table->addGroup($group);

上面的代码段出现在控制器的操作中。然后将表对象传递给视图,我有一个视图助手,它将表输出到我自己的规范。

然而,我面临的挑战是,我最终得到了一个非常混乱的“结果”模型。例如,我有很多:

$group->addRow($this->_resultsModel->getSurnameRow());

'_resultsModel'属性是一个包含许多方法的对象:

public function getSurnameRow()
{
    $row = new Row();
    $row->setTitle('Surname');
    $row->setData($this->_getAssociateArrayOfSurnames());
    return $row;
}

此外,结果对象扩展了结果抽象类,其中包含原始数据的访问器,设置器和计算方法。这些“计算”方法中的每一个都返回一个可以分配给行对象的关联数据数组。结果对象和抽象类都很长,只是看起来脱节。

总而言之......我最终得到了一个大型结果模型,可以完成所有计算并返回所有行对象。虽然它确实有效,但我确信有更好的做事方式。我应该在控制器中构建行吗?我应该只调用模型中的计算吗?有没有人有抽象这样的东西的经验?

我很抱歉这是一个很糟糕的解释。如果有人有任何我可以提出的问题,请告诉我。

1 个答案:

答案 0 :(得分:0)

首先,控制器内部不应该有太多的逻辑 - 控制器可以处理模型的输入并将结果转发到输出(视图),而不是计算东西。

第二:你的结果有很多其他的事情要做,为什么它必须处理表格表示?另外,为什么它必须有很多单独的函数,只有字符串不同?

我想我会在声明性数组中做一些事情:也就是说,我会像这样收集一个声明性数组:

$table_description = [
     'group1' => ['surname', 'firstname', 'whatever'],
     'group2' => ['other', 'rows', 'as', 'needed']
];

并构建一些解析此描述并用实际数据填充的内容(如果需要,使用反射)

主要问题是:表格跟随功能,算法遵循数据结构。

如果您有一个数据结构,它被分为表,组和行,那么您可能会有一个algorhythm,它会做同样的事情。

第二个问题是:变化可能发生在哪里?如果表方案可以轻松更改,请将它们放入“config”(描述符)。如果行的格式可以轻松更改,请确保它们是自格式化的(或者有人知道如何请求格式化程序,而不知道行实际包含的内容)

一个好主意是能够回退到默认值:也就是说,如果大多数行是以默认方式构造的,那么请确保您能够使用默认方法,例如:

class Row {
   public $formatter;
   private $data_assoc;
   private $title;
   public function __construct($title, $data, RowFormatter $formatter=NULL){
       if ($formatter == NULL){
          $this->formatter = DefaultFormatter::getInstance();   
       }
       /*set title, data_assoc...*/
   }
   public function setFormatter(RowFormatter $formatter){/*obvious*/}
   public function format(){
       return $this->formatter->format($this->data_assoc);
   }
}

现在看来,你的resultsModel有太多责任,你有这些前缀名称(getSurnameRow) - 为什么resultsModel应该知道所有行?此外,关联数组(映射)是非常好的结构,尤其是在PHP中。为什么getData('Surname')不够用?为什么Row不能从标题和关联数据构建自己?

如果您想使用不同的属性执行相同的操作,请使用枚举或字符串。如果您需要能够有时插入内容,请使用工厂,该工厂根据属性名称查找索引表,如果不是例外情况,则返回默认实现。

$row = RowFactory::getRow('Surname');
$row->setData($this->data['Surname']);

这样的代码复制很好,只要它有两个属性和简单的正交性 - 比如getX,getY。但是由于具有无限的属性,我想这只是重复。