Yii项目开发总结

技巧库 · stoneyang · 于 6个月前 发布 · 1248 次阅读

学习Yii很久了,一直做的是小案例,自以为学的还不错。直到最近用Yii开发了一个非常简单的CMS,一路下来,磕磕绊绊,才知自己不足。加上最近正学习着偏架构方面的知识。特此总结一下。小白经验,大神轻拍,欢迎讨论。

本篇文章主要是将一些 MVC 代码组织的技巧,不会涉及详细编码。声明使用的模版是Yii高级模板。

痛苦的开发。

所有的php框架几乎都遵循MVC模式设计,网络各种教程、官方手册,也是一股脑的说着按着MVC组织代码。 先说说开发中我的悲惨历史。

大家都知道,在Yii中,一张表对应一个AR,再对应一个控制器。

在开发开始的时候确实是不错的。比如留言功能里面。我在common\models下生成了对应的AR类。然后在frontendbackend的控制器里导入了这个AR类,修改相应视图。然后根据需要对查询做了简单封装,即是在生成的AR类中写了一些方法,然后控制器调用。功能实现,代码逻辑也清晰。自觉棒棒哒。

然后就到痛苦的时候了。在设计文章和栏目的时候,我分了三张表:文章表,栏目表,文章内容表。在common\models生成三个AR类,后台、前台分别调用。由于创建文章时三张表都需要同时用到,以及插入、修改、删除的时候,需要用事务保证一致性。于是我只能就在文章的控制器里添加了一个又一个涉及数据库操作的方法。然后在前台显示的时候,又在对应的模型类里添加了一些方法,前台控制器又是一般修改。首页显示的控制器,映入了八个AR类。然后到最后,看着控制器里一个个AR查询的方法,AR类中又是一堆不同的查询方法,我彻底懵逼了。

好不容易实现完功能,自己都不敢看自己的代码了。

MVC中M的思考

开发完之后,痛定思痛。在大量阅读博客、书籍之下。获得以下经验,与君共享。

关于MVC中,首先声明的一点是设计要“胖Model痩Controller”,即Controller应该只有薄薄的一层。主要业务逻辑都在Model中。

视图层和控制层不用多谈,大家都知道。主要该谈谈的是模型层。

模型中前后台的组织

对于前后都要用到的模型,大家都会生成AR类到common\models,然后在frontendbackend的控制器里导入了这些AR类。但是,这不是一个好的习惯。前后台有着不同的应用逻辑。引用同一个AR类必然职责不清。正确的做法是在前后台的models文件夹里,新建不同的类,然后继承common\models中的AR类,做到模型分离。

举个例子。在common\models中有个Post.phpAR类,你应该在backend\models中新建一个Post.php然后继承common\models中的Post.php类。前台也是如此,做到引用分离。

业务逻辑层(service)

开发中必定涉及到多表关联。如果全部在控制器中引入模型类,写查询语句,代码组织就会非常糟糕,不利于维护管理,更正确的做法是。建立一个业务逻辑层(service)。

也就是做backendfrontend文件夹下建立一个services文件夹,保存我们的业务逻辑。比如我前面有三张表:文章表,栏目表,文章内容表,分别对应三个模型。当我需要保存一片文章时,同时涉及到三张表的操作。我就可以建立一个PostService.php,里面引入这三个AR类,然后建立一个SavePost()方法,在方法里同时对三张表做操作。这样,控制器里只需引入这个服务类,调用相应方法就行了。

读写分离。

实际的应用都是读多写少,因此,我们可以做到代码层面的读写分离。(详细的可以了解了解CQRS架构),方便分别优化。而且Yii中的AR虽然很方便,但是效率不高,太复杂的查询也是不好实现的。

这里Yii提供了一个简单高效的SQL查询。那就是DAO。 就是使用方式是这个的东西。

$post = Yii::$app->db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')
           ->bindValue(':id', $_GET['id'])
           ->bindValue(':status', 1)
           ->queryOne();

我们可以在services文件夹里建立只提供查询的文件,比如PostQuery.php文件提供对文章的查询

总结

第一次开发一个完整的东西,学习到了很多。最近打算对这个CMS进行重构。道路阻且长,加油!

本文由 stoneyang 创作,采用 知识共享署名 3.0 中国大陆许可协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。

本帖已被设为精华帖!
共收到 7 条回复
forecho#16个月前 0 个赞

首先感谢分享,总结的很不错,我相信这些过程都是程序开发的必经成长之路。

前面几个都很认同,并且 service 层在 GetYii 也有实践。

最后分享的一个「读写分离」没搞懂什么意思? 对于「读写分离」我所谓的理解,不应该是数据库读写分离吗?使用你这种方式就真的能实现吗?

其实用 service 层来解决问题的同事也会带来一些另外的问题,所以现在都在使用「微服务」和「领域驱动设计」。

forecho#26个月前 0 个赞

对于你说的「痛苦的开发」,其实还有一种解决方式,就是在 Model 层使用 afterSave 来关联操作,并且记得要启动事务。

可以看看这个 https://getyii.com/topic/358

stoneyang#36个月前 1 个赞

@forecho [[#2楼](#comment2)](#comment2) 谢谢指教,领域驱动设计现在正在关注。对于读写分离,有数据层方面的,有代码层方面的,详细的架构是CQRS。引用一段话

CQRS即Command Query Responsibility Seperation(命令查询职责分离),其设计思想来源于Mayer提出的CQS(Command Query Seperation)。这种命令与查询的分离方式,可以更好地控制请求者的操作。查询操作不会造成数据的修改,因而它属于一种幂等操作,可以反复地发起,而不用担心会对系统造成影响。基于这种特性,我们还可以为其提供缓存,从而改进查询的性能。命令操作则与之相反,它会直接影响系统信息的改变。查询操作与命令操作对事务的要求也不一样。由于查询操作不会改变系统状态,因而,不会产生最终的数据不一致。从请求响应的角度来看,查询操作常常需要同步请求,实时返回结果;命令操作则不然,因为我们并不期待命令操作必须返回结果,这就可以采用fire-and-forget方式,而这种方式正是运用异步操作的前提。此外,对于大多数软件系统而言,查询操作发起的频率通常要远远高于命令操作。如上种种,都是将命令与查询进行分离的根本原因。

Yii中AR是很方便,但是性能不高,直接用SQL查询,效率更高

4楼 已删除.
upliu#56个月前 1 个赞

@stoneyang #3楼 Yii中AR是很方便,但是性能不高,直接用SQL查询,效率更高 希望看到你对于这个观点亲自实践得出的数据。AR 会比直接写 SQL 多一些处理必然会影响到性能,但假设 AR 额外耗时 0.002s,直接SQL额外耗时 0.0015s ,然后你就得出结论 AR 性能不高,其实只是 0.0005s 的差异。

upliu#66个月前 0 个赞

另外前后台 Model 共用同一个 common/models/ 下面的模型另一种方式是使用不同 scenario

xzw2716#72个月前 0 个赞

最近也在纠结这个问题,今天看了楼主的文章,思路清晰了。谢谢。

8楼 已删除.
fecommerce#92个月前 1 个赞

@stoneyang

@forecho

我分享一下我的观点:

读写分离,yii2已经在架构层面解决了,我认为没有必要自己搞这些了。

2.用AR就可以了,损耗的那点性能,是php的,不是mysql的,无论你用那种方式,都不能提升mysql, 对于php损耗那点性能,基本可以忽略了,而且php在水平扩展非常容易。

3.对于服务层,我说一下我的理解

首先,MVC架构,在一些涉及到多表操作的复杂功能中力不从心,会造成controller很大,如果不写在controller中, 但是写到model又不合适,因为Amodel的代码不能写到Bmodel中去,这些需要加一个层,用来处理中间逻辑, 我感觉你说的服务层就是用来干这事,我认为这个层叫服务层不合适,在fecshop中我叫他block层(参考的magento的命名),block层 收集各个model的xx,进行处理,最终返回一个数组给controller。

对于服务层,我的观点是体现在多应用,譬如你的多个应用都需要用到一个底层逻辑,譬如你的手机端web,pc端web, 你的ios app 安卓app,等, 或者你是站群,有很多站都公用一套底层逻辑, 这个时候这套底层逻辑,称之为服务层更为妥当

以上是我个人的观点,只是说出来探讨理解。

添加回复 (需要登录)
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册