在不破坏封装的情况下实现此目的的正确方法是什么?我保持示例简单,但如果我决定添加更多实体,解决方案应该可以很好地扩展。我有一个学生和类似的课程:
class Student {
private $id;
private $name;
/**
* Courses student is attending
* @var Course[]
*/
private $courses = array();
private $numberOfAttendingCourses;
}
class Course {
private $code;
}
让我说我想渲染一下:
<ul>
<li>[studentName] ([numberOfAttendingCourses])</li>
</ul>
这也是另一个用例中的视图
<ul>
<li>[studentName] (ID: [studentId])
<ul>
<li>[course1Name] ([course1Code])</li>
<li>[course2Name] ([course2Code])</li>
</ul>
</li>
</ul>
我知道如何使用getter和setter来实现它。我对保持两个对象尽可能封装的方式感兴趣。
答案 0 :(得分:4)
最简单的方法是将值结构传递给模板渲染器。这将使您免于在模板本身内做太多逻辑。
例如 - 首先为您的可渲染类创建一个接口
class Renderable {
public function getViewData();
}
然后让您的学生和课程类符合界面。
class Student implements Renderable{
private $id;
private $name;
/**
* Courses student is attending
* @var Course[]
*/
private $courses = array();
private $numberOfAttendingCourses;
public function getViewData(){
$dat = array("id"=>$this->id, "name"=>$this->name, "courses"=>array());
foreach($this->courses as $course){
$dat["courses"][] = $course->getViewData();
}
$dat["numCourses"] = count($dat["courses"]);
return $dat;
}
}
class Course implements Renderable{
private $code;
private $name;
public function fetchViewData(){
return array("code"=>$this->code,
"name"=>$this->name);
}
}
创建模板解析器。
class Template {
public function render($templateFile, array $data){
if ($data instanceof Renderable){
$data = $data->getViewData();
}
extract ($data);
ob_start();
include($templateFile);
ob_get_clean();
}
}
然后是标记的简单模板
<ul>
<li><?= htmlspecialchars($name) ?> (ID: <?= htmlspecialchars($id) ?>)
<ul>
<?php foreach($courses as $course): ?>
<li><?= htmlspecialchars($course["code"]) ?> (<?= htmlspecialchars($course["code"]) ?>)</li>
<?php endforeach; ?>
</ul>
</li>
</ul>
然后,一旦你有了一个有效的学生对象,你就可以
$template = new Template();
$template->render("/path/to/template.phtml", $student->fetchViewData());
这也使您可以非常简单地开发和测试模板,因为您可以使用可维护的数组,而不是去创建学生和一堆Course对象。