ThinkPHP中ORM模型

任何一个Web框架中,数据读写都占据着重要的地位。对大多数中小型项目而言,业务并不是特别复杂,中心主要放在数据库的操作上。在ThinkPHP5中对数据库的操作是比较巧妙的,ThinkPHP5默认支持四种类型的数据库,在thinkphp\library\think\db\connector中,有四个数据库的连接器:think/db/connector/Pgsql.php,think/db/connector/Mysql.php,think/db/connector/pgsql.php,think/db/connector/Sqlite.php

在ThinkPHP5中,当我们需要使用数据库时,需要在database.php中作相关的设置,ThinkPHP5中操作数据库主要分为三种:原生SQL语句,构造器,ORM模型。其中前两种方式是第三种方式的基石。我们以数据库的查询为例:

第一种方式:原生sql语句:

TP5中为我们提供了一个Db类用于数据库的相关操作,Db中提供了一个query方法,query方法第一个参数需要一个SQL语句,其中的参数部分使用“?”占位符代替,第二个参数是一个参数的数组,TP5中会自动把第二个参数数组中的值依次替换掉第一个参数中的占位符:

$result=Db::query('select * from table where id=?',[$id]);

在说到接下来的两种访问方法前,我们来看一下TP5是如何设计数据库访问层的,这些会帮助到我们理解后面的两种方式

如果我们不使用任何框架而是使用原生的PHP,我们连接数据库通常是在代码中使用PDO对象或者是MySQL的连接类,连接上之后我们就可以使用一些原生的SQL语句对数据库进行访问,访问模式如下:


ThinkPHP中ORM模型_第1张图片
image.png

但是在一个真正的面向对象的代码中,他并不是通过一系列代码去实现我们的过程,而是通过设计多个类,并明确每个类的职责,然后通过这些类与类或者对象与对象之间产生相互的作用从而完成一系列的功能。重点:TP5中访问数据库并不是直接通过代码访问数据库,而是通过TP5提供的数据库访问中间层,也就是我们俗称的DAL(DAL是数据访问层的英文缩写,即为数据访问层(Data Access Layer)。其功能主要是负责数据库的访问。简单地说就是实现对数据表的Select(查询)、Insert(插入)、Update(更新)、Delete(删除)等操作。)来访问,这样的好处是可以简化我们的SQL代码的编写,简洁地操作数据库。而且我们不需要关心具体的实现,只要使用中间层封装好的Db类,就可以访问不同的数据库,做到跨数据库的一致性。 TP5中数据库访问层使用的是这样的一个架构:

ThinkPHP中ORM模型_第2张图片
数据访问层.png

Db:数据库操作的入口对象,操作CURD都是通过BD::Query操作的,兼顾根据配置文件连接数据库的作用。其本质是工厂模式的操作思想,我们操作数据库时,使用的都是Db操作类,Db类内部会根据我们的配置文件来选择不同的驱动,驱动决定了Collection的类型。Db通过实例化Collection对象来连接数据库,我们使用Db类时,不需要我们关心我们连接数据库的细节,只要在配置中指定我们需要使用数据库的类型,数据库的中间层就能帮我们找到相应的数据库的连接的驱动,从而实现对不同数据库的支持。大大提高了我们的开发效率。

Collection:这是一个连接器对象,是通过PDO链接的,Db靠着Collection对象连接器连接数据库。它并不是真正的连接数据库,而是处于待命地连接数据库状态,要执行SQL时才真正连接并执行SQL语句,这样的一种惰性节约了数据库的资源。所以并不是在实例化的时候就连接数据库,而是在有实际的数据操作的时候才会去连接数据库。

Query:查询构造器。是对CURD操作进行封装,我们可以优雅编写SQL语句同时支持链式操作,Query隐藏了不同数据库操作的差异细节,不同数据库的查询在原生SQL上是有一定差异的,为了隐藏这些差异性,让开发者只需要使用统一的标准操作数据库,提供了标准的API供开发者使用,但是内部还是需要处理差异性的。(隐藏了细节和差异,面向对象封装的体现)

Builder:生成器,把Query的查询器封装的数据翻译成原生的SQL语句后,将其传递给Collection,再由连接器连接数据库执行SQL语句。如果你使用原生操作,直接使用collection直接执行原生操作。但如果使用Query查询器时,通过builder转化成原生SQL,之后再返回到Collection中执行原生的SQL语句。简单地说,Builder的作用就是处理差异,TP5通过使用不同的Builder处理器解析相同的标准的Query查询器从而适配不同的数据库,也即是我们无论使用哪一种查询方式,最终都是生成原生的SQL语句到数据库中进行查询。

驱动Driver:提供了几个不同的类,每一个类负责一种数据库的连接,利用工厂模式的思想,Db类会根据配置文件信息选择对应的驱动从而决定Collection的连接类型,我们在使用Db类时不用关心具体的连接过程,不需要关心细节。

接下来,我们来看一下第二种操作数据库的方式:Query构造器。Query构造器与与原生的SQL相比,不仅代码更为简洁,且查询构造器封装了不同数据库的操作,提供了统一操作数据库的语法,不需要关心不同数据库在原生SQL上的差异性。示例:

$result=Db::table('test')//指定操作的表 ->where('id','=',$id)//查询条件 ->select();//find()方法是找到第一个,select()方法是找到所有满足查询条件的记录

(Tip:这里的table()以及where()我们称之为链式方法,除此之外链式方法还有whereOr,group等方法,链式方法不会执行真正的SQL语句,每个链式方法返回的都是一个Query对象,而select()/find()/update()/insert()/delete()称之为执行方法。对Query对象使用执行方法,才会生成SQL语句。链式方法必须要在执行方法之前调用。不同的链式方法之间没有一个固定的顺序,但是相同的链式方法的位置关系是会对最终的查询结果产生影响的。此外,我们使用链式方法时,在我们使用执行方法后,之前链式方法的状态就会被清除,但是在我们使用执行方法前,这些状态是会被保留的,例如:

Db::table();Db::where();是等同于Db::table()->where())

第三种操作方式方法是通过ORM方式进行操作,ORM的全称是Object Relation Mapping 对象关系映射,ORM的实际上是使用面向对象的思维方式来思考我们的数据表。在传统的SQL中我们将数据表视做一个二维关系的数据结构;在ORM方式中,我们将每一张表视作一个对象,我们操作的不再是一张表,而是一个对象,我们并不需要关心对象获取数据的细节,我们只需要通过对象来获取数据。表与表之间的关系也不再是数据结构与数据结构之间的外键关系,而是对象与对象之间的作用关系。

在TP5中,ORM具体实现机制是模型,TP5中的模型不仅仅用于数据库的操作,还可以包含相关的业务逻辑。TP5中的模型不仅仅是一个对象,它是一个业务的集合,一个表对应一个对象,而模型可以对应多个表或多个对象的。模型并不是根据表而是根据业务逻辑划分的。模型主要的作用是处理一些比较复杂的业务逻辑。业务逻辑非常简单时,也会出现一个模型对应一个表或一个对象,而业务逻辑比较复杂时,模型与表或对象之间就不是意义对应的关系了。数据库的查询是数据库的查询,而模型是模型,模型关心的是业务逻辑而不是数据查询。模型不是和数据库表一一对应的,它可能会横跨多个表。对于一些比较复杂的业务逻辑,我们是需要分层设计的,模型并不是只有Model一层,置于具体的划分视情况而定,TP5中主要分为Model,Service,Logic三层,Service通常是很多个Model层的API的集合,而Logic又是比Service更高一层的逻辑层。

模型和前面的数据库访问层Db有什么关系,又有什么差异呢?其实当我们只需要对数据库做基本的CURD操作时,查询构造器其实往往是足够的,但是通常我们会有更为复杂的业务逻辑,最常见的是我们QQ中的多设备登录以及异地登录等。那这种问题显然仅仅靠Db类时不足够的,我们的解决方式是使用ORM的模型对象来承载我们的业务逻辑。Db相比较模型最大的劣势是Db通常不能很好的承载我们的业务逻辑。TP5中的Model模型和Db其实是不分离的,Db是Model的基础,Model的本质还是Db在驱动的,只是在TP5即将模型的概念抽离了出来并进行了封装,我们在操作数据库就是在操作一个Model实体对象,具体的连接和实现并不需要我们关心,它会自动地调用Db类进行操作。Model不是数据库访问层,而是建立在数据库访问层上的业务逻辑层。

定义Model时,需要我们先继承think\Model,继承Model后,默认数据表名和模型名称是一一对应的(当我们需要将其对应到另一个表上时,只需要将该模型的protected id),而不需要在其中定义了上述的查询方法后,再在外部进行调用。(其实无论我们是否继承了think/Model,我们自己编写的逻辑层也可以是一个模型,但是相对而言会更加麻烦)使用模型的查询方式时,会返回一个使对象而不是前面两种方式返回的是一个数组。返回对象时,我们操作会更加的方便,其中的方法和属性会帮助我们进一步地处理我们的查询结果。(但是在这种形式返回的ControllerType是一个HTML格式,如果要改变默认的返回格式,我们可以在config.php中将default_return_type设置为json)。

我们在调用数据库的操作方法时,有两种操作方式,一种是TP5推荐我们使用的静态查询方式,另一种是实例化对象使用,但是相对而言,静态方法调用显得更加简洁,而且就模型的本质意义而言,用静态调用更加符合逻辑,因为模型对应的是一张表,而一个对象对应的应该是表下的一条记录。

模型中除了get查询外,还有select,all,find几种查询方式,其中get和find返回的是一个模型对象(也就是一条记录),而select和all返回的是一组模型对象(也就是一组记录)其中get和all是模型特有的,而find和select是属于Db的方法。我们前面说到,模型是建立在Db之上的,因此不难想到,我们在模型中也可以使用select和find方法,但是我们在Db中是不可以使用get和all方法的。

其实,模型的性能是比不上原生的SQL语句的,因为毕竟会造成一定的性能损耗,但是,通常情况下我们还是会选择使用模型。在性能差距可以接受的的差别范围内,模型的使用往往可以提升我们代码的可读性,而可读性其实往往是我们代码的一个重要部分。

你可能感兴趣的:(ThinkPHP中ORM模型)