30. Yii2在DetailView中使用自定义方法解析模型数据

作者:尐邪|2015-4-23 交流探讨

说明:yii2提供了足够解析模型数据的方法,但有时候我们还是需要实现一些自定义的数据格式,如果框架提供的方法不足以满足要求时,除了直接在模板中改写外,其实可以看看框架的源码,为框架增加更多的新方法。前几天在群里看到说,yii2的作者已经转go了,这个框架更新已经不在积极。

如GridView Widget中可以使用匿名方法对模型数据进行自定义格式化处理类似,DetailView Widget也提供了类似的处理方法,只是稍微繁琐一些,查看DetailView源码可以发现:

1. 查看其注释

/**
 * @var array|Formatter the formatter used to format model attribute values into displayable texts.
 * This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]]
 * instance. If this property is not set, the "formatter" application component will be used.
 */
public $formatter;

2. 继续跟踪 formatter

if ($this->formatter == null) {
    $this->formatter = Yii::$app->getFormatter();
} elseif (is_array($this->formatter)) {
    // 注意这行 2
    $this->formatter = Yii::createObject($this->formatter);
}
if (!$this->formatter instanceof Formatter) {
    // 注意这行 1
    throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.');
}

3. 得知自定义,格式化,工具类需要从 Formatter 继承

那具体方法要怎么实现呢,接着看源码

protected function renderAttribute($attribute, $index)
{
    if (is_string($this->template)) {
        return strtr($this->template, [
            '{label}' => $attribute['label'],
            // 从此行得知 调用的是 Formatter 类中的 format方法
            '{value}' => $this->formatter->format($attribute['value'], $attribute['format']),
        ]);
    } else {
        return call_user_func($this->template, $attribute, $index, $this);
    }
}

4. 于是我们去 yii\i18n\Formatter 中找对应的 format 方法

public function format($value, $format)
{
    if (is_array($format)) {
        if (!isset($format[0])) {
            throw new InvalidParamException('The $format array must contain at least one element.');
        }
        $f = $format[0];
        $format[0] = $value;
        $params = $format;
        $format = $f;
    } else {
        $params = [$value];
    }
    // 注意这行
    $method = 'as' . $format;
    if ($this->hasMethod($method)) {
        return call_user_func_array([$this, $method], $params);
    } else {
        throw new InvalidParamException("Unknown format type: $format");
    }
}

5. 接着可能就联想到了最常见的 ‘ctime:datetime’ 的格式化方法, 搜索下 asDatetime 就会找到对应的方法了

public function asDatetime($value, $format = null)
{
    if ($format === null) {
        $format = $this->datetimeFormat;
    }
    return $this->formatDateTimeValue($value, $format, 'datetime');
}

6. 发现有两个参数,其第一个参数就是解析对应模型属性的值了

到这边,整个流程基本明了,于是乎编辑我们自己的自定义类及格式化方法。

namespace common\louli\vendor;

use yii\helpers\Html;
use yii\i18n\Formatter;

class ImgKeysFormatter extends Formatter
{
    public function asKeys2img($value)
    {
        $tHtml = '';
        $keys = explode('|',$value);
        $tHtml .= Html::beginTag('div');
        foreach($keys as $key) {
            $tHtml .= Html::img(\Yii::$app->params['qiniu']['baseurl'].$key.'-ll120png',[
                'width'=>60,
                'height'=>60,
                'style'=>'border-radius:50%;margin:5px;'
            ]);
        }
        $tHtml .= Html::endTag('div');
        return $tHtml;
    }
}

7. 这样我们就有了 keys2img 方法

接下来就是嵌入使用了,多的就不赘述了,请看以下节选的View源码

<?= DetailView::widget([
    'model' => $model,
    'attributes' => [
        'id',
        'nickname',
        'logo:keys2img',// 使用自定义格式化方法
        'randnickname',
        'randlogo:keys2img:随机头像',// 自定义格式及标签
        'handler',
        'ctime:datetime',
        [
            'attribute'=>'ctime',
            'value'=>date("Y-m-d H:i:s",$model->ctime)
        ],
        'status',
    ],
    'formatter' => [ // 这边引入我们自定义的类 (包含需要使用的格式化方法)
        'class'=>'common\louli\vendor\ImgKeysFormatter',
    ],
]) ?>