It is convenient to use the same identification attribute, say info
, in all of the active records of your application. It should be a virtual read-only attribute defined by a getter method, its label being the model name.
One can easily use the info
attribute in breadcrumbs, detail views, grid views and other places. It will be $model->info
instead of $model->id
, $model->name
or $model->name . ' ' . $model->lastname
.
Example ¶
Let us consider a hypothetical application treating states and cities having one-to-many relation.
Models ¶
class State extends ActiveRecord
{
// ...
public function attributeLabels()
{
return [
'id' => 'ID',
'name' => 'State name',
// ...
'info' => 'State',
];
}
public function getInfo()
{
return $this->name;
}
}
class City extends ActiveRecord
{
// ...
public function getInfo()
{
return $this->name;
}
public function getState()
{
return $this->hasOne(State::className(), ['id' => 'state_id']);
}
}
The info
attribute can be used throughout the application.
Title and breadcrumbs ¶
This is the fragment of the city update view:
$this->title = 'Update ' . $model->info;
$this->params['breadcrumbs'][] = ['label' => 'States', 'url' => ['state/index']];
$this->params['breadcrumbs'][] = ['label' => $model->state->info, 'url' => ['state/view', 'id' => $model->state_id]];
$this->params['breadcrumbs'][] = ['label' => $model->info, 'url' => ['view', 'id' => $model->id]];
$this->params['breadcrumbs'][] = 'Update';
The page title "Update Seattle" is more informative then "Update City: 123" generated by the standard Gii template.
Cancel button ¶
A cancel button in the state or the city view:
<?= Html::a(
'Delete',
['delete', 'id' => $model->id],
[
'class' => 'btn btn-danger',
'data' => [
'confirm' => "Deleting {$model->info}. Are you sure?",
'method' => 'post',
],
]
) ?>
This button, when pressed, asks: "Deleting Seattle. Are you sure?" instead of the standard "Are you sure you want to delete this item?".
DetailView attribute ¶
A hyperlink in the detail view:
<?= DetailView::widget([
'model' => $model,
'attributes' => [
// ...
[
'attribute' => 'state_id',
'format' => 'raw',
'value' => Html::a($model->state->info, ['state/view', 'id' => $model->state_id]),
],
],
]) ?>
GridView column ¶
A column in a cities grid view:
<?= GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
// ...
'state.info',
],
]); ?>
In this case the 'state.info'
column label will be 'State'
, according to the City model definition.
The dropDownList ¶
In the drop down list of the city's form:
<?= $form->field($model, 'state_id')
->dropDownList(
ArrayHelper::map(State::find()->all(), 'id', 'info'),
['prompt' => 'Select one']
)
?>
General considerations ¶
Universality ¶
The universal info
attribute simplifies the application design because the developer should not remember the attribute names for every model used. Also, the info attribute label is always the model name, so the 'father.grandfather.info'
column specification in the detail or grid views do not require the implicit label specification, like 'father.grandfather.info:text:My label'
.
The info
attribute is similar to the __toString()
method used to convert any PHP object to the string.
The info
attribute does not guarantee the unambiguous identification string, but gives a rapid information on the model in question.
Custom ActiveRecord class ¶
If most of your models have the name
attribute or similar data useful for identification, it is helpful to define you own ActiveRecord
class:
class ActiveRecord extends \yii\db\ActiveRecord
{
// ...
/**
* @return string Instantiated model name
*/
public function getCalledClassName()
{
$reflectionClass = new \ReflectionClass(get_called_class());
return $reflectionClass->getShortName();
}
/**
* @return string Short description of the model
*/
public function getInfo()
{
// Take the model name
$m = $this->getCalledClassName();
// Compound info in different cases
if ($this->isNewRecord)
$i = 'New ' . $m;
elseif ($this->hasAttribute ('name') and trim($this->name))
$i = trim($this->name);
// ... other cases specific to your application
else
$i = $m . ' ' . $this->id;
// Return the result
return $i;
}
}
Override getInfo() ¶
In some models you can override the default getInfo()
method defined in your ActiveRecord
class. For example, the active record of a person can be identified by the first and the last name:
class Person extends ActiveRecord
{
// ...
public function attributeLabels()
{
return [
// ...
'info' => 'Person',
];
}
public function getInfo()
{
// The first and the last names combined
$i = trim(trim($this->name) . ' ' . trim($this->lastname)]);
// If empty, take the default value
if (! $i)
$i = parent::getInfo();
// Return the result
return $i;
}
}
The info
value is never empty. Some possible examples of its values:
- Jane Doe
- Jane
- Doe
- Person 123
- New Person
The case 1 is when there are both the first and the last names evaluated. The cases 2 and 3 are when there is only the first or only the last name evaluated, respectively. The case 4 is when the first and the last names are both empty. The last case is for a new record not yet saved to the database.
Search ActiveRecord class ¶
The info
attribute should be defined as not searchable in the search class:
class PersonSearch extends Person
{
// ...
public function rules()
{
return [
// ...
[['!info'], 'safe', 'on' => '*']
];
}
}
Gii templates ¶
One can include the above code examples in the customized Gii model and CRUD templates.
Interface
Might be smart to implement an interface for this behaviour? That way you're sure you have the getInfo() call implemented for every model.
Thanks for the good wiki entry.
Re: #19929
Hi EdoFre.
Thank you for your comment.
Imagine an application with a hundred of models, most of which having the
name
attribute. In this case the quickest way to writegetInfo()
method for every model is to create the customActiveRecord
class like one I have shown. Other application's models simply extend this class, sometimes overriding thegetInfo()
method.Alexandre
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.