前言:
先交代下背景, 在一个项目中, 有一个数据表有水平分表的需求. 当时想找到一种方法, 把对数据库的操作, 写到一个模型里, 通过去换模型属性中的 table 来达到代码不变操作的数据表变化的效果.
我们都知道, 模型要想关联数据表的话, 有两中方式, 第一种就是将模型名和数据表一致. 这样模型就会默认关联到名字对应的数据表. 第二种就是定义模型的 protected $table 来指定表明. 我当时就想, 有没有什么方法, 能初始化模型对象的时候将 table 属性赋值呢, 这个值存在数据库里. 这样就可以动态的来控制这个模型关联的表名了.
模型初始化
基于以上的需求, 文档里的模型初始化引起了我的注意
tp5 文档
我感觉这是我要找的东西. 紧接着我着手开始测试
首次测试
我根据手册的写法, 在调用父类初始化后面, 写上对 table 的初始化, 那么现在我们来打印出来实例化的 order 模型
打印实例化对象
从图中大家可以发现, table 属性的确已经修改了. 然后我就没有再做更多的测试了, 因为我试过手动将 table 数据改为别的表名, 就可以修改模型所关联的数据表. 我想这个 table 属性已经有了肯定就没问题了.
直到我在这个模型里写了很多方法后, 我想去回来换个表名来试试写入数据. 爆炸的事情出现了.
- // 我使用包含 table 属性的对象去查数据库. 查询出来的结果, 居然任然是原来那个模型名对应的表
- $order = new \App\API\model\Order();
- return $order->select();
这就很爆炸了, 我已经写好的这么多代码难道都不能用了?
冷静下来之后, 我决定先试试, 手动在模型中该表 table 属性来试试.
直接修改模型的 table 属性
那么我们再来访问下呢?
image.PNG
报错, 报表不存在, 这是正确的, 因为我没有建立 order_1 这个表. 不过这也说明了, order 模型的确已经和 order_1 表关联起来了. 这样就让我摸不着头脑了. 以前学习面向对象那些理论又浮现在我们脑海里. 我梳理了下思路
order 模型中定义 table 属性, 其实是对父类 Model 中的 table 的重写. 并且 table 属性是一个 protected 的. 那么就是说, 只有在模型内部或则子类中可以使用和修改
在实例化模型的地方是控制器, 也就是类的外部, 理论上外部是对象只能读取和操作类中 public 的属性的
可是 initialize 又是在实例化模型自动触发的方法, 触发的地方又是在模型的内部.
但是我又是使用 $this 又是指代我控制器中的被实例化出来的模型对象. 那是不是还是不能访问被保护的属性呢?
好了瞎分析完了之后, 我决定还是去网上搜索下, 看看有没有人和我一样的应用场景. 后来我发现, 使用模型初始化的人, 似乎很少. 少数几篇博客讲解了下. 其中有个应用场景和我类似. 他在代码中是这样写的
- protected function initialize()
- {
- parent::initialize(); // TODO: Change the autogenerated stub
- $this->table('order_1');// 假装这里名字是从数据库里取得
- }
我通过 IDE 的智能感知, 进入套 table 方法中, 看了下注释
image.PNG
看样子是我需要的方法, 可这个方法不是模型基类里的啊, 是在 query 类当中的. 我有些摸不清楚头脑, 但不管怎样, 还是要试一试
为了让代码不报错, 我去增加了一个 order_1 表. 这一试, 嘿嘿, 搞定了!
image.PNG
打印出了我在新表中的一条数据. 哈哈, 看来这个思路是可行的
就在我认为这个思路是可行的时候, 我在执行我写好的一些模型方法时, 我发现了一个大坑!!
我就不详细说我是怎么发现的了. 直接看代码
模型里我还是这么写的
- protected function initialize()
- {
- parent::initialize(); // TODO: Change the autogenerated stub
- $this->table('order_1');// 假装这里名字是从数据库里取得
- }
- // 控制器里实例化模型后, 调用 count 方法
- $order = new \App\API\model\Order();
- var_dump($order->count());
- var_dump($order->count());die;
结果让人非常的震惊!
结果
同一个对象, 调用同一个方法, 结果居然不一样! 我反复试过都是这样. 我决定将他们的 sql 打印出来看看有什么问题
image.PNG
结果同样是让人哭笑不得
image.PNG
结果模型初始化是一次性的?(黑人问号. jpg)
我当时就觉得是 tp5 的 bug, 我要向作者反应!
后来我冷静下来, 还是先把项目问题解决了来再说哦. 饭碗要紧.
最终经过我的摸 (luan) 索(gao) , 我找到解决方案. 下面就把代码贴出来, 但是我确实不知道怎么解释这个问题. 也希望大神能够指出, 感激不尽
- protected function initialize()
- {
- parent::initialize(); // TODO: Change the autogenerated stub
- $this->name('order_1');// 将 table 方法换位 name 方法
- }
name 方法是也是指定表名, 只是不带表前缀. 经测试, 传入不含表前缀的表名可行, 我这里的数据表设计的时候没有表前缀, 所以传的都一样.
那么改过之后, 再来打印下之前的 sql
image.PNG
经测试可以正常切换两个表~
本次博客只在记录, 内容中有很多自己瞎猜的, 站不住脚, 让大神见笑. 非常希望能有大神指点
来源: http://www.jianshu.com/p/d47bff14ac0c