DavidPersson / li3_behaviors
Requires
- php: >=5.4
- composer/installers: 1.*
- unionofrad/lithium: 1.0.*@RC
This package is not auto-updated.
Last update: 2019-02-20 17:55:41 UTC
README
此库提供实现模型行为的基类。模型行为提供了一种简单的方式来扩展模型。这种模式允许将常用逻辑封装在行为中,以保持模型轻量,并且只由其自身的业务逻辑组成。
用法
管理和加载行为
首先,为了在模型中使用行为的能力,在模型中使用行为特性。之后,在模型类的$_actsAs
属性中定义你计划使用的所有行为。
// ... class Posts extends \lithium\data\Model { use li3_behaviors\data\model\Behaviors; protected $_actsAs = [ 'Sluggable' => ['field' => 'slug', 'label' => 'title'] ]; // ...
Behaviors
特性还使模型中可用一些静态方法,允许如下管理行为。
// Bind the sluggable behavior with configuration. Posts::bindBehavior('Sluggable', ['field' => 'slug', 'label' => 'title']); // Accessing configuration. Posts::behavior('Sluggable')->config(); Posts::behavior('Sluggable')->config('field'); // Updating configuration. Posts::behavior('Sluggable')->config('field', 'alt'); // Unbinding it again. Posts::unbindBehavior('Sluggable');
创建行为
现在我们能够加载和管理行为,我们可以创建自己的行为,它必须扩展Behavior
基类。以下示例中,我们在extensions/data/behavior/Sluggable.php
中创建了一个Sluggable
行为。
namespace app\extensions\data\behavior; use lithium\util\Inflector; class Sluggable extends \li3_behaviors\data\model\Behavior { protected static $_defaults = [ 'field' => 'slug', 'label' => 'title' ]; protected static function _filters($model, $behavior) { $model::applyFilter('save', function($self, $params, $chain) use ($behavior) { $params['data'][$behavior->config('field')] = static::_generate( $params['data'][$behavior->config('label')] ); return $chain->next($self, $params, $chain); }); } protected static function _generate($value) { return strtolower(Inflector::slug($value)); } }
行为配置
可以通过行为内的config()
从行为中访问每个行为的配置。默认情况下,将自动使用从模型$_actsAs
属性提供的用户配置以及行为中提供的默认值来设置行为的配置。
默认值将使用简单的数组添加($config += $defaults
)与提供的配置合并。如果您想改变配置合并的方式,请继续阅读。
提供自定义配置逻辑
行为通常对配置有不同的要求。在某些情况下,只需要合并一维数组,在其他情况下,需要合并或以自定义方式规范化嵌套的多维数组。
这就是为什么可以通过实现者自己轻松控制默认值与提供的配置合并的原因。默认情况下,我们执行简单的单维合并,将默认值和配置相互添加。要控制配置合并,重写基类中的_config()
方法。
以下示例中,我们将规范化某些配置选项,在合并到行为的默认值时。
// ... class Serializable extends \li3_behaviors\data\model\Behavior { protected static $_defaults = [ 'fields' => [] ]; protected static function _config($model, $behavior, $config, $defaults) { $config += $defaults; $config['fields'] = Set::normalize($config['fields']); foreach ($config['fields'] as $field => &$pass) { if (!$pass) { $pass = 'json'; } } return $config; } // ...
_config()
方法获取配置和$_defaults
中定义的默认值作为第3和第4个参数。该方法必须最终返回用于行为实例的配置。
将静态方法公开给模型
行为中存在的任何公共静态方法都会自动公开到模型中。这允许轻松地向模型添加方法。每个公开的静态方法都获得当前模型类名作为其第一个参数,以及行为的实例作为第二个参数。
这在需要查询模型结果或从行为中检索配置时很有用。
以下示例展示了如何将令牌生成方法公开给模型。
// ... class TokenGenerator extends \li3_behaviors\data\model\Behavior { protected static $_defaults = [ 'short' => false ]; // Generates a random token either short (8 chars) or long (16 chars) and // returns it. Default expiration is one year. public static function token($model, $behavior) { $token = substr(md5(String::random(32)), 0, $behavior->config('short') ? 8 : 16); $expires = date('Y-m-d H:i:s', strtotime('+1 year')); return compact('token', 'expires'); } // ...
将实例方法公开给模型
类似于公开静态方法,实例方法也可以轻松公开给模型。行为中实现的任何具体方法都将被公开。除了$entity
参数外,每个行为方法还会接收到模型名称和行为实例作为第二个参数,即用于访问配置。
// ... class Publishable extends \li3_behaviors\data\model\Behavior { protected static $_defaults = [ 'field' => 'is_published' ]; public function publish($model, $behavior, $entity) { $field = $behavior->config('field'); $entity->{$field} = true; } // ...
动态添加模型实例方法
有时您需要动态向模型实例添加方法。例如,当行为字段名称是用户可配置的,需要将其添加为实体的方法时。
可以通过重写_methods()
方法并返回一个以模型实例别名键控的方法数组来实现这一点。
// ... class Taggable extends \li3_behaviors\data\model\Behavior { // ... protected static function _methods($model, $behavior) { return [ $behavior->config('field') => function() { /* ... */ } ] } // ...
上述示例行为将为从模型返回的每个实体启用以下方法。
Posts::bindBehavior('Taggable', ['field' => 'taxonomy']); $item = Posts::create(); $item->taxonomy();
将过滤器附加到模型
要修改现有模型方法,应使用过滤器。如果您的行为需要使用过滤器,一个很好的地方是在行为中的_filters()
方法上附加它们。覆盖它以在初始化阶段将过滤器附加到模型。
// ... class Timestamp extends \li3_behaviors\data\model\Behavior { // ... protected static function _filters($model, $behavior) { $model::applyFilter('save', function($self, $params, $chain) use ($behavior) { $params['data'] = static::_timestamp($behavior, $params['entity'], $params['data']); return $chain->next($self, $params, $chain); }); } protected static function _timestamp($behavior, $entity, $data) { // ... } // ...
将查找器附加到模型
要将查找器添加到模型的查找方法中,应使用查找器。如果您的行为需要使用查找器,一个很好的地方是在行为中的_finders()
方法上附加它们。覆盖它以在初始化阶段将查找器添加到模型。
// ... class Taggable extends \li3_behaviors\data\model\Behavior { // ... protected static function _finders($model, $behavior) { $model::finder('tag', function($self, $params, $chain) use ($behavior) { // ... return $chain->next($self, $params, $chain); }); } // ...
对先前实现的感谢
- Nate Abele,https://github.com/nateabele/li3_behaviors
- Simon Jaillet,https://github.com/jails/li3_behaviors