Эволюция кода

Один из самых приятных в программировании (лично для меня) моментов — это когда тяжелый и запутанный код становится прозрачным. В одном из текущих проектов я отказался от паттерна Active Record в пользу паттерна Data Mapper.

Вот как выглядел один из методов контроллера:

public function actionIncludedUnits($id)
{
    $unitsTable        = \app\models\Unit::tableName();
    $unitsBundlesTable = \app\models\UnitsBundle::tableName();
    $unitsGroupsTable  = \app\models\unitsGroups\UnitsGroup::tableName();
    $tangiblesTable    = \app\models\tangibles\Tangible::tableName();

    $query = "SELECT u.id unitId,
                     ug.id unitsGroupId,
                     ug.division_slug slug,
                     ug.group_num l, 
                     u.group_num r, 
                     t.name tangibleName
                FROM {$unitsBundlesTable} ub
           LEFT JOIN {$unitsTable} u ON u.bundle_of_units_id = ub.id
           LEFT JOIN {$unitsGroupsTable} ug ON u.units_group_id = ug.id
           LEFT JOIN {$tangiblesTable} t ON u.tangible_id = t.id
               WHERE ub.complex_unit_id = :id";

    $raw = Yii::$app->db->createCommand($query)
        ->bindValue(':id', $id)
        ->queryAll();

    $res = [];

    foreach ($raw as $item) {
        $res[$item['unitId']] = [
            'unitsGroupNumber' => sprintf('%02d/%02d', $item['l'], $item['r']),
            'tangibleName' => $item['tangibleName'],
            'unitUrl' => \yii\helpers\Url::to([
                'units/view', 
                'slug' => $item['slug'], 
                'group' => $item['unitsGroupId'], 
                'id' => $item['unitId']
            ]),
        ];
    }

    return (\yii\helpers\Json::encode($res));
}

Вот как он выглядит сейчас:

public function actionIncludedUnits($id)
{
    $units = (new mappers\UnitMapper)->search([
        'Unit' => [
            'complexUnitId' => $id,
        ],
    ]);

    $res = [];

    foreach ($units->getModels() as $unit)
        $res[$unit->id] = [
            'unitsGroupNumber' => $unit->spoNumberOutput(),
            'tangibleName'     => $unit->tangible->name,
            'categoryName'     => $unit->tangible->category->name,
            'unitUrl'          => \yii\helpers\Url::to([
                'units/view', 
                'slug'  => $unit->unitsGroup->divisionSlug, 
                'group' => $unit->unitsGroup->id, 
                'id'    => $unit->id,
            ]),
        ];

    return \yii\helpers\Json::encode($res);
}

Здесь, конечно, вся работа лежит под капотом, и все же понять, что происходит теперь гораздо проще, а сам код стал элегантнее.

Изменить шаблон (лейаут) для части сайта в Yii2

В Yii2 все страницы отрисовываются внутри базового шаблона (лейаута) — app\views\layouts\main.php. Однако, можно оторвать как контроллер, так и метод от шаблона.

Чтобы все методы контроллера использовали отдельный шаблон, необходимо явно задать в контроллере атрибут $layout:

class SomeController extends Controller
{
    public $layout = 'another-layout';
    /* остальной код контроллера */
}

Если вместо имени шаблона передать false, то шаблон не будет применён ни к одному действию контроллера. Значение по умолчанию — null — используется для наследования шаблона модуля (документация).

Если нужно отвязать от базового шаблона отдельное действие контроллера, то меняем

$this->render(['view' compact('foo', 'bar')])

на

$this->renderPartial(['view' compact('foo', 'bar')])

renderPartial() отрисует переданные данные, не добавляя их в шаблон проекта.

Создание массивов для форм в Yii2

При генерации форм в Yii2 для выпадающих списков и списков чекбоксов нужны массивы данных в виде пар [ключ => значение]. Подготовить такие массивы можно либо исключительно силами ORM:

use app\models\Brand;

$brands = Brand::find()
    ->select('brand')
    ->orderBy('brand')
    ->indexBy('id')
    ->column();

либо при помощи класса-помощника ArrayHelper:

use app\models\Brand;
use yii\helpers\ArrayHelper;

$brands = ArrayHelper::map(Brand::findAll(), 'id', 'brand');

Добавляем в ActiveForm выпадающий список:

$form->field($model, 'id_brand')
    ->label('Бренд')
    ->dropdownList($brands);

Список чекбоксов:

Html::activeCheckboxList($model, 'id_brand', $brands);