此文章仅是Laravist群中Abraham同学在日常聊天对PHP、对Laravel、对项目的一些杂谈的简单整理.
Abraham,给人感觉是一位在PHP语言方面理解比较深的人
laravel以及MVC
1.个人粗浅的理解,所谓业务逻辑就是验证表单,发短信,发邮件,查询转成数据库sql等等,这些除了数据库查询之外常用的东西,也包括构造页面的标题了,或者 ajax 接口生成 json 结果这些琐碎的事。
2 这些东西不该被写在控制器中的原因是根据SOLID原则,类的职责必须是单一的,不该让一个类知道太多的事,也有人管这叫“关注分离”(seprate concerned),当然这是设计模式的思想,落实到实践中的好处有很多。
据个栗子,例如你的应用有照片、文章、笑话等等很多栏目,用户可以在每个栏目下发布对应的内容,你想在它们发送完内容后自动给他们发送一封 email。
如果写在控制器中, 一是违背 dry 原则, 每个控制器中都要有重复的代码, 十分不利于维护,日后你想更换 email 的发送服务,则要修改每个控制器中的 email 发送代码;二 是 email 发送类和控制器类是紧耦合的, 你无法独立去测试其中任何一个部分,你想测试 email 是否跑通就要去执行控制器类, 想测试控制器是否正常工作则必须得有 email 类, 如果项目很大,两个部分是不同的人完成的,工作进度不一样,这样会极大影响开发效率。 控制器依赖 email, 正确的做法是把 email 接口独立出来(先写接口),再去用一个发送类实现他,然后把接口注入到控制器,这样控制器无需关心email 类的细节,只知道那个接口能发送 email 就足够。
在Laravel中,对于这些发邮件之类的可以新建个 service 、或者是 你的 app 名之类的目录, 或者是 repository 目录。 控制器需要依赖他的时候用 laravel 的服务容器在参数中注入他。
正常情况下,控制器代码行数太多,比如多于 10 行。 或者是控制器中有 if 之类的判断,都是不合格的、 要让控制器傻到,接收请求,调用对应组件赋值给变量,把变量传进view 就好。
</blockquote>
2.理清了路由模型绑定的逻辑,非常智能。先以数组注册所有绑定, 键为路由变量名,值是闭包。调用控制器之前,判断路由是不是命中了模型绑定,如果命中,执行闭包, 注意这里, 返回的类型是什么, 调用控制器的时候,就反射到哪个参数上。
3.资料库不是必须得加, 小的控制器能直接在里查询数据库就挺好。 以下几种情况,可以考虑加 Repository。
1) 是你要测试控制器的方法,让 orm 和控制器隔离写测试方便
2) 隔离方法之间的责任,降低复杂度,方便维护
我是第二点, 因为要加缓存, 如果把读缓存,判断缓存生效之类的动作写在控制器方法里,对一个只是用来转发的控制器方法来说,他知道太多内容了。
但是中间加资料库, 就可以在资料库中处理这些细节。控制器和资料库之间,都知道资料库实现了特定的接口就行。
不管系统用不用缓存,资料库只要实现对应的接口,控制器在任何时候都不需要修改、 如果要维护和数据有关的东西, 也知道只去找资料库、
先理解“数据流”该怎么处理,就可以更好地理解MVC。正常的数据流应该是这样的M--->C---->V。M是数据提供者,所以业务逻辑应该在M里。C只是分发数据流,以只有一个作用,解耦M和V,让数据和视图分离。
一个功能点一个目录,里面存放和他有关的一切信息,包括 Eloquent 类。
缓存简介
L1是页面级缓存,L2是APC缓存(php5.3以后没有APC了,用鸟哥的yac吧),L3是Redis缓存。缓存全部没命中才会到数据库的,数据库根本就没几个读写。缓存也不敢按时间或者LRU淘汰的,是另外有php命令行的进程推送缓存更新。
opcode缓存自然也是有的,连自动检查php文件修改更新opcode都要关掉,只能通过另外一个进程手动更新opcode。
应该有参数可以控制模板编译缓存(这句话整理者实在不懂)
至于文件缓存不好,试试文件系统下分256*256级目录,里面有好几万个文件的时候,性能有多糟糕。直接上redis吧,虽然有点网络开销
正式项目应该都会有代码发布系统吧,不会直接ftp或者scp传文件吧在发布系统里,统一控制重新编译就可以。关掉各种编译文件的更新检查。
git commit hook + phing
上传图片
图片上传过来,强制处理一次,用ImageMagick处理,所谓的原图也是压缩成大小相等的图片,删掉原图,exif信息存数据库,这样才安全
代码图片事例 --- (整理者:我的注释跟他比起来就是渣)
图片重力旋转
图片的 Exif 中有个Orientation 字段,用来存放照片方向。手机拍照有重力感应,所以照片都带有这个信息,手机的图库会正确识别显示拍照方向,显示出来。所以你要么在PHP识别旋转存储并且去掉exif,要么原样不动保留exif留给前端js显示的时候根据exif信息旋转。
UPYUN:
过年分享的小插件和开源项目及建议
https://github.com/hirak/prestissimo
composer 的并行下载插件,原理是先并行下载,再从缓存里恢复。
极大提升 update 速度。
官方提供的 laravel/laravel 只是基础,你可以按需自由调整。
比如:https://github.com/laravel/spark
这是 laravel 作者写的项目,虽然没 release,但大家尽量学习下。
通常大家划分目录按以下几种形式(可以同时采用多种)。
1,体现在前台的功能模块(比如邮件,授权,多步认证)
2,体现在编程中的基础通能(文件处理,队列操作之类的)
3,设计模式命名目录,一眼就知道目录作用
这如果展开了说,那就要说的太多了。总之目标就是,保证同一个目录,只放同一类东西。 出了一个问题,知道去哪个目录找文件。提高维护的方便程度。## Mysql 数据表MyISAM还是innodb
从长远角度看,改成innodb后面的可维护性将大大提高
1、MYISAM表锁,性能影响比较大,访问量大的时候尤其明显。
2、MYISAM很容易crash,修复后可能导致数据丢失。
3、MYISAM只能将索引load到内存,不像innodb,innodb索引和数据都可以load到内存。
4、CDB这边对innodb做了专门的优化,innodb比MYISAM更适合在CDB上跑。
实际项目的某些功能看法
1.耦合处理
比如你要给全站的文章进行网址唯一化,根据标题去自动生成拼音网址,存在 slug 列中。 此时垃圾的写法就是在控制器里每个涉及到的方法(增加、修改)都把标题生成拼音,一起存进去,这样当控制器中存在大量其它代码时(缓存、验证等等),a 是会变得臃肿难以维护, b 是控制器类中的修改、增加方法耦合太严重,要改得一起改。 这时候最好的办法是把 slug 过程独立出来。 如果在 serviceprovider 中 listen eloquent.saving* ,用独立的类处理,这样日后你修改 slug 方式,甚至不用改动控制器里的代码。
2.功能是否写在控制器</h4>
你的会员注册、 订单确认、 都要发送验证邮件时,写在控制器里是灾难的。详情和上面差不多。
php 框架里的事件机制基本都是四人帮“观察者模式”的简单实现,建议直接去读理论原文。 看点设计模式对写出好代码很重要,laravel 框架本身应用了 5 - 10 种设计模式,对于理解作者的设计也很重要。
可维护性强。 你可以任何更换 slug 类,而完全不用碰 控制器。也不用非得判断, 你可以在 provider 里 listen 一个自定义事件,注册和订单时 fire , 名字可以随便起。 eloquent 只是默认提供的。
3.在线人数统计
如果你不想用 redis,那继续用 mysql session,按时间查找按用户id去重count一下也行。考虑到性能,可以不对时间加索引,配置个crontab每分钟执行一次,把去重count的结果缓存到某个表的某个字段里。如果用redis,可以找一个单独的 redis 库,访问最频繁的几个页面加入写在线人数的代码,key为 用户uId,value 随便写或者写一些其他需要的信息,ttl 设置成 60 秒。用户过来,通过 pipline 或者 multi 写入 key - value 和 expire。获取在线人数就是 dbsize 这个库,如果访问频率非常高的页面获取在线人数,也可以配置 crontab 把 dbsize 的结果缓存。
Laravel的Eloquent和Facade(整理者水平不够看源代码萌萌的)eloquent 的默认触发事件是基于 laravel 的事件机制,并不是只有使用数据库才可以用事件。 你可以在程序任何地方触发时间,并且用对应的监听器进行处理。 你可以读一下 eloquent 的基类 model.php, 里面的 saving 其实就是 fire 这个函数的封装。 讨论是很难懂的,最好直接看源码.
Facade 是一个快速调用对象的方法,省掉了你实例化的过程(底层使用的是 loc 自动邦你 new xxx),框架本身需要更好的测试(不必测试 facade 调用是否成功),所以都是原生写法。
但 facade 这个组件本身也经过成熟测试,所以你可以在客户端代码尽情使用。
为什么laravel中有的函数根本显示不出来,提示不了,但是还是可以使用呢!比如latest(),无论是使用静态方法,还是使用对象调用,phpstorm都不能提示有这个方法,可是竟然能用
阅读面向对象
Tip:Eloquent注意点
$redis = \Redis::connection();</p><p>$catelist = $redis->get($foo)
先写 sql 再写 Eloquent,防止 Eloquent 用着爽,数据库遭殃。。。
你的取一条随机记录应该是这样大概
select user_id from table where id >= floor(rand() * max(id))
插播一句想用好 Eloquent 必须得知道方法链中的每个方法的返回值类型是什么。 不然第一个方法 返回数千个记录, 紧接着又进行集合操作。 我觉得运气不好的话,php 甚至会内存超限。
<h2>针对Laravel某些功能不如意</h2>
框架是基于接口的,如果某个功能框架默认的源码不能实现。 找到相应的实现,可以整个重写,也可以继承他。然后在配置文件里把 provider 替换掉
Guard 这个类是最容易替换的。这算是一个类似控制器的东西,里面注入了各种依赖。 你简单的继承它,把对应的方法覆写就行。看了下 config/app.php ,貌似没有 Guard , 如果想改 auth 部分, 看来只能重写 AuthServiceProvider 了。
这里面有个 registerAuthenticator() 方法, 在这个方法里做手脚即可,把里面的 UserServiceProvider 和 Guard 任意替换成你自己的实现,希望我说明白了。
主要是配置文件里只暴露一个 AuthServiceProvider ,如果要动 guard 就得覆写我上面说的方法。
如果想取个 salt 什么的,重写默认的 EloquentUserProvider,然后把 config/auth.php 里的 Eloquent 改成自己的。
日常学习进阶
静态工场, 单例模式, 都要求构造方法私有。没事多看看设计模式吧,对写程序帮助真的挺大的。我的建议是把 martin fowlor 的博客的所有文章都读了。他是 java 大牛,属于设计模式的教父级人物,言简意赅。
原文由 郑方方 创作 来自:http://www.jianshu.com/p/fd8b72b3d7d7