【问题标题】:PHP OOP core frameworkPHP OOP 核心框架
【发布时间】:2012-04-08 10:15:30
【问题描述】:

我只是发布这个问题,所以你们中的一些人可能能够以正确的方式指出我。我正在慢慢为 OOP 热身,开始理解这个概念。我想制作一个良好的坚实核心或基础以用作 CMS 后端。它还将使用 MVC。我一直使用http://gilbitron.github.com/PIP/ 作为 MVC 基础。

我想不通的是:

说,在后端的项目页面上,我有 2 个部分:htmltext 和项目,我应该能够同时编辑它们。 uri 将类似于: //domain/backend/projects(方法是索引并显示2个部分)

当我点击项目时应该如何处理? //域/后端/项目/项目/或 //域/后端/项目/列表/

更进一步,一个项目将包含一些图像或画廊: //domain/backend/projects/edit/5/gallery/2

我的问题是,首先:这是一个好方法吗?更重要的是如何在 OOP 中实现它

主要项目控制器:

class projects {

    function index(){
        // view index
    }

    function edit{
        $project = new Project();
        $projectdata = $project->load(5);
    }
}

单个项目控制器

class project {

    function __construct(){
        $this->projectmodel = $this->loadModel('project_model'); // prepare the model to be used
    }

    function load($id){
        $this->projectmodel->loadproject($id);
    }
}

项目模型

class project_model extends model { //extends for DB  access and such

    function __construct(){
        // do stuff
    }

    function loadproject($id){
        return $this->db->query("SELECT * FROM projects where id=" . $id . " LIMIT 1");
    }
}

现在我的问题。如果这个项目有图像,我应该在哪里加载图像类来处理这些?我应该像 $this->images = new Images(); 这样将它加载到 project_model 中吗?并在模型内部有一个函数

function loadimages($id){
    $this->images->load($id);
}

然后图像会是这样的:

class image extends model { //extends for DB  access and such 

    function __construct(){
    }

    function load($id){
        return $this->db->query("SELECT * FROM project_images where id=" . $id . " LIMIT 1");
    }
}

似乎控制器和模型以这种方式混淆了。然而,从逻辑上讲,项目是一个包含项目信息的容器,它可以是文本、图像,也可能是视频。我将如何在逻辑上进行设置。

【问题讨论】:

  • 附带说明,将类名的首字母大写 class Image extends Model { 是一种常见的命名约定。

标签: php oop class object content-management-system


【解决方案1】:

原来的问题

关于 URL 的第一部分称为:路由或调度。 article 与 Symfony 2.x 的关系非常好,但它背后的想法才是最重要的。此外,您可能会查看其他框架如何实现它。

对于您的原始 URL 示例,图库将存储在 DB 中。他们不会吗?他们将有一个唯一 ID。这使得/backend/projects/edit/5/gallery/2 毫无意义。相反,您的 URL 应该看起来更像:

/backend/gallery/5/edit         // edit gallery with ID 5
/backend/project/3              // view project with ID 3
/backend/galleries/project/4    // list galleries filtered by project with ID 4

网址应该只包含您真正需要的信息。

这也表示 3 个控制器:

  1. 单一画廊管理
  2. 单一项目管理
  3. 处理画廊列表

示例 URL 的模式类似于:

/backend(/:controller(/:id|:page)(/:action(/:parameter)))

/backend 部分是强制性的,但 controller 是可选的。如果找到控制器,则id(或页面,当您处理列表时)和action 是可选的。如果找到操作,则附加 parameter 是可选的。如果写成正则表达式,这种结构可以让您处理大部分路由。

OOP 超越类

在开始使用或编写某种 PHP 框架之前,您应该学习如何编写正确的面向对象代码。这并不意味着“知道如何编写课程”。 意味着,您必须真正了解什么是面向对象编程,它基于什么原则,人们常犯的错误以及最普遍的误解是什么。以下是一些可能对您有所帮助的讲座:

这应该给你一些主题的概述.. 是的,它很多。但怀疑你会更喜欢视频而不是书籍。否则,一些阅读材料:

您会注意到许多材料与语言无关。这是因为对于基于类的面向对象语言的理论是相同的。

附言

请注意代码中的 extends 关键字。这意味着“是一个”。没关系,如果class Oak extends Tree,因为所有的橡树都是树。但是如果你有class User extends Database,有人可能会被冒犯。实际上有一个 OOP 原则在谈论它:Liskov substitution principle .. 还有一个很short explanation

【讨论】:

  • 感谢 tereško,非常有帮助和洞察力。我也会浏览你分享的链接。我一直在阅读 OOP。我开始了解如何使用 OOP,但是一旦您尝试使其适用于更大的系统,您就需要很好地了解什么以及为什么要这样做。路线的一部分是一个令人惊叹的时刻。让画廊控制器处理一个项目。这确实意味着依赖,我了解到应该尽可能避免依赖,但现在它完全没问题。感谢您的广泛解释。我会深入研究。
  • @MisterM ,好吧 .. 画廊控制器并没有真正处理这个项目。它只是与选定的画廊一起工作。这个画廊与一个项目相关联的事实对其只有次要影响。
  • 更正:is-a 是值和类之间的关系。类之间的关系,例如子类化或子类型化,称为a-kind-of。也就是说:"foo"is-aStringSubTypea-kind-ofSuperType
  • @RandallSchulz 在过去的几天里,我了解到必须使其尽可能简单。你是(或至少简介是这样说的)母语人士。但可以合理地假设,这里约 98% 的用户群没有英语作为第一语言。有人可能会将“一种”误解为“有点像”,这完全无法传达 LSP 背后的想法。
  • @tereško:这与语言熟悉度无关,而是关于正确使用技术术语。软件开发是一个技术含量很高的领域。我们(无论如何,专业人士)如此松散、不准确和不精确地使用术语是对自己的伤害。无论如何,人们来这里是为了自我教育,这就是我的答案和 cmets 的目标。有关术语的事实是该活动的有效部分。
【解决方案2】:

(由于问题本身已经足够长,我不想扩展它,所以我添加了这个作为答案)

projects.php 这个控制器扩展了项目控制器。主要目的是使用自己的模型处理多个getAllProjects(),然后将其传递给项目控制器getProject()

class Projects extends Project{

public function __construct()
{
    parent::__construct();
    $this->projects_model = new Projects_model();
}       

public function getAllProjects()
{
    $res = $this->projects_model->getAllProjects();     
    if($res) foreach($res as $v) $projects[] = $this->getProject($v);

    return $projects;
}
}

project.php
project_model 只是获取项目数据和相关数据(如图像)

class Project{

public function __construct()
{
    $this->project_model = new Project_model;
    $this->images = new Images();
}


public function getProject($data=NULL)
{
    if($data==NULL) $data = $this->project_model->loadProject($data['id']);
    $images = $this->project_model->loadProjectImages($data['id']);

    return array('project_id' => $data, 'project' => $data, 'images' => $images);
}   


public function getProjectByID($id)
{
    $data = $this->project_model->loadProject($id);
    return getProject($data);
}   

}

这种方法的好处是,我可以单独使用项目类,它会起作用。我可以将其封装在项目中,也可以使用。

这是做事的好方法吗?还是将所有这些放在一个控制器和一个模型类中会更好?

【讨论】:

    【解决方案3】:

    如果这个项目有图像,我应该在哪里加载图像类来处理这些图像?我应该像 $this->images = new Images(); 这样将它加载到 project_model 中吗?并在模型内部有一个函数

    是的。该模型的目标是封装业务逻辑,这样您只需编写一次算法,并多次共享它们。如果我是你,我会做的是向 Project 模型添加一个 getImages() 方法,因为图像是项目的直接属性,这使得每个项目都知道如何检索它自己的图像。

    与我使用过的 ORMS(水合和对等/表)类相比,您的代码缺少一些关键概念,因此我会尽量坚持使用您的代码。您可以通过 Project 对象检索图像,然后通过 2 种方式中的视图 1 引用图像:

    // Controller
    $this->project = new Project();
    $this->projectImages = $project->getImages(); // Implemenation #2
    
    // View
    
    // Implemenation #1 (reference to object initialized in controller)
    <?php foreach ($project->getImages() as $image): ?>
    <img src="<?php echo $image['path'] ?>" />
    <?php endforeach ?>
    
    // Implemenation #2 (reference to separate variable initialized in controller)
    <?php foreach ($projectImages as $image): ?>
    <img src="<?php echo $image['path'] ?>" />
    <?php endforeach ?>
    

    以上只是说明性的,但应该给你一个想法。我建议你看看Doctrine。它是一个经过验证且稳定的 ORM,它提供了您尝试编写的大部分内容,此外它还添加了我之前提到的缺失组件;水合、对象关系和表格类,以及许多其他功能,例如嵌套集。

    【讨论】:

    • 谢谢 Mike,我将深入研究 ORM。阅读您的答案后,我确实编写了一些新代码。该代码还将克服人们遇到的复数/单一问题。我不确定这是否是一种合乎逻辑的方式。我会发布它。
    • 我曾经使用复数,但已经看到单数更好。我唯一一次复数任何 var 是如果我有一个数组,语义上听起来更好,而且允许您将单个元素命名为复数的单数版本:foreach ($elements as $element) {