数据库是关系型的,PHP5和symfony是面向对象的,为了更有效地使用面向对象方式访问关系的数据库系统,将面向对象的逻辑转换到关系型逻辑是必须的。
ORM的最大优点是重用,数据对象方法可以在应用的不同部分调用,甚至不同的应用中被调用。ORM层同时也封装了数据逻辑——比如,计算论坛用户的等级需要用户发布了多少信息以及发布信息的受欢迎度等等,当要在页面显示用户等级的时候只需调用一个数据模型方法即可,所有复杂的逻辑业务都在方法内部实现。而且,你可以在必要的时候修改计算方式,这是只要修改数据模型方法即可,显示部分根本无需修改。
用对象替代记录,用类替代表还有另一个好处:对象提供的访问属性可以不与表字段对应。看下面的代码:
public function getName() { return $this->getFirstName.' '.$this->getLastName(); } |
所有重复数据存储方法和数据自身的业务逻辑都可以保存在对象中。看个购物车统计:
public function getTotal() { $total = 0; foreach ($this->getItems() as $item) { $total += $item->getPrice() * $item->getQuantity(); } return $total; } |
还有一个好处就是不同数据库使用的SQL语法可能会有区别,从一种数据库转换到另一种数据库将必须重写SQL查询语句(以前方式),采用抽象层与ORM将不涉及具体的SQL语句实现,只关注操作的业务本身。
Symfony使用Propel作为ORM,Propel使用Creole作为数据库抽象,他们都是第三方组件,都有Propel小组开发。
Symfony项目中所有应用共享一个模型。模型是独立于应用的,模型文件存储在项目根下的lib/model目录。
要创建数据对象模型,你不惜告诉symfony如何去影响,即需要给定一个大纲文件。在大纲文件中你定义表、字段、关系、默认值、字符集等信息。
Symfony的大纲文件采用YAML格式,必须保存在myproject/config/文件夹,名字一般为schema.yml。大纲文件也可是使用XML格式,要使用XML文件必须删除YAML文件。
schema.yml |
propel: post _attributes: { phpName: Post } id: title: varchar(255) author_id: body: longvarchar created_at: author: _attributes: { phpName: Author } id: name: varchar(255) password: varchar(255) fullname: varchar(255) |
Schema.xml |
<?xml version="1.0" encoding="UTF-8"?> <database name="propel" defaultIdMethod="native" noxsd="true"> <table name="posts" phpName="Post"> <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" /> <column name="title" type="varchar" size="255" required="true" /> <column name="author_id" type="integer" /> <foreign-key foreignTable="users"> <reference local="author_id" foreign="id"/> </foreign-key> <column name="body" type="longvarchar" /> <column name="created_at" type="timestamp" /> </table> <table name="author" phpName=" Author "> <column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" /> <column name="name" type="varchar" size="50" required="true" /> <column name="password" type="varchar" size="50" required="true" /> <column name="fullname" type="varchar" size="100" /> </table> |
数据库名称blog不会在schema.yml文件中显示,它定义在其他文件(database.yml和propel.ini),这有好处:通过简单的修改数据库链接即可满足不同目的的使用,比如开发过程中使用blog_dev数据库,测试阶段使用blog_test数据库,运行时候使用blog数据库等等。
Schema.yml威尔建中第一个键表示连接名。大纲中包含多个表,每个表包含一组列。在YAML中,键以冒号结束,结构以缩进反应(切记不要使用tab)。表可以包含特殊属性,比如phpName(生成后的类名),如果没有定义phpName,symfony使用camelCase方式定义之。
【cameCase约定删除单词中的下划线并将单词的首字母大写,比如:post表的类名为Post,blog_article的类名为BlogArticle】
表包含列信息,列值可通过三种不同方式定义:
l 如果没有定义任何信息,symfony将依据列名和空列约定猜测最佳属性。例如,id列将被定义为自动增长的整型,且设置为主键;post表中的author_id将理解为author表的外间;created_at自动设置为时间戳类型。
l 如果只定义了一个属性则为列的类型。可接受的类型:boolean, integer, float, date, varchar(size), longvarchar (在MySQL中转化为text)等等,如果varchar长度大于255,你应该使用longvarchar类型,longchar类型在MySQL中最大65K;时间与时间戳受限于Unix,不能保存早于
l 如果要定义其他属性(如:默认值、非空等),你需要使用键值对来些,后面会介绍。
列也可以有phpName属性,采用首字母大写方式(Id,Title,Content等),无需覆盖。
Schema用来建立ORM层的模型类。在命令行可以非常快速的生成:
symfony propel-build-model |
运行命令后将启动schema分析并在lib/model/om目录生成基本的数据模型类。你会发现,两个表在om目录下产生了四个文件,另外在model目录下还有额外的四个文件,这没有错。
为什么在两个目录保留两个版本的数据对象模型?
你可能需要在模型对象中添加自定义方法和属性,但在项目开发过程中,你可能也要调整表以及表结构。一旦修改了schema.yml文件,你需要使用上面的命令行语句重新生成对象模型类,如果你的自定义方法和属性写在了生成的类中,他们将被重新生成所删除!
基类存在lib/model/om目录,是有schema直接生成的。你不应该修改他们。另一方面,自定义类保存在了lib/model目录,他们继承自基类。当调用命令行语句重新生成数据对象模型的时候,他们不会被改动,所以应该将自定义方法和属性放这里。
Post和author是对象类,他们表示了数据库中的记录,提供了对记录及相关记录字段的访问,也就是说可以通过调用Post对象的方法返回一条Post记录的标题。
$post = new Post(); ... $title = $psot->getTitle(); |
PostPeer和AuthorPeer是peer类,类中包含了操作表的静态方法。他们提供了从表获取记录的方法,返回对象或者对象集合。
$posts = PostPeer::retrieveByPks(array(123, 124, 125)); // $posts是Post类的对象数组 |
首先让我们看看关系与面向对象的对应表
关系 |
面向对象 |
表 |
类 |
行、记录 |
对象 |
字段、列 |
属性 |
通过对象的setter与getter方法管理列值:
$post = new Post(); $post->setTitle('My first article'); $post->setBody('This is my very first article./n Hope you enjoy it!'); $title = $post->getTitle(); $body = $post->getContent(); |
一次设置多个字段需要使用fromArray()方法: