## traits 详解
>[info] traits 知识并不是ThinkPHP教学的内容,但软删除的模型中应用了这项新技术~~
* * * * *
> 我们知道,PHP面向对象是单继承的,无法同时从两个或以上的基类继承属性或方法。从PHP 5.4起, Traits(构件类)技术出现,使多继承成为现实!
* * * * *
>[warning] 注意:
1. Traits类的声明使用trait关键词,
2. 类中使用use关键字声明要组合的Traits类名称;
3. Traits类不能直接实例化
* * * * *
#### 实例演示:
~~~
<?php
//创建trait类:Test1
trait Test1
{
public function demo1()
{
return 'Test1::demo1()<br />';
}
}
//创建基类(父类):Father
class Father
{
public function demo1()
{
return 'Father::demo1<br />';
}
}
//创建自定义类:MyClass 是Father的派生类(子类)
class MyClass extends Father
{
use Test1;
public function demo1()
{
return 'MyClass::demo1<br />';
}
}
//创建自定义MyClass的对象
$obj = new MyClass;
//访问$obj对象的demo1()方法
echo $obj -> demo1();
~~~
> 1. 现在我们已经有了三个类:trait类,基类Father和派生类MyClass;
> 2. 每个类中都有一个demo1( )方法;
> 3. $obj是MyClass实例化的对象,那么:`echo $obj -> demo1();`将会调用哪个类的demo1()方法呢?
* 让我们运行一下吧:
~~~
MyClass::demo1
~~~
> 可见,实际调用的是MyClass类的demo1()方法。这符合继承的原理,因为子复写了父类同名方法。
> <font color="red">下面我们把MyClass类中的demo1()方法删除</font>
* 修改后的代码如下:
~~~
<?php
//创建trait类:Test1
trait Test1
{
public function demo1()
{
return 'Test1::demo1()<br />';
}
}
//创建基类(父类):Father
class Father
{
public function demo1()
{
return 'Father::demo1<br />';
}
}
//创建自定义类:MyClass 是Father的派生类(子类)
class MyClass extends Father
{
use Test1;
}
//创建自定义MyClass的对象
$obj = new MyClass;
//访问$obj对象的demo1()方法
echo $obj -> demo1();
~~~
> 按照之前知识,如果调用的方法,在子类中没有定义,则调用父类中同名方法,真的是这样的吗?让我们测试一下吧:
* 这时运行结果如下:
~~~
Test1::demo1()
~~~
> 觉得奇怪吗?为什么不是父类的输出结果:`Father::demo1()`,而是:`Test1::demo1()`呢。这因为子类中,除了要继承Father类,还要继承trait类Test1,显然,子类MyClass中的`use Test1` 的继承优先级要高于原父类的Father,于是产生覆盖效果。
> <span style="color:red;">trait类同名方法覆盖了基类Father中的同名方法。</span>
#### 那么我们屏蔽掉:`use Test1;`,会调用父类同名方法吗?
* 再次修改一下代码:
~~~
<?php
//创建trait类:Test1
trait Test1
{
public function demo1()
{
return 'Test1::demo1()<br />';
}
}
//创建基类(父类):Father
class Father
{
public function demo1()
{
return 'Father::demo1<br />';
}
}
//创建自定义类:MyClass 是Father的派生类(子类)
class MyClass extends Father
{
}
//创建自定义MyClass的对象
$obj = new MyClass;
//访问$obj对象的demo1()方法
echo $obj -> demo1();
~~~
> 现在子类MyClass中已经没有use Test1;trait类Test已失效。
* 现在执行结果,如不出意料,应该是父类的demo1()方法的结果:
~~~
Father::demo1
~~~
>[info] 果然如此,调用是父类中的demo1()方法。
* * * * *
>[info] 现在我们思考另一个问题:如果有二个trait类,都是一个demo1方法,怎么办?这是个很常见的问题,必须得到解决。
* 我们再次修改代码,增加一个trait类Test2:
~~~
<?php
//创建trait类:Test1
trait Test1
{
public function demo1()
{
return 'Test1::demo1()<br />';
}
}
//创建trait类:Test2
trait Test2
{
public function demo1()
{
return 'Test2::demo1()<br />';
}
}
//创建基类(父类):Father
class Father
{
public function demo1()
{
return 'Father::demo1<br />';
}
}
//创建自定义类:MyClass 是Father的派生类(子类)
class MyClass extends Father
{
use Test1,Test2;
}
//创建自定义MyClass的对象
$obj = new MyClass;
//访问$obj对象的demo1()方法
echo $obj -> demo1();
~~~
> 现在有二个trait类Test1和Test2,这二个类中都有demo1方法,`echo $obj -> demo1();`好纠结,应该调用哪个呢?
* 运行结果,肯定是错误的:
~~~
//大意是trait中的方法demo1 没有提交运行,因为有重复的定义
Fatal error: Trait method demo1 has not been applied, because there are collisions with other trait methods on MyClass in /Users/zhupeter/Documents/web/test/index.php on line 31
~~~
>[info] 解决方案有二种:
1. 设置冲突时,调用哪一个方法:`insteadof;`
2. 给可能冲突的方法起一个别名:`as;`
~~~
<?php
//创建trait类:Test1
trait Test1
{
public function demo1()
{
return 'Test1::demo1()<br />';
}
}
//创建trait类:Test2
trait Test2
{
public function demo1()
{
return 'Test2::demo1()<br />';
}
}
//创建基类(父类):Father
class Father
{
public function demo1()
{
return 'Father::demo1<br />';
}
}
//创建自定义类:MyClass 是Father的派生类(子类)
class MyClass extends Father
{
use Test1,Test2{
//方案1:Test2的demo1方法与Test1的demo1方法冲突时,调用Test2的demo1方法;
Test2::demo1 insteadof Test1;
//Test2中的demo1方法可以使用方法别名td2调用
Test2::demo1 as td2;
}
}
//创建自定义MyClass的对象
$obj = new MyClass;
//访问$obj对象的demo1()方法
//根据use中设置的调用规则,现在调用的应该是Test2类中的demo1方法
echo $obj -> demo1();
//第二套解决方案:给可能冲突的该当起别名来规避
//现在调用的仍然是Test2类中的demo1方法
echo $obj -> td2();
~~~
* 结过分析,运行结果也是可以预见的:
~~~
Test2::demo1()
Test2::demo1()
~~~
#### 这种特殊类的声明,学会了吗?亲们~~
* * * * *
#### 总结:
>[danger] ThinkPHP5中的软删除等很多特殊操作,都是通过trait类来实现的,简而言之,traits 提供了实现多继承的一种机制。一定要掌握这种新技术。
- 前言[随时更新]
- 开发环境
- 1.Mac环境
- 2.windows环境
- 模型对象
- 1.创建模型对象
- 2.模型初始化
- 数据对象
- 1.定义数据对象
- 2.创建数据对象
- 1.data方法
- 2.setAttr方法
- 3.__set方法
- 4.查询数据对象
- 1.getData方法
- 2.getAttr方法
- 3.__get方法
- OOP难点总结
- 1.get_class( )实例讲解
- 2.get_called_class( )实例讲解
- 3.__call( )实例讲解
- 3.__callStatic( )实例讲解
- 4.call_user_func_array函数[重点]
- 5.普通方法与静态方法
- 6.在Model源码中的应用
- 7.new static 延迟静态绑定
- PHP标准化规范
- 查询数据
- 1.获取单条:get静态方法
- 2.获取单条:对象查询
- 3.获取多条:all静态方法
- 4.获取多条:对象查询
- 5.获取字段值:value方法
- 6.获取列值:column方法
- 7.动态查询:getBy字段名
- 8.助手函数:model查询
- 9.加载器:Loader类查询
- 10.数据库与模型查询对比
- 新增数据
- 1.sava方法
- 2.savaAll方法
- 3.create静态方法
- 4.insert静态调用
- 更新数据
- 1.单条更新:save方法
- 2.批量更新:saveAll方法
- 3.静态更新:update方法
- 4.查询类Query直接更新
- 5. 闭包更新
- 删除数据
- 1.删除当前记录:delete
- 2.静态条件删除:destory
- 获取器
- 1.模型方法:set属性Attr
- 修改器
- 1.set属性Attr
- 时间戳
- 1.MySQL中日期类型复习
- 2.时间戳功能详解
- 软删除[重点]
- 1.traits详解[选学内容]
- 2.SoftDelet类源码分析
- 3. delete实例删除
- 4.destroy条件删除
- 5.restore恢复数据
- 类型转换
- 1. 规则设置
- 2. 实例演示
- 查询范围
- 1. 基本概念
- 2.实例演示