Yii2 关键概念

与laravel相较,Yii2 将配置(依赖关系定义)外化,用行为(更类似于python中的织入)类,弥补 Trait的一些不足,好处是可以动态扩展动作。至于事件处理则大同小异,有趣的是在命名上,yii2借用了jquery事件系统的那一套,on,off,trigger。当然也相同之处,比如应用都是建立在容器之上。相比其它的面向领域,面向接口编程,Yii2使用模块,来分层,分中心小应用,细化大架构。而getter/setter,及过滤器,java痕迹太明显。

组件Component

  • Yii 应用的主要基石。是 yii\base\Component 类或其子类的实例
    • 主要由 属性(Property),事件(Event),行为(Behavior)三个功能组成
    • 比常规的对象(Object)稍微重量级,要使用额外的内存和 CPU 时间来处理 事件 和 行为
    • 不需要使用事件和行为时,继承 yii\base\Object ,支持属性(Property)功能
  • 重写 Component 或 Object
    • 永远在重写的构造方法结尾处调用一下父类的构造方法
    • 传入 $config 作为构造器方法最后一个参数,由父构造方调用,在应用配置之前初始化
    • 若重写了 BaseObject::init() 方法,确保在 init 方法的开头处调用了父类的 init 方法
  • 组件实例化2种方式
    • 实例化组件类,new组件类, 组件普通参数 + 组件配置属性参数
    • 通过 \Yii::createObject静态方法,创建组件实例
      • 第一个数组参数 class关联组件类名,后续关联元素依次为组件实例属性及值
      • 第二数组参数 组件的普通参数

Yii::createObject() 基于依赖注入容器实现

  • yii\base\BaseObject 类执行时的生命周期
    • 构造方法内的预初始化过程
    • 通过 $config 配置对象
    • 在 init() 方法内进行初始化后的收尾工作
    • 对象方法调用, 上述步骤皆在对象的构造方法内完成,即获得实例就已经初始化,可供使用

属性Property

  • Yii 引入 yii\base\Object 的基类, 支持基于类内的 getter 和 setter(读取器和设定器)方法来定义属性
  • getter 和 setter 定义属性规则和限制
    • 这类属性的名字是不区分大小写,源于PHP 方法名是不区分大小写
    • 若此类属性名和类成员变量相同,以后者为准
    • 该类属性不支持可见性(访问限制)
    • 这类属性的 getter 和 setter 方法只能定义为非静态的
    • 对不确定有无魔术方法(getter 或 setter的属性正常调用 property_exists() 将不会生效
      • 若真有此需求,应用 canGetProperty() 或 canSetProperty()

事件Events

  • 事件可以将自定义代码“注入”到现有代码中的特定执行点

    • 附加自定义代码到某个事件,当这个事件被触发时,这些代码就会自动执行
  • 事件处理器 Event Handlers

    • 事件处理器是一个PHP 回调函数,也可以是一个可调用对象
    • 字符串形式指定的 PHP 全局函数 ,如 'trim'
    • 对象名和方法名数组形式指定的对象方法,[$object, $method]
    • 类名和方法名数组形式指定的静态类方法,如 [$class, $method]
    • 匿名函数,如 function ($event) { ... }
  • 事件对象$event

    • event name:事件名
    • event sender:调用 trigger() 方法的对象
    • custom data 附加事件处理器时传入的数据,默认为空
  • 附加事件处理器

    • 调用组件类的on方法,诸如 \yii\base\Component::on()
    • public void on ( $name, $handler, $data = null, $append = true )
  • 事件处理器顺序(Event Handler Order)

    • 当事件被触发,已附加的处理器将按附加次序依次调用
    • 若需要停止同一事件的后续处理器的调用,可设置 event 参数的 yii\base\Event::event参数的‘yii\base\Event::handled属性为真
    • 第四个参数 $append 为假时,可在处理器队列最前面插入新处理器
  • 触发事件(Triggering Events)

    • 事件通过调用 yii\base\Component::trigger() 方法触发
      • public void trigger ( $name, yii\base\Event $event = null )
    • 推荐使用类常量来表示事件名, 事件对象必须是 yii\base\Event类或其子类的实例
  • 移除事件处理器(Detaching Event Handlers)

    • public boolean off ( $name, $handler = null )
  • 类级别的事件处理器

    • 应用场景 想要一个类的所有实例都响应一个被触发的事件
    • 调用静态方法 yii\base\Event::on() 在类级别附加处理器
      • 在事件处理器内,通过 $event->sender 获取触发事件的对象
      • 当对象触发事件时,它首先调用实例级别的处理器,然后才会调用类级别处理器
    • 静态方法yii\base\Event::trigger()来触发一个类级别事件,移除用off
    • 移除签名 public static boolean off ( $class, $name, $handler = null )
  • 接口事件

    • 调用 Event::on() 并将接口类名作为第一个参数
    • 可在实现接口事件类中触发这个事件,但不能让所有实现这个接口的类都触发事件
  • 全局事件

    • 需要一个全局可访问的单例,如应用实例
    • 事件触发者不调用其自身的 trigger() 方法,而是调用单例的 trigger() 方法来触发全局事件
    • 优点 是当附加处理器到一个对象要触发的事件时, 不需要产生该对象
  • 通配符事件Wildcard Events

    • foo.event.* ,通配符模式支持实例 或类级别的事件

行为

  • 行为是 yii\base\Behavior 或其子类的实例,也称为 mixins,类似于原生的Trait

    • 作用 无须改变类继承关系即可增强一个已有的 组件 类功能
    • 当行为附加到组件后,它将“注入”它的方法和属性到组件
    • 行为通过组件能响应被触发的事件,从而自定义或调整组件正常执行的代码
  • 处理事件

    • 让行为响应对应组件的事件触发, 应覆写 yii\base\Behavior::events() 方法
      • 行为的 events() 方法返回事件列表和相应的处理器,指定事件处理器格式如下
        • 指向行为类的方法名的字符串
        • 对象或类名和方法名的数组,如 [$object, 'methodName'];
        • 匿名方法
  • 附加行为

    • 静态附加行为
      • 覆写行为要附加的组件类的 behaviors() 方法即可
      • behaviors() 方法返回行为配置列表,每个行为配置可以是行为类名也可以是配置数组
      • 过指定行为配置数组相应的键可以给行为关联一个名称,这种行为称为命名行为,反之匿名行为或命名行为
    • 动态附加行为
      • 在对应组件里调用 yii\base\Component::attachBehavior() 方法
      • 或 yii\base\Component::attachBehaviors() 方法一次附加多个行为
        • public void attachBehaviors (array $behaviors )
      • 通过配置附加行为
        • ['as myBehavior2' => MyBehavior::className()]
  • 使用行为

    • 必须先将为附加到 component 类或其子类组件,然后可通过访问组件访问行为的公共成员变量
    • 若两个行为都定义了一样的属性或方法,并且它们都附加到同一个组件,先附加者有优先权
    • 附加行为到组件时的命名行为,可以使用这个名称来访问行为对象$component->getBehavior('myBehavior');
    • 获取附加到这个组件的所有行为 getBehaviors()
  • 移除行为

    • 可以调用 yii\base\Component::detachBehavior() 方法用行为相关联的名字实现
  • Yii2内置行为类

    • yii\behaviors\TimestampBehavior 在 Active Record 存储时自动更新它的时间戳属性
    • yii\behaviors\BlameableBehavior 使用当前用户 ID 自动填充指定的属性
    • yii\behaviors\SluggableBehavior 自动填充指定的属性,其值可以在 URL 中用作 slug
    • yii\behaviors\AttributeBehavior 在发生特定事件时自动为 ActiveRecord 对象的一个或多个属性 指定一个指定的值
    • yii2tech\ar\softdelete\SoftDeleteBehavior 提供软删除和软恢复 ActiveRecord 的 方法
    • yii2tech\ar\position\PositionBehavior 允许通过提供重新排序方法来 管理整数字段中的记录顺序
  • 行为 VS Traits

    • 都将自己的属性和方法“注入”到主类中,二者类似互补类而非替代类
    • 行为类优点
      • 行为类像普通类支持继承
      • 行为无须修改组件类就可动态附加到组件或移除
      • 行为是可配置的,而 traits 则不可行
      • 行为可以通过响应事件来定制组件的代码执行
    • traits 的原因
      • Traits 比行为更有效,因为行为是既需要时间又需要内存的对象
    • 名称冲突解决方案
      • 当附属于同一组件的不同行为之间可能存在名称冲突时, 通过优先考虑附加到该组件的行为
      • 不同 traits 引起的名称冲突需要通过 重命名受影响的属性或方法进行手动解决

配置 Configurations

  • 概述

    • 在创建新对象和初始化已存在对象时使用配置
    • 配置通常包含被创建对象的类名和一组将要赋值给对象 属性的初始值
    • 亦可包含一组将被附加到对象事件上的句柄,和一组将被附加到对象上的行为
  • 使用

    • Yii::createObject() 方法接受一个配置数组并根据数组中指定的类名创建对象
    • 对于已存在的对象,可以使用 Yii::configure() 方法根据配置去初始化其属性
      • Yii::configure($object, $config)
      • 注若配置一个已存在的对象,那么配置数组中不应该包含指定类名的 class 元素
  • 配置的格式

    • class 元素指定了将要创建的对象的完全限定类名
    • propertyName 元素指定了对象属性的初始值,键名是属性名,值是该属性对应的初始值
      • 只有公共成员变量以及通过 getter/setter 定义的 属性可以被配置
    • on eventName 元素指定了附加到对象事件上的句柄,数组的键名由 on 前缀加事件名组成
    • as behaviorName 元素指定了附加到对象的行为,值表示创建行为的配置信息
  • 应用的配置

    • application 类拥有很多可配置的属性和事件
    • components 属性可以接收配置数组并通过应用注册为组件
    • yii2.0.11+ 系统配置支持使用 container 属性来配置依赖注入容器
  • 小部件的配置

    • yii\base\Widget::widget() 和 yii\base\Widget::begin()方法都可以用来创建小部件
    • 通过用配置来自定义其属性,注意 给出类名的情况下,配置数组不需要再包含class键
  • 默认配置

    • Yii::createObject() 方法基于依赖注入容器实现
    • 使用 Yii::creatObject() 创建对象时,可以附加一系列默认配置到指定类的任何实例
    • 默认配置可以在入口脚本 中调用 Yii::$container->set() 来定义

别名

  • 设置与解析
    • 使用 Yii::setAlias() 来给文件路径或 URL 定义别名
    • 调用 Yii::getAlias() 命令来解析根别名到对应的文件路径或 URL
  • 应用提供了一个名为 aliases 的可写属性, 可在应用配置中设置它
  • 使用别名 Yii内路径属性接受别名
  • Yii2 预定义别名
  • 扩展的别名
    • 一个通过 Composer 安装的 扩展 都自动添加了一个别名,定义于引导启动阶段

类的自动加载

  • Yii自动加载器
    • 每个类都必须置于命名空间之下
    • 每个类都必须保存为单独文件
    • 要将自定义命名空间添加到自动加载器,需要使用 Yii::setAlias() 为命名空间的根目录定义别名
  • 类映射表
    • 类映射表功能,建立一个从类的名字到类文件路径的映射
    • 当自动加载器加载一个文件时,他首先检查映射表里有没有该类
    • 可以用 Yii::$classMap 方法向映射表中添加类
  • 其他自动加载器
    • 在其他自动加载器安装成功之后, 再包含 Yii.php (yii的自动加载器)
    • 目的使 Yii 成为第一个响应任何类自动加载请求的自动加载器
  • Yii 自动加载器支持自动加载扩展的类,需要在 composer.json 文件里正确地定义 autoload 部分

服务定位器(Service Locator)

  • 定义
    • 提供各种应用所需的服务(或组件)的对象
    • 在服务定位器中, 每个组件都只有一个单独的实例,并通过ID 唯一地标识
    • 在 Yii 中,服务定位器是 yii\di\ServiceLocator 或其子类的一个实例
  • 应用场景
    • 最常用的服务定位器是application(应用)对象,可以通过 \Yii::$app 访问
    • 每个模块对象本身也是一个服务定位器, 模板可视为一个子应用
  • 使用服务定位器
    • 注册相关组件
      • 通过 yii\di\ServiceLocator::set() 方法进行相关组件注册。
      • public void set ( $id, $definition )
      • $definition 可以是类名,配置数组,php可调用对象,或者本身就是一个对象实例
    • 允许通过组件 ID 像访问一个属性值那样访问一个组件
      • 服务定位器会返回同一个组件的单例
      • yii\di\ServiceLocator::has() 检查某组件 ID 是否被注册
      • yii\di\ServiceLocator::get()
  • 遍历树(Tree traversal)
    • 模块允许任意嵌套; Yii 应用程序本质上是一个模块树
  • 模块中组件的配置决不会与来自父模块中组件的配置合并

依赖注入容器

  • 依赖注入容器是一个对象,知道怎样初始化并配置对象及其依赖的所有对象
  • Yii 通过 yii\di\Container 类提供 DI 容器特性
    • 构造方法注入
      • 容器会尝试获取它所依赖的类或接口的实例,然后通过构造器将其注入新的对象
    • 方法注入
      • 可以提供仅由类的单个方法需要的依赖关系
    • Setter 和属性注入
      • Setter 和属性注入是通过配置提供,该配置会提供给容器用于通过相应的 Setter 或属性注入依赖
    • PHP 回调注入(PHP Callable Injection)
      • 容器将使用已注册的 PHP 回调来构建类的新实例

yii\di\Container::get() 方法将其第三个参数作为配置数组应用于正在创建的对象 如果该类实现 yii\base\Configurable 接口(例如 yii\base\BaseObject),则配置数组将作为最后一个参数传递给类构造函数

  • 注册依赖关系
    • 用 yii\di\Container::set() 注册依赖关系
    • 注册会用到一个依赖关系名称和一个依赖关系的定义, 键值对可递归,方便容器管理实例,类似laravel的别名系统
    • 依赖关系名称可为类名,接口名或一个别名,依赖关系的定义可以是类名,配置数组,一个PHP回调
    • 通过 set() 注册的依赖关系,在每次使用时都会产生一个新实例
    • 使用 yii\di\Container::setSingleton() 注册一个单例的依赖关系
  • 解析依赖关系
    • 依赖关系解析是递归式进行
    • 注册依赖关系后,容器会自动解析依赖关系, 将依赖实例化并注入新创建的对象
    • 依赖关系名,可以是通过 set() 或 setSingleton() 注册的,也可以是一个类构造器参数列表和一个 configuration 用于配置新创建的对象
  • 使用依赖注入
    • 在应用程序的入口脚本中引入 Yii.php 文件时, Yii 就创建了一个 DI 容器
    • 该 DI 容器可以通过 Yii::$container 访问
    • 当调用 Yii::createObject() 时,此方法实际上会调用这个容器的 get() 方法创建新对象
    • 在部件调用中给出的属性将始终覆盖DI容器中的定义,若报错无法被实例化,需要告知容器如何处理依赖关系
  • 高级实用性
    • 一次配置多个定义,将配置数组传递给 setDefinitions() 或 setSingletons() 方法
    • 配置数组格式
      • key:类名称,接口名称或别名
      • value:与 class 关联的定义,class关联的定义,‘identifies 参数值将传递给set()方法
      • 可以选择将依赖项的构造函数参数作为第三个参数
      • Instance::of('tempFileStorage') 符号,Container 将隐含地提供一个用 tempFileStorage 名称注册的依赖项
        • 应用场景 内部配置依赖项
    • 通过 set() 注册的依赖项将在每次需要时实例化
  • 依赖关系注册使用
    • 应用开发在入口注册依赖关系
    • 扩展开发,则在扩展引导类中注册依赖关系
  • 小结
    • Yii 在依赖住入(DI)容器之上实现了它的服务定位器
    • 当一个服务定位器尝试创建一个新的对象实例时,它会把调用转发到 DI 容器
本作品采用《CC 协议》,转载必须注明作者和本文链接
pardon110
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
开发者 @ 社科大
文章
133
粉丝
24
喜欢
100
收藏
54
排名:107
访问:8.9 万
私信
所有博文
社区赞助商