良好的架构(schema)设计指南

架构 模式 schema 设计 指南 relation

几乎所有的 Yii 的应用程序都是建立在数据库之上,虽然 Yii 可以非常灵活的操作数据库,但有些时候一些设计的选择可以使它更便于使用.

首先 Yii 应用程序广泛使用了 CActiveRecord, 设计的考虑主要围绕优化使用而不是组成复杂的 SQL 语句. 实际上大多的设计是使用友好的 SQL 模式来解决实践中的问题.

最常用的方式是创建易于被人阅读和理解的代码,例如使用命名来传达意思,但是这很难做到.

特别当你在论坛或 '#yii' 频道寻求帮助时,使用奇怪的名字意味着你会有一堆麻烦:解释代码是干什么的。你的问题有时就不会得到有效解决.

一堆一致性问题.

然而这些只是准测而不是规则,你的代码不遵循这些准则也照样会工作,但是如果你遵循这些准则你的代码将更加简单.

使用单数而不是使用复数来命名表名

我们认为 SQL 表 包含很多记录,一个模型(model) 只是其中的一个,在任何地方使用 $model = new Comments() 来表示再次定义关系时感觉非常奇怪.

命名您的表名为 comment 而不是 comments,invoice 而不是 invoices 等等。对于的模型(model)类名也是一样(Comment,Invoice等等).

如果你不可以改变数据库的模式,但至少在适当的情况下你可以改变 Yii 模型类(model class)的名称,在这种不匹配的情况下你应该在代码中添加一个额外的注释 //COMMENT 来提醒用户.

不要在字段名前面加上表名

下面的做法是在传统 SQL 设计模式上非常常见的,但是它在于 ActiveRecord 结合时时冗长复杂的。在 category 表中:

[sql]
-- 不可取                           -- 可取
create table category (            create table category(
    category_id    INTEGER ...,        id    INTEGER ...,
    category_name  VARCHAR ...,        name  VARCHAR ...,
    category_value INTEGER             value INTEGER
);                                 );

// 不友好的                          // 友好的
$model->category_id                $model->id
$model->category_name              $model->name
$model->category_value             $model->value

虽然使用这种 long 方式生称的 SQL 语句容易理解一些,但是在 ActiveRecord 中使用就不太方便了.

不要在模型类(model class)的类名中出现表名的前缀

Yii 支持表前缀,表前缀被用在 所有 应用放到共享的主机并且共享一个数据库的环境中. 你的博客表前缀为 blog_,计时应用的表前缀为 time_等等. 它们存在于同一个数据库中而互相不起冲突.

前缀 tbl_ 在很多教程和例子中经常看到。

但是类不需要包含这些前缀,因为没有必要避免冲突:你的博客应用和你的计时应用时两个不同的应用.

[php]
class TblComment extends CActiveRecord {       // 不可取
class Comment extends CActiveRecord {          // 可取

在代码中看到无处不见的前缀使人们非常反感.

确保每个表的主键为 id

大多数表都有一个唯一主机,命名主键为 id(而不是 commentidpostid) 会使你的工作更加顺手.

虽然无论你是否访问数据库 Yii 都可以通过读取数据结构来得到它的主键,但是系统的其他部分可能不会这样做,并且它们依赖的主键为 id.

例子:CArrayDataProvider 默认的主键为 id, 虽然你可以重写它的属性 'keyField', 但是把这放到第一位是很不方便的.

显而易见,当使用复合主键时这将不能工作,但是这毕竟是少数.

避免特殊意义的主键名

一个经典的设计错误是创建的表的主键有实际意义. 在下面的列子中 用户(user)表使用的用户名(name)作为主键:

[sql]
-- 不要这么做!
CREATE TABLE user (
    name   VARCHAR(16) PRIMARY KEY,  -- bad idea
    email  VARCHAR...
    ...
)

这样做引发了两个问题: This presents two difficulties:

  1. 当在其他地方 引用 时,它的效率大大降低,因为它包含16个字符而不是4个字节的整数.当大量的 引用 时性能大大降低。

  2. 当系统中存在外键约束时用户想 修改用户名 是很困难的:表中的字段和引用都必须在同一时间改变,这样做的代价时昂贵的。

最好的解决办法时创建一个整型字段作为主键,并设置 name 字段为 unique:

[sql]
-- much better
CREATE TABLE user (
   id     INTEGER PRIMARY KEY AUTO_INCREMENT,
   name   VARCHAR(32) NOT NULL UNIQUE,
   email  ...
   ...
);

这种方式在用户更新用户名时只修改了一条记录.

确认在数据库结构中定义的外键关系

大多数的数据支持定义表之间的关系,一个表中的字段指向另一个表的主键。这些外键帮助保证了数据的完整性当有记录执行某条记录时不允许你删除它.

MySQL的InnoDB 强制使用外键约束,而 MyISAM 允许你 定义 它们,但不会 强制 使用. Yii 知道怎么处理这些关系,并且可以使用 Gii或Giix 自动生成关系.

抛开 Yii ,外键是维护数据库完整性的重要部分,在网络上有许多学习它的教程.

使用 "id" 作为外键字段的结尾

通过以上的说明,如果你有一个字段指向一个用户(user),命名这个字段为 userid 而不是 user. 原因是你肯定要为表中的每一个外键定义一个 关系.

在 Yii、类变量、数据字段、虚拟属性、关系共享一个命名空间(namespace), 所以不可能通过 $model->user 的方式来 同时 访问表的外键 关系.

通过外键 userid, $model->user 形式的 BELONGS_TO 关系可以轻松自然的使用:

[php]
class Post extends CActiveRecord {

   public function relations()
   {
       return array(
          'user' => array(self::BELONGS_TO, 'User', 'userid')
       );
   }

注意: 有很多人喜欢用 Id_id 来代替 id.这只是个人爱好问题,意义是一样的.

在关系的名称(name)上体现它们的单数或复数的特性

继续我们的主题一致性和代码易于阅读,关系应当在名称上体现它们代表的为单数还是复数.

  • HAS_ONE - 返回一个模型(model): 单数
  • BELONGS_TO - 返回一个模型(model): 单数
  • HAS_MANY - 返回一个由多个模型(model)组成的数组: 复数
  • MANY_MANY - 返回一个由多个模型(model)组成的数组: 复数

注意: 对于返回一个数组的关系,它们可能只包含一个模型,但事实上它们是通过复数名称方式认证来的 数组

你只需要通过看关系的名称就会知道它返回的是一个数组还是一个模型:

$model->post
$model->comments
$model->author
$model->members

如果你必须通过查看代码才会知道,那这将会让你的代码更加难以阅读和维护.

评论

搜索