The DoYouHaoBaby PHP Framework中文开发文档手册
Copyright(c) DoYouHaoBaby.NET All Rights Reserved.
[ 创建需要的模型 ]

确定了模型的属性和行为,就可以创建模型了。

创建存储模型的数据表

模型在程序中都是用一个类来表现的,然后通过添加方法和属性来定义出完整的模型。但如果模型需要保存到数据库,那么数据库中就必须有保存该模型需要的数据表。

虽然没有强制要求,但大多数情况下,模型的属性和数据表的字段是一一对应的( 当然这个可以通过映射来解决 ):

user 模型属性对应的数据表字段
user_id INT
user_name VARCHAR
user_password VARCHAR

 

task 模型属性对应的数据表字段
task_id INT
task_subject VARCHAR
task_description TEXT
create_dateline INT
update_dateline INT
task_is_completed BOOL


 
这种一一对应可以减少许多麻烦,提高开发效率,这也是“契约式编程”概念的一部分。

注意上表中,我们为 task 和 user 模型分别添加了 task_id 和 user_id 两个属性。这两个属性会被设置为数据表的主键。主键让数据库和应用程序可以更容易更快速的查找保存的对象,因此对于绝大多数模型,我们都需要添加一个单独的主键。


ORM 的作用

不过上表列出的字段是不完整的。因为 user 模型和 task 模型之间的关系并没有体现出来。这就是“对象与关系式数据库之间的阻抗不匹配”问题。

对象与关系式数据库之间的阻抗不匹配问题,根源在于关系式数据库没有办法直接保存对象与对象之间的关系。例如一个用户有多个任务,在面向对象体系中就是“用户”对象聚合了多个“任务”对象,而关系式数据库不支持这样的表述方式。

注意:所以在关系式数据库中存储对象间的关系需要特别的方法,并且需要 ORM 系统来提供帮助。看到这里,就应该明白 ORM(Object-relational mapping —— 对象关系映射)的真正含义了。

例如“用户”和“任务”关联,而“任务”和“任务评论”关联,“任务评论”和“评论人”关联。在这些关联对象间导航应该是很自然和简单的事情:


例如“用户”和“任务”关联,而“任务”和“任务评论”关联,“任务评论”和“评论人”关联。在这些关联对象间导航应该是很自然和简单的事情:

  1. // 开始遍历用户的所有任务// $users->tasks 是包含多个 Task 对象的集合   
  2. foreach ($user->tasks as $task){   
  3.   
  4.     echo "任务主题:{$task->subject}\n";     // 遍历任务的所有评论   
  5.   
  6.     // $task->comments 是包含多个 Comment 对象的集合   
  7.     foreach ($task->comments as $comment)    {   
  8.   
  9.         echo "评论人:{$comment->author->username}\n";       
  10.     }   
  11. }  


上述代码中,从 user 对象的 tasks 属性可以访问到聚合的 task 对象。而从 task 对象的 comments 属性又可以访问到任务的所有评论,再通过 $comment->author->username 访问评论人的用户名。

作为一个出色的 ORM,不但要能够支持多种关联关系,以及对象间的泛化和依赖,还应该让开发者可以在对象关系网间自由的导航。正是这种顺畅的对象导航,让领域驱动开发变得更加简单。开发者不需要操心如何维护对象间的关系,要做的仅仅是将对象间的关系定义出来。只要定义好关系,我们就可以通过直观的代码在对象间进行游历。

DoYouHaoBaby 支持四种关联关系,分别是“一对一”、“一对多”、“多对多”和“从属”关联关系。为了便于描述,每一个关联关系的两端分别称为“来源对象”和“目标对象”,如下图:


如图:


这四种关系在关系式数据库中的表示方式分别是:

关联关系 来源对象的外键字段  目标对象的外键字段
一对一 无 来源对象的主键
一对多 无 来源对象的主键
多对多 无 无
从属 目标对象的主键 无


在上述四种关联关系中,“一对一”、“一对多”和“从属”实际上是相反的关系。而“多对多”关联关系必须依赖一个单独的数据表来存储,这个表称为“中间表”。关于“多对多”的问题,我们稍后再来阐述。


创建数据表和模型

根据“用户”和“任务”间的关联关系,我们需要为 task 模型的数据表添加一个 user_id 字段,用于保存“用户”模型的主键值。最终两个数据表如下图:
如图:


  1. CREATE TABLE IF NOT EXISTS `wdi_task` (   
  2.   `task_id` int(11) NOT NULL auto_increment,   
  3.   `task_subject` varchar(200) NOT NULL,   
  4.   `task_description` text,   
  5.   `create_dateline` int(11) NOT NULL,   
  6.   `update_dateline` int(11) NOT NULL,   
  7.   `task_is_completed` tinyint(1) NOT NULL default '0',   
  8.   `user_id` int(11) NOT NULL,   
  9.   PRIMARY KEY  (`task_id`),   
  10.   KEY `user_id` (`user_id`)   
  11. ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=16 ;   
  12.   
  13. CREATE TABLE IF NOT EXISTS `wdi_user` (   
  14.   `user_id` int(11) NOT NULL auto_increment,   
  15.   `user_name` varchar(20) character set latin1 NOT NULL,   
  16.   `user_password` varchar(80) character set latin1 NOT NULL,   
  17.   `user_status` tinyint(1) NOT NULL default '1',   
  18.   PRIMARY KEY  (`user_id`),   
  19.   UNIQUE KEY `user_name` (`user_name`)   
  20. ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;  

在 phpMyAdmin 或者其他工具在数据库中执行上述 DDL 语句创建出需要的数据表后,我们就可以来创建模型了。

如图:

然后我们在Lib/Model 下面创建分别创建好 Task 和 User 模型,可以手工创建,也可以使用系统内置的图像化界面工具创建,我们将在后面不断地完善这些模型。

如图:

关于自动填充时间

关于“自动填充时间”说明,框架规定了3个自动填充时间,当数据库字段被设定为当前的字段的时候,系统会自动调用当前的时间戳进行自动填充。

字段为dateline表示任何时候用当前Linux时间戳进行填充。
字段为create_dateline表示创建对象的时候使用当前Linux时。
字段为update_dateline表示更新对象的时候使用当前Linux时。