Модели и ActiveRecord в Yii 2: короткое руководство
Как в Yii 2 связать таблицу базы с классом, читать и сохранять данные через ActiveRecord и не наступить на типичные грабли валидации. Займёт около часа с нуля.

В конце вы получите рабочий класс модели, привязанный к таблице MySQL, с чтением, сохранением и валидацией записей через ActiveRecord. Всё на Yii 2.0.x — если у вас Yii 1.1, синтаксис отличается принципиально, эта инструкция не подойдёт. На разбор с нуля уйдёт примерно час, если приложение уже развёрнуто через yii2-app-basic или yii2-app-advanced.
Что нужно иметь до начала
- PHP 7.4 или новее и установленный Composer;
- развёрнутое приложение Yii 2.0.x (например, шаблон
basic); - настроенное подключение к базе в
config/db.php— компонентdbс DSN, логином и паролем; - таблицу в базе, к которой будем цепляться. В примерах — таблица
customer.
Если подключение к базе ещё не проверено, откройте консоль в корне проекта и выполните ./yii migrate — если команда не ругается на соединение, компонент db настроен верно.
ActiveRecord в Yii — это не «магия ORM», а тонкая обёртка над таблицей: один класс — одна таблица, один объект — одна строка. Как только вы держите это в голове, поведение перестаёт удивлять.
Шаг 1. Готовим таблицу
Для примера таблица customer. Создать её проще всего миграцией. Сгенерируйте заготовку:
./yii migrate/create create_customer_table
В созданном файле в каталоге migrations/ опишите структуру. Метод safeUp выполняется в транзакции:
<?php
use yii\db\Migration;
class m240101_000000_create_customer_table extends Migration
{
public function safeUp()
{
$this->createTable('customer', [
'id' => $this->primaryKey(),
'name' => $this->string(255)->notNull(),
'email' => $this->string(255)->notNull()->unique(),
'created_at' => $this->integer(),
]);
}
public function safeDown()
{
$this->dropTable('customer');
}
}
Примените:
./yii migrate
Проверка: в базе появилась таблица customer с четырьмя столбцами, консоль вывела строку *** applied m240101_000000_create_customer_table.
Шаг 2. Создаём класс модели
Модель ActiveRecord наследуется от yii\db\ActiveRecord. Положите файл в каталог models/. Минимум, который нужен, — указать, к какой таблице привязан класс, через статический метод tableName():
<?php
namespace app\models;
use yii\db\ActiveRecord;
class Customer extends ActiveRecord
{
// Явно возвращаем имя таблицы
public static function tableName()
{
return 'customer';
}
}
Если метод tableName() не переопределить, Yii выведет имя таблицы из имени класса по своим правилам — для класса Customer это будет customer. Но лучше указывать явно: имена классов и таблиц совпадают не всегда, а неявное поведение потом трудно искать.
Атрибуты (столбцы) объявлять как свойства класса не нужно — ActiveRecord подхватывает их из схемы таблицы автоматически. Обращение идёт через $customer->name, хотя такого свойства в классе нет.
Шаг 3. Читаем данные
Чтение строится через find(), который возвращает объект запроса ActiveQuery. Дальше — цепочка методов и один из терминальных вызовов.
use app\models\Customer;
// Одна запись по первичному ключу
$customer = Customer::findOne(1);
// Одна запись по условию
$customer = Customer::findOne(['email' => 'ann@example.com']);
// Несколько записей с условием и сортировкой
$customers = Customer::find()
->where(['>', 'id', 10])
->orderBy(['name' => SORT_ASC])
->all();
// Количество без выборки строк
$count = Customer::find()->where(['name' => 'Анна'])->count();
Проверка: findOne() вернёт объект Customer или null, если строки нет. all() вернёт массив объектов (пустой, если ничего не нашлось). Обратите внимание: findOne(1) и findOne(['email' => ...]) — разные вещи, число трактуется как значение первичного ключа.
Шаг 4. Сохраняем и обновляем
Новая запись — это новый объект. Заполняете атрибуты и вызываете save():
$customer = new Customer();
$customer->name = 'Анна';
$customer->email = 'ann@example.com';
$customer->created_at = time();
if ($customer->save()) {
// $customer->id уже заполнен значением из базы
echo 'Сохранено, id = ' . $customer->id;
} else {
// save() вернул false — смотрим, что не прошло валидацию
var_dump($customer->getErrors());
}
Обновление отличается только тем, что объект вы сначала достаёте из базы:
$customer = Customer::findOne(1);
$customer->name = 'Анна Петрова';
$customer->save(); // Yii сам поймёт, что это UPDATE, а не INSERT
// Удаление
$customer->delete();
Проверка: после save() у новой записи заполняется id. Метод возвращает true при успехе и false, если запись не прошла валидацию — молча ничего не теряется, ошибки лежат в getErrors().
Один нюанс, который экономит время: save() по умолчанию сначала прогоняет валидацию. Если правил валидации нет, а столбец в базе NOT NULL и вы его не заполнили — упадёт уже сама база на уровне SQL. Поэтому правила лучше описать.
Шаг 5. Добавляем правила валидации
Правила задаются методом rules() — он возвращает массив. Каждое правило: список атрибутов, имя валидатора и параметры.
class Customer extends ActiveRecord
{
public static function tableName()
{
return 'customer';
}
public function rules()
{
return [
// name и email обязательны
[['name', 'email'], 'required'],
// email должен быть корректным адресом
['email', 'email'],
// длина строк не больше 255
[['name', 'email'], 'string', 'max' => 255],
// email уникален в пределах таблицы
['email', 'unique'],
// created_at — целое число
['created_at', 'integer'],
];
}
}
Теперь при save() с пустым email метод вернёт false, а в getErrors()['email'] будет текст «Email cannot be blank.». База не тронется.
Валидатор unique здесь бьётся именно с ActiveRecord: он делает запрос к таблице и проверяет, нет ли уже такого значения. Для форм это удобнее, чем ловить ошибку уникального индекса из SQL.
Если не получилось
- Unknown property: app\models\Customer::name — Yii не видит столбец. Чаще всего таблица не та: проверьте
tableName()и что миграция реально применена (./yii migrate/history). - The table does not exist: customer — приложение смотрит в другую базу или в базе нет таблицы. Сверьте DSN в
config/db.phpс той базой, где создали таблицу. - save() возвращает false, но getErrors() пустой — сработал
beforeSaveили транзакция откатилась не по валидации. Оберните в try/catch и посмотрите исключение; частая причина — падение на уровне SQL из-заNOT NULLбез правила. - SQLSTATE[23000]: Integrity constraint violation — база отклонила запись по уникальному индексу или внешнему ключу. Добавьте правило
unique, чтобы ловить это до похода в базу. - Изменения не сохраняются, ошибок нет — вы вызвали
save(false)и пропустили валидацию, либо не присвоили атрибут (опечатка в имени столбца тихо создаёт «виртуальное» свойство). Сверьте имена.
Что делать дальше
Когда базовая модель работает, следующий разумный шаг — связи между таблицами через hasOne() и hasMany(): это отдельная тема, здесь мы её не разворачивали. Также посмотрите генератор Gii (./yii gii или веб-интерфейс): он умеет собрать класс модели по существующей таблице сразу с правилами и метками атрибутов, чтобы не писать rules() руками. Для форм пригодится связка модели со сценариями валидации — но начинайте с одной модели и одной таблицы, пока поведение find/save не станет предсказуемым.
Репетитор 1—11 класс и ВУЗ: Математика и физика методом наведения на мысль, помощь с ДЗ.
Спросить за 8 ₽Источники: Yii 2 Guide — Active Record, Yii 2 Guide — Validating Input
Частые вопросы
Я на Yii 1.1, этот код сработает?
Обязательно ли переопределять tableName()?
Почему save() вернул false, но ничего не записал и не выдал ошибку?
Как обновить сразу много строк без загрузки каждой в объект?
Валидатор unique гарантирует, что дубля не будет?
Нужно ли объявлять свойства-столбцы в классе модели?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.