Yii2: A reason to support DetailView value as Closure - delayed evaluation of value

Created on 20 Jul 2015  路  14Comments  路  Source: yiisoft/yii2

Currently DataColumn supports setting value as Closure, while DetailView - does not, which is inconsistent. Often exactly the same formatting function is used to format the value in both GridView and DetailView.

Also, allowing setting value as Closure in DetailView delays the evaluation of that value until the actual rendering of that value takes place. And that evaluation may not happen at all if visible is set to false. This allows for the following code to work.

Initial (non-working) code:

<?= \yii\widgets\DetailView::widget([
    'model' => $model,
    'attributes' => [
        [
            'label' => 'User Info',
            // When $model->user is null we get PHP error.
            'value' => '<span class="' ($model->user->is_enabled ? 'green' : 'red') . '">' . $model->user->name . '</span>',
            'visible' => $model->user !== null,
        ],
    ],
]); ?>

A workaround without support for Closures:

'value' => ($model->user !== null ? '<span class="' ($model->user->is_enabled ? 'green' : 'red') . '">' . $model->user->name . '</span>' : ''),
'visible' => $model->user !== null,

But why add additional check for $model->user !== null (when it already present in 'visible') and define an empty string value for 'value' when that empty string case will never happen?

Better code with support for Closures:

// When $model->user is null Closure will not be called at all (due to visible being false), no PHP error.
'value' => function(){ return '<span class="' ($model->user->is_enabled ? 'green' : 'red') . '">' . $model->user->name . '</span>'; },
'visible' => $model->user !== null,
enhancement

Most helpful comment

If you want to use Closures in DetailView, just cover your function into call_user_func()

For example:

'value' => call_user_func(function($model) {
    return "{$model->lastname} {$model->firstname}";
}, $model)

I've seen this question many times. May be, add this solution to docs?

All 14 comments

Detailview have only one record. If you have some condition for a field value, You should do it before detailview.. and then show result in detailview

Detailview is only one record. If you have some condition for a field, You should do it before detailview

Try to implement my example above as you suggest, then compare with my version (which assumes support for Closures in DetailView) and see how many more additional lines/variables and duplicate checks you have in your code.

<?php
$userinfo = ($model->user)?(($model->user->enable)?'blue':'red'):'-';
?>
<?= \yii\widgets\DetailView::widget([
    'model' => $model,
    'attributes' => [
        [
            'label' => 'User Info',
            'value' => $userinfo,
        ],
    ],
]); ?>
<?php
$userinfo = ($model->user)?(($model->user->enable)?'blue':'red'):'-';
?>
<?= \yii\widgets\DetailView::widget([
    'model' => $model,
    'attributes' => [
        [
            'label' => 'User Info',
            'value' => $userinfo,
        ],
    ],
]); ?>

You have lost a crucial functionality from my example by missing the line that sets a value for 'visible':

'visible' => $model->user !== null,

Now, lets assume your code has that line with 'visible'. In that case your code has:
1) One additional code line: $userinfo = ($model->user)?(($model->user->enable)?'blue':'red'):'-';
2) One additional variable: $userinfo
3) One duplicate check for $model->user !== null
4) A definition for one additional string ('-') which is not supposed to exist in the code _at all_ - because it is not supposed to be displayed on the page _ever_ - when $model->user is null the appropriate row in DetailView will not be displayed due to the check in 'visible'.

I only give you example.. that closure in detailview not important thing.. We can do with other way although need more line code..

I would second that. There is no obvious reason why closures are not implemented in DetailView. The code with closures is more elegant compared to calculating values prior to the widget kicking in.

If you want to use Closures in DetailView, just cover your function into call_user_func()

For example:

'value' => call_user_func(function($model) {
    return "{$model->lastname} {$model->firstname}";
}, $model)

I've seen this question many times. May be, add this solution to docs?

@berezuev
call_user_func immediately calls the specified function, so your "solution" is equivalent to:

'value' => "{$model->lastname} {$model->firstname}"

which does not solve the problem I am describing in my first post in any way.

@hscstudio

<?php
$userinfo = ($model->user)?(($model->user->enable)?'blue':'red'):'-';
?>
<?= \yii\widgets\DetailView::widget([
    'model' => $model,
    'attributes' => [
        [
            'label' => 'User Info',
            'value' => $userinfo,
        ],
    ],
]); ?>

So ugly solution. Had to create an extra variable in View for simple task.

The anonymous function will be the most elegant solution, and fully comply with the GridView.

So ugly solution. Had to create an extra variable in View for simple task.

??? Why do not write this:

<?= \yii\widgets\DetailView::widget([
    'model' => $model,
    'attributes' => [
        [
            'label' => 'User Info',
            'value' => ($model->user) ? (($model->user->enable) ? 'blue' : 'red') : '-',
        ],
    ],
]); ?>

Sometimes you can't put all the logic into a single line.

@klimov-paul

??? Why do not write this

Because the calculation logic of "value" is much more complicated and can not be placed in such a ternary operator. And I'm not too lazy to repeat again another reason: The anonymous function will be the most elegant solution, and fully comply with the GridView.

I think optional support of closures would not hurt.

Resolved by commit 6428fab406a1777c875d32d37d3644fa0f3ab3ad

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Locustv2 picture Locustv2  路  3Comments

chaintng picture chaintng  路  3Comments

nokimaro picture nokimaro  路  3Comments

kminooie picture kminooie  路  3Comments

MUTOgen picture MUTOgen  路  3Comments