石头杨 2016-11-28 20:31:29 11433次浏览 8条评论 18 10 0

学习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进行重构。道路阻且长,加油!

觉得很赞
  • 评论于 2016-12-01 15:11 举报

    楼主写的不错,希望分享更多经验

  • 评论于 2016-12-02 01:05 举报

    写的不错!我也是刚刚学习,交流下经验。对于model层,我一般都是放在common中,但是为了防止混淆,我会建立表单模型,譬如:Post.php,我会让它对应一个PostForm.php(继承自Model),让它处理业务逻辑,Post.php仅仅处理简单的数据操作或者完全就是一个数据模型。如果,按照楼主的方法,虽然前后端代码分离的更加清晰,但是我想这样做代码是不是会有很多重复了。。毕竟,模型层的操作会有很多一样的。

    1 条回复
    评论于 2016-12-02 10:07 回复

    嗯嗯,学习了,这个方法不错。关于前后端分别建一个 Model,继承 common 中的 model,也只是针对一些复杂的,需要较多业务操作的,简单的直接 common 中一个model 就行了。一切根据需求来就行了,不导致过度设计。

  • 评论于 2016-12-02 13:26 举报

    相当不错。我的也是都放在 common/models 只有特殊的才放在 各自 frontendbackend 文件夹下。都没有考虑过继承。

  • 评论于 2016-12-05 10:09 举报

    总结的不错,学习了

  • 评论于 2016-12-06 16:44 举报

    相当不错;特别是对于模型处理;还有就是处理业务逻辑service与java的思想差不多

  • 评论于 2016-12-12 14:17 举报

    AR模型虽然效率低一些,但是有更多的高级功能。比如事件,行为,事务等等。拿写操作来说,AR模型中提供beforeSave, afterSave这这两个方法,这两个方法会触发相应的事件,你可以在这些事件上绑定一些事件处理器完成对写流程的控制或者完成额外的一些操作,这是Yii2操作数据库最为高级的方式,也是最精华的优点之一。个人以为,牺牲点运行性能,增加开发和理解上的便利性是划算的。

    2 条回复
    评论于 2016-12-12 16:38 回复

    不好意思,是我没有表述清楚。我说的是代码层面的读写分离。并不是全用 SQL 语句。

    数据库操作就涉及增、删、改、查,其中增、删、改都是会对数据库的内容的状态造成改变,而查操作是不会对数据产生修改的。

    针对这个特性,我们可以在代码层面做出读(即查)写(即增、删、改)分离。便于针对不同的优化。

    我提出的是,写操作使用 AR 模型,读操作则直接用 SQL 语句。

    原因有这么几点:

    • 一般的应用中80%是读,20%是写,或者更高。AR 效率低,用来做读性能消耗太大。
    • 使用 AR 做读的话,全部采用面向对象的方式,一些复杂的查询不宜实现。
    • 其次使用 AR 做读,你并不了解最终生成的 SQL 是什么样的。检测到一些SQL语句执行过慢时,AR 中不好优化。

    其实究根结底,最终还是为了性能和维护。

    评论于 2016-12-20 09:26 回复

    我认为你的观点——使用AR模型做写操作,而用SQL做读操作非常有道理。Yii的读操作也是分为几个层次,原生态上SQL,DAO和AR模型。DAO的方式读,也提供了Query和BuildQuery帮你生成复杂的SQL语句,但是执行的方式几乎还是和PDO一样,所以效率高的同时,方便性也得到保障。

    觉得很赞
  • 评论于 2017-09-17 23:15 举报

    忽然看见这篇文章很不错,顶给那些刚接触yii的小伙伴们

  • 评论于 2022-01-07 15:57 举报

    写得不错,简单并容易理解

您需要登录后才可以评论。登录 | 立即注册