Maudit
(Maudit)
September 28, 2021, 4:07pm
1
DATABASE
item
int id
int category_id
varchar(255) name
category
int id
varchar(255) name
Implementation 1
ItemController.php
public function actionCreate()
{
...
$categories = ArrayHelper::map(Category::find()->all(), 'id', 'name');
return $this->render('create', [
'model' => $model,
'categories' => $categories,
]);
}
views/item/_form.php
<?= $form->field($model, 'category_id')->dropDownList($categories, ['prompt' => 'Select...']); ?>
Implementation 2
Item.php
...
public function getCategoryOptions()
{
return ArrayHelper::map(Category::find()->all(), 'id', 'name');
}
views/item/_form.php
<?= $form->field($model, 'category_id')->dropDownList($model->categoryOptions, 'id', 'name'), ['prompt' => 'Select...']); ?>
Implementation 3
views/item/_form.php
<?= $form->field($model, 'category_id')->dropDownList(ArrayHelper::map(Category::find()->all(), 'id', 'name'), ['prompt' => 'Select...']); ?>
samdark
(Alexander Makarov)
September 28, 2021, 6:51pm
2
Personally, I’d prefer variant 2.
2 Likes
InsaneSkull
(Insane Skull)
September 29, 2021, 4:28am
3
Maudit:
Implementation 2
Should create this function in Category
model. Make it static function and reuse everywhere.
Arrayhelper not needed
Category::find()->select('name')->indexBy('id')->column();
5 Likes
TomaszKane
(Tomasz Kane)
September 29, 2021, 3:29pm
4
I have Trait who implements this type of method for any ActiveRecord model class.
2 Likes
softark
(Softark)
September 29, 2021, 11:44pm
5
It’s very interesting. Could you show us how you do it?
1 Like
InsaneSkull
(Insane Skull)
September 30, 2021, 3:41am
6
@TomaszKane that will be interesting to see, can you share how you do it with trait?
1 Like
TomaszKane
(Tomasz Kane)
September 30, 2021, 7:22am
7
It’s something like this:
<?php
namespace alterpage\application\traits;
use alterpage\helpers\ArrayHelper;
use yii\base\InvalidConfigException;
use yii\caching\DbDependency;
use yii\caching\Dependency;
use yii\db\ActiveRecord;
use yii\db\ExpressionInterface;
trait ModelMapTrait
{
protected static $modelMapValueColumn;
/**
* @param string|\Closure $valueColumn
* @param string|array|ExpressionInterface $whereCondition
*
* @return array
*
* @throws InvalidConfigException
*/
public static function getMap($valueColumn = null, $whereCondition = null): array
{
if (is_null($valueColumn)) {
$valueColumn = static::getModelMapValueColumn();
}
$primaryKey = current(self::primaryKey());
$query = self::find();
if ($whereCondition) {
$query->where($whereCondition);
}
$array = ArrayHelper::map(
$query
->asArray()
->cache(static::getModelMapCacheTime(), static::getModelMapCacheDependency())
->all(),
$primaryKey,
$valueColumn
);
return $array;
}
/**
* @return int Cache time in seconds
*/
protected static function getModelMapCacheTime(): int
{
return 3600;
}
/**
* @return string|\Closure "Name" column
*
* @throws InvalidConfigException
*/
protected static function getModelMapValueColumn()
{
if (empty(self::$modelMapValueColumn)) {
/** @var ActiveRecord $model */
$model = new self;
if ($model->hasAttribute('system_name')) {
self::$modelMapValueColumn = 'system_name';
} elseif ($model->hasAttribute('name')) {
self::$modelMapValueColumn = 'name';
} elseif ($model->hasAttribute('title')) {
self::$modelMapValueColumn = 'title';
} else {
throw new InvalidConfigException('ModelMapValueColumn was not configured.');
}
}
return self::$modelMapValueColumn;
}
protected static function getModelMapCacheDependency(): Dependency
{
return new DbDependency(['sql' => 'SELECT max(updated_at) FROM ' . self::tableName()]);
}
}
In ActiveRecord model:
class Product extends ActiveRecord
{
use ModelMapTrait;
}
Usage:
Product::getMap()
If needs override in Product model cache time, dependency or $modelMapValueColumn.
99,5% of my models work with it.
5 Likes
softark
(Softark)
September 30, 2021, 8:12am
8
@TomaszKane Nice! Well thought trait. Thank you for sharing it.
GSTAR1
October 15, 2021, 6:06pm
9
This is the best method:
Model:
public function getCategoryOptions()
{
return ArrayHelper::map(Category::find()->all(), 'id', 'name');
}
Controller:
public function actionCreate()
{
$model = new Product();
$categories = $model->getCategoryOptions();
return $this->render('create', [
'model' => $model,
'categories' => $categories,
]);
}
View:
<?= $form->field($model, 'category_id')->dropDownList($categories, ['prompt' => 'Select...']); ?>
You should only be passing in data to your view file, you should not be making any model calls from your view file. That way, when you decide to swap your view layer to something different, or if you want to return JSON, then you already have all the data you need.
2 Likes
Your best method required map method which is not needed for normal queries. What i need to do if i want to use this method outside Product
model? Create object of product model there?
Maudit
(Maudit)
October 18, 2021, 8:36am
11
Thanks everyone for the answers!
I have adopted the following solution:
Category.php
public static function getOptions()
{
return self::find()->select('name')->indexBy('id')->column();
}
ItemController.php
public function actionCreate()
{
...
$categoryOptions = Category::getOptions();
...
return $this->render('create', [
'model' => $model,
'categoryOptions' => $categoryOptions,
]);
}
_form.php
<?= $form->field($model, 'category_id')->dropDownList($categoryOptions, ['prompt' => 'Select...']); ?>
1 Like