Тоже столкнулся с этой проблемой. Сделал свой виджет немного подправив DetailView по аналогии с тем, как оно сделано в GridView.
Создаём свой класс app\components\widgets\DetailWidget и чуть-чуть меняем метод renderAttribute:
<?php
namespace app\components\widgets;
use Yii;
use yii\base\Arrayable;
use yii\base\InvalidConfigException;
use yii\base\Model;
use yii\base\Widget;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Inflector;
use yii\i18n\Formatter;
class DetailView extends \yii\widgets\DetailView
{
/**
* @inheritdoc
*/
protected function renderAttribute($attribute, $index)
{
if (is_string($this->template)) {
$captionOptions = Html::renderTagAttributes(ArrayHelper::getValue($attribute, 'captionOptions', []));
$contentOptions = Html::renderTagAttributes(ArrayHelper::getValue($attribute, 'contentOptions', []));
if ($attribute['value'] instanceof Closure) {
$value = call_user_func($attribute['value'], $model);
} else {
$value = $this->formatter->format($attribute['value'], $attribute['format']);
}
return strtr($this->template, [
'{label}' => $attribute['label'],
'{value}' => $value,
'{captionOptions}' => $captionOptions,
'{contentOptions}' => $contentOptions,
]);
}
return call_user_func($this->template, $attribute, $index, $this);
}
}
Теперь, используя наш новый виджет app\components\widgets\DetailView вместо yii\widgets\DetailView, можно в качестве value ставить свою функцию, например у меня это выглядит так:
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'title',
'author',
'year',
'description',
'format',
[
'attribute' => 'categories',
'format' => 'html',
'value' => function ($model) {
/** @var app\models\library\Book $model */
$items = $model->getCategories()->all();
$result = '';
foreach ($items as $item) {
$result .= "<p>$item->name</p>";
}
return $result;
}
]
],
]) ?>