Новости YouTube. Июль — Ноябрь 2025 года
Обновления YouTube (Июль 2025)
Вкладка "Идеи для видео" в Творческой студии YouTube теперь доступна во всех странах.
Вкладка "Идеи для видео" находится на странице "Контент" в Творческой студии YouTube при входе с компьютера. На ней собраны предложения для новых видео от искусственного интеллекта. Подробнее…
Обновления YouTube (Сентябрь 2025)
Подборки лучших моментов трансляций с мобильных устройств в Прямых трансляциях YouTube.
После завершения трансляции в YouTube система будет автоматически создавать Shorts с лучшими моментами. Это позволит авторам быстро загружать такие подборки на канал. Подробнее о том, кому будет доступна функция…
Новая функция "Коллаборации" в Творческой студии YouTube.
В Творческой студии YouTube для видео можно добавлять до пяти соавторов. Их имена и кнопки "Подписаться" для их каналов появятся, когда они примут приглашение. Подробнее…
Обновление мобильного приложения "Творческая студия".
С 3 сентября 2025 года YouTube больше не поддерживает мобильное приложение "Творческая студия" версии 23.32 и ниже. Скачайте обновление из Google Play или App Store.
Обновления YouTube (Октябрь 2025)
Редактировать Shorts в YouTube стало ещё проще.
Теперь при работе с клипами можно сразу видеть, как они сочетаются с другими элементами – аудио, видео и оверлеями. Чтобы точнее смонтировать ролик, можно менять масштаб и привязывать клипы к нужным точкам, переставлять или удалять их, добавлять музыку или текст с привязкой ко времени и просматривать результат. Всё это поможет лучше склепать залипательные шорты.
Это только первые из множества запланированных улучшений, которые упростят создание Shorts в приложении. Новые возможности уже работают на устройствах Android и в ближайшие недели станут доступны всем авторам. Подробнее…
Прямые трансляции в горизонтальном и вертикальном форматах одновременно.
В YouTube были добавены настройки, которые позволяют проводить трансляции в двух форматах одновременно. Если включить эту функцию, из горизонтальной трансляции будет создаваться вертикальная, которая будет показываться в ленте Shorts в мобильном приложении YouTube. В ближайшие недели функция станет доступна всем авторам. В будущем планируется больше вариантов кадрирования и совместимость со внешними видеокодерами для вертикальных трансляций.
Вкладка "Авторское право" в Творческой студии YouTube переименована в "Обнаружение контента".
С 21 октября 2025 г. вкладка "Авторское право" в меню слева в Творческой студии YouTube будет называться "Обнаружение контента". На ней находится уже знакомый Инструмент поиска совпадений, а также новая функция определения сходства (доступна не всем авторам).
Обновления YouTube (Ноябрь 2025)
Функция "Упомянутые или показанные места" для YouTube Shorts.
Теперь в описании Shorts зрители могут посмотреть, какие места на карте YouTube или автор отметили в видео. Это поможет привлекать пользователей, которые ищут Shorts об определенных местах. Теоретически можно изменять или отключать автоматически добавленные геотеги в Творческой студии. Подробнее…
Комментарии к видео YouTube только для подписчиков канала.
Теперь авторы могут разрешить комментарии к отдельным видео и Shorts только своим подписчикам и спонсорам канала. Раньше такие ограничения были доступны только в чатах трансляций. Также можно установить минимальный срок подписки, после которого можно писать комментарии. Все это помогает авторам лучше контролировать, кто может комментировать их контент. Подробнее…
Варианты ответов на комментарии теперь доступны всем авторам на YouTube.
Эта улучшенная с помощью ИИ функция YouTube предлагает формулировки в вашем стиле и помогает быстрее отвечать зрителям. Подробнее…
Сводка комментариев в Творческой студии YouTube.
В мобильном приложении "Творческая студии YouTube" появилась сводка комментариев. Она автоматически определяет темы, которые обсуждают зрители, и позволяет быстро понять, что они думают о видео. Подробнее…
По материалам:
https://support.google.com/youtube/answer/9072033
Yii 2 Active Record: валидация, массовое присваивание, кастомизация запросов, получение дополнительных полей и счётчики
- Информация о материале
- Родительская категория: Полное руководство по Yii 2.0
- Категория: Работа с базами данных
- Active Record
- Объявление классов Active Record
- Подключение к базам данных
- Получение данных
- Доступ к данным
- Сохранение данных
- Удаление данных
- Жизненные циклы Active Record
- Работа с транзакциями
- Оптимистическая блокировка
- Работа со связными данными
- Сохранение связных данных
- Связывание объектов из разных баз данных
- Тонкая настройка классов Query
- Получение дополнительных атрибутов
Active Record
Active Record обеспечивает объектно-ориентированный интерфейс для доступа и манипулирования данными, хранящимися в базах данных. Класс Active Record соответствует таблице в базе данных, объект Active Record соответствует строке этой таблицы, а атрибут объекта Active Record представляет собой значение отдельного столбца строки. Вместо непосредственного написания SQL-выражений вы сможете получать доступ к атрибутам Active Record и вызывать методы Active Record для доступа и манипулирования данными, хранящимися в таблицах базы данных.
Для примера предположим, что Customer - это класс Active Record, который сопоставлен с таблицей customer, а name - столбец в таблице customer. Тогда вы можете написать следующий код для вставки новой строки в таблицу customer:
$customer = new Customer();
$customer->name = 'Qiang';
$customer->save();
Вышеприведённый код аналогичен использованию следующего SQL-выражения в MySQL, которое менее интуитивно, потенциально может вызвать ошибки и даже проблемы совместимости, если вы используете различные виды баз данных:
$db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [
':name' => 'Qiang',
])->execute();
Yii поддерживает работу с Active Record для следующих реляционных баз данных:
- MySQL 4.1 и выше: посредством [[yii\db\ActiveRecord]]
- PostgreSQL 7.3 и выше: посредством [[yii\db\ActiveRecord]]
- SQLite 2 и 3: посредством [[yii\db\ActiveRecord]]
- Microsoft SQL Server 2008 и выше: посредством [[yii\db\ActiveRecord]]
- Oracle: посредством [[yii\db\ActiveRecord]]
- CUBRID 9.3 и выше: посредством [[yii\db\ActiveRecord]] (Имейте ввиду, что вследствие бага в PDO-расширении для CUBRID, заключение значений в кавычки не работает, поэтому необходимо использовать CUBRID версии 9.3 как на клиентской стороне, так и на сервере)
- Sphinx: посредством [[yii\sphinx\ActiveRecord]], потребуется расширение
yii2-sphinx - ElasticSearch: посредством [[yii\elasticsearch\ActiveRecord]], потребуется расширение
yii2-elasticsearch
Кроме того Yii поддерживает использование Active Record со следующими NoSQL базами данных:
- Redis 2.6.12 и выше: посредством [[yii\redis\ActiveRecord]], потребуется расширение
yii2-redis - MongoDB 1.3.0 и выше: посредством [[yii\mongodb\ActiveRecord]], потребуется расширение
yii2-mongodb
В этом руководстве мы в основном будем описывать использование Active Record для реляционных баз данных. Однако большая часть этого материала также применима при использовании Active Record с NoSQL базами данных.
Объявление классов Active Record
Для начала объявите свой собственный класс, унаследовав класс [[yii\db\ActiveRecord]].
Настройка имени таблицы
По умолчанию каждый класс Active Record ассоциирован с таблицей в базе данных. Метод [[yii\db\ActiveRecord::tableName()|tableName()]] получает имя таблицы из имени класса с помощью [[yii\helpers\Inflector::camel2id()]]. Если таблица не названа соответственно, вы можете переопределить данный метод.
Также может быть применён [[yii\db\Connection::$tablePrefix|tablePrefix]] по умолчанию. Например, если [[yii\db\Connection::$tablePrefix|tablePrefix]] задан как tbl_, Customer преобразуется в tbl_customer, а OrderItem в tbl_order_item.
Если имя таблицы указано в формате {{%TableName}}, символ % заменяется префиксом. Например, , {{%post}} становится {{tbl_post}}. Фигуриные скобки используются для экранирования в SQL-запросах.
В нижеследующем примере мы объявляем класс Active Record с названием Customer для таблицы customer.
namespace app\models;
use yii\db\ActiveRecord;
class Customer extends ActiveRecord
{
const STATUS_INACTIVE = 0;
const STATUS_ACTIVE = 1;
/**
* @return string название таблицы, сопоставленной с этим ActiveRecord-классом.
*/
public static function tableName()
{
return '{{customer}}';
}
}
Классы Active record называются "моделями"
Объекты Active Record являются моделями. Именно поэтому мы обычно задаём классам Active Record пространство имён app\models (или другое пространство имён, предназначенное для моделей).
Т.к. класс [[yii\db\ActiveRecord]] наследует класс [[yii\base\Model]], он обладает всеми возможностями моделей, такими как атрибуты, правила валидации, способы сериализации данных и т.д.
Подключение к базам данных
По умолчанию Active Record для доступа и манипулирования данными БД использует компонент приложения db в качестве компонента [[yii\db\Connection|DB connection]]. Как сказано в разделе Объекты доступа к данным (DAO), вы можете настраивать компонент db на уровне конфигурации приложения как показано ниже:
return [
'components' => [
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=testdb',
'username' => 'demo',
'password' => 'demo',
],
],
];
Если вы хотите использовать для подключения к базе данных другой компонент подключения, отличный от db, вам нужно переопределить метод [[yii\db\ActiveRecord::getDb()|getDb()]]:
class Customer extends ActiveRecord
{
// ...
public static function getDb()
{
// использовать компонент приложения "db2"
return \Yii::$app->db2;
}
}
Получение данных
После объявления класса Active Record вы можете использовать его для получения данных из соответствующей таблицы базы данных. Этот процесс, как правило, состоит из следующих трёх шагов:
- Создать новый объект запроса вызовом метода [[yii\db\ActiveRecord::find()]];
- Настроить объект запроса вызовом методов построения запросов;
- Вызвать один из методов получения данных для извлечения данных в виде объектов Active Record.
Как вы могли заметить, эти шаги очень похожи на работу с построителем запросов. Различие лишь в том, что для создания объекта запроса вместо оператора new используется метод [[yii\db\ActiveRecord::find()]], возвращающий новый объект запроса, являющийся представителем класса [[yii\db\ActiveQuery]].
Ниже приведено несколько примеров использования Active Query для получения данных:
// возвращает покупателя с идентификатором 123
// SELECT * FROM `customer` WHERE `id` = 123
$customer = Customer::find()
->where(['id' => 123])
->one();
// возвращает всех активных покупателей, сортируя их по идентификаторам
// SELECT * FROM `customer` WHERE `status` = 1 ORDER BY `id`
$customers = Customer::find()
->where(['status' => Customer::STATUS_ACTIVE])
->orderBy('id')
->all();
// возвращает количество активных покупателей
// SELECT COUNT(*) FROM `customer` WHERE `status` = 1
$count = Customer::find()
->where(['status' => Customer::STATUS_ACTIVE])
->count();
// возвращает всех покупателей массивом, индексированным их идентификаторами
// SELECT * FROM `customer`
$customers = Customer::find()
->indexBy('id')
->all();
В примерах выше $customer - это объект класса Customer, в то время как $customers - это массив таких объектов. Все эти объекты заполнены данными таблицы customer.
Info: Т.к. класс [[yii\db\ActiveQuery]] наследует [[yii\db\Query]], вы можете использовать в нём все методы построения запросов и все методы класса Query как описано в разделе Построитель запросов.
Т.к. извлечение данных по первичному ключу или значениям отдельных столбцов достаточно распространённая задача, Yii предоставляет два коротких метода для её решения:
- [[yii\db\ActiveRecord::findOne()]]: возвращает один объект Active Record, заполненный первой строкой результата запроса.
- [[yii\db\ActiveRecord::findAll()]]: возвращает массив объектов Active Record, заполненных всеми полученными результатами запроса.
Оба метода могут принимать параметры в одном из следующих форматов:
- скалярное значение: значение интерпретируется как первичный ключ, по которому следует искать. Yii прочитает информацию о структуре базы данных и автоматически определит, какой столбец таблицы содержит первичные ключи.
- массив скалярных значений: массив интерпретируется как набор первичных ключей, по которым следует искать.
- ассоциативный массив: ключи массива интерпретируются как названия столбцов, а значения - как содержимое столбцов, которое следует искать. За подробностями вы можете обратиться к разделу Hash Format
Нижеследующий код демонстрирует, каким образом эти методы могут быть использованы:
// возвращает покупателя с идентификатором 123
// SELECT * FROM `customer` WHERE `id` = 123
$customer = Customer::findOne(123);
// возвращает покупателей с идентификаторами 100, 101, 123 и 124
// SELECT * FROM `customer` WHERE `id` IN (100, 101, 123, 124)
$customers = Customer::findAll([100, 101, 123, 124]);
// возвращает активного покупателя с идентификатором 123
// SELECT * FROM `customer` WHERE `id` = 123 AND `status` = 1
$customer = Customer::findOne([
'id' => 123,
'status' => Customer::STATUS_ACTIVE,
]);
// возвращает всех неактивных покупателей
// SELECT * FROM `customer` WHERE `status` = 0
$customers = Customer::findAll([
'status' => Customer::STATUS_INACTIVE,
]);
Warning: Если вам нужно передать в эти методы данные, полученные от пользователя, убедитесь что передаваемое значение – это скаляр, а если необходимо указать условия в формате массива – убедитесь, что пользовательские данные не могут изменить структуру этого массива.
// yii\web\Controller гарантирует, что $id будет скаляром public function actionView($id) { $model = Post::findOne($id); // ... } // явное указание имени столбца для поиска гарантирует поиск по столбцу `id`, // и возвращение одной записи как для массива, так и для скаляра в принятом от пользователя поле `id` $model = Post::findOne(['id' => Yii::$app->request->get('id')]); // НЕ используйте этот код! Пользователь может передать в параметр `id` массив // и осуществить поиск по имени столбца, которое не должно быть использовано для поиска по логике вашего приложения. $model = Post::findOne(Yii::$app->request->get('id'));
Note: Ни метод [[yii\db\ActiveRecord::findOne()]], ни [[yii\db\ActiveQuery::one()]] не добавляет условие
LIMIT 1к генерируемым SQL-запросам. Если ваш запрос может вернуть много строк данных, вы должны вызвать методlimit(1)явно в целях улучшения производительности, например:Customer::find()->limit(1)->one().
Помимо использования методов построения запросов вы можете также писать запросы на "чистом" SQL для получения данных и заполнения ими объектов Active Record. Вы можете делать это посредством метода [[yii\db\ActiveRecord::findBySql()]]:
// возвращает всех неактивных покупателей
$sql = 'SELECT * FROM customer WHERE status=:status';
$customers = Customer::findBySql($sql, [':status' => Customer::STATUS_INACTIVE])->all();
Не используйте дополнительные методы построения запросов после вызова метода [[yii\db\ActiveRecord::findBySql()|findBySql()]], т.к. они будут проигнорированы.
Доступ к данным
Как сказано выше, получаемые из базы данные заполняют объекты Active Record и каждая строка результата запроса соответствует одному объекту Active Record. Вы можете получить доступ к значениям столбцов с помощью атрибутов этих объектов. Например так:
// "id" и "email" - названия столбцов в таблице "customer"
$customer = Customer::findOne(123);
$id = $customer->id;
$email = $customer->email;
Note: Атрибуты объекта Active Record названы в соответствии с названиями столбцов связной таблицы с учётом регистра. Yii автоматически объявляет для каждого столбца связной таблицы атрибут в Active Record. Вы НЕ должны переопределять какие-либо из этих атрибутов.
Атрибуты Active Record названы в соответствии с именами столбцов таблицы. Если столбцы вашей таблицы именуются через нижнее подчёркивание, то может оказаться, что вам придётся писать PHP-код вроде этого: $customer->first_name - в нём будет использоваться нижнее подчёркивание для разделения слов в названиях атрибутов. Если вы обеспокоены единообразием стиля кодирования, вам придётся переименовать столбцы вашей таблицы соответствующим образом (например, назвать столбцы в стиле camelCase).
Преобразование данных
Часто бывает так, что данные вводятся и/или отображаются в формате, который отличается от формата их хранения в базе данных. Например, в базе данных вы храните дни рождения покупателей в формате UNIX timestamp (что, кстати говоря, не является хорошим дизайном), в то время как во многих случаях вы хотите манипулировать днями рождения в виде строк формата 'ДД.ММ.ГГГГ'. Для достижения этой цели, вы можете объявить методы преобразования данных в ActiveRecord-классе Customer как показано ниже:
class Customer extends ActiveRecord
{
// ...
public function getBirthdayText()
{
return date('d.m.Y', $this->birthday);
}
public function setBirthdayText($value)
{
$this->birthday = strtotime($value);
}
}
Теперь в своём PHP коде вместо доступа к $customer->birthday, вы сможете получить доступ к $customer->birthdayText, что позволить вам вводить и отображать дни рождения покупателей в формате 'ДД.ММ.ГГГГ'.
Tip: Вышеприведённый пример демонстрирует общий способ преобразования данных в различные форматы. Если вы работаете с датами и временем, вы можете использовать DateValidator и [[yii\jui\DatePicker|DatePicker]], которые проще в использовании и являются более мощными инструментами.
Получение данных в виде массива
Несмотря на то, что получение данных в виде Active Record объектов является удобным и гибким, этот способ не всегда подходит при получении большого количества данных из-за больших накладных расходов памяти. В этом случае вы можете получить данные в виде PHP-массива, используя перед выполнением запроса метод [[yii\db\ActiveQuery::asArray()|asArray()]]:
// возвращает всех покупателей
// каждый покупатель будет представлен в виде ассоциативного массива
$customers = Customer::find()
->asArray()
->all();
Note: В то время как этот способ бережёт память и улучшает производительность, он ближе к низкому слою абстракции базы данных и вы потеряете многие возможности Active Record. Важное отличие заключается в типах данных значений столбцов. Когда вы получаете данные в виде объектов Active Record, значения столбцов автоматически приводятся к типам, соответствующим типам столбцов; с другой стороны, когда вы получаете данные в массивах, значения столбцов будут строковыми (до тех пор, пока они являются результатом работы PDO-слоя без какой-либо обработки), несмотря на настоящие типы данных соответствующих столбцов.
Пакетное получение данных
В главе Построитель запросов мы объясняли, что вы можете использовать пакетную выборку для снижения расходов памяти при получении большого количества данных из базы. Вы можете использовать такой же подход при работе с Active Record. Например:
// получить 10 покупателей одновременно
foreach (Customer::find()->batch(10) as $customers) {
// $customers - это массив, в котором находится 10 или меньше объектов класса Customer
}
// получить одновременно десять покупателей и перебрать их одного за другим
foreach (Customer::find()->each(10) as $customer) {
// $customer - это объект класса Customer
}
// пакетная выборка с жадной загрузкой
foreach (Customer::find()->with('orders')->each() as $customer) {
// $customer - это объект класса Customer
}
Сохранение данных
Используя Active Record, вы легко можете сохранить данные в базу данных, осуществив следующие шаги:
- Подготовьте объект Active Record;
- Присвойте новые значения атрибутам Active Record;
- Вызовите метод [[yii\db\ActiveRecord::save()]] для сохранения данных в базу данных.
Например:
// вставить новую строку данных
$customer = new Customer();
$customer->name = 'James';
$customer->email = 'james@example.com';
$customer->save();
// обновить имеющуюся строку данных
$customer = Customer::findOne(123);
$customer->email = 'james@newexample.com';
$customer->save();
Метод [[yii\db\ActiveRecord::save()|save()]] может вставить или обновить строку данных в зависимости от состояния Active Record объекта. Если объект создан с помощью оператора new, вызов метода [[yii\db\ActiveRecord::save()|save()]] приведёт к вставке новой строки данных; если же объект был получен с помощью запроса на получение данных, вызов [[yii\db\ActiveRecord::save()|save()]] обновит строку таблицы, соответствующую объекту Active Record.
Вы можете различать два состояния Active Record объекта с помощью проверки значения его свойства [[yii\db\ActiveRecord::isNewRecord|isNewRecord]]. Это свойство также используется внутри метода [[yii\db\ActiveRecord::save()|save()]] как показано ниже:
public function save($runValidation = true, $attributeNames = null)
{
if ($this->getIsNewRecord()) {
return $this->insert($runValidation, $attributeNames);
} else {
return $this->update($runValidation, $attributeNames) !== false;
}
}
Tip: Вы можете вызвать [[yii\db\ActiveRecord::insert()|insert()]] или [[yii\db\ActiveRecord::update()|update()]] непосредственно, чтобы вставить или обновить строку данных в таблице.
Валидация данных
Т.к. класс [[yii\db\ActiveRecord]] наследует класс [[yii\base\Model]], он обладает такими же возможностями валидации данных. Вы можете объявить правила валидации переопределив метод [[yii\db\ActiveRecord::rules()|rules()]] и осуществлять валидацию данных посредством вызовов метода [[yii\db\ActiveRecord::validate()|validate()]].
Когда вы вызываете метод [[yii\db\ActiveRecord::save()|save()]], по умолчанию он автоматически вызывает метод [[yii\db\ActiveRecord::validate()|validate()]]. Только после успешного прохождения валидации происходит сохранение данных; в ином случае метод [[yii\db\ActiveRecord::save()|save()]] просто возвращает false, и вы можете проверить свойство [[yii\db\ActiveRecord::errors|errors]] для получения сообщений об ошибках валидации.
Tip: Если вы уверены, что ваши данные не требуют валидации (например, данные пришли из доверенного источника), вы можете вызвать
save(false), чтобы пропустить валидацию.
Массовое присваивание
Как и обычные модели, объекты Active Record тоже обладают возможностью массового присваивания. Как будет показано ниже, используя эту возможность, вы можете одним PHP выражением присвоить значения множества атрибутов Active Record объекту. Запомните однако, что только безопасные атрибуты могут быть массово присвоены.
$values = [
'name' => 'James',
'email' => 'james@example.com',
];
$customer = new Customer();
$customer->attributes = $values;
$customer->save();
Обновление счётчиков
Распространённой задачей является инкремент или декремент столбца в таблице базы данных. Назовём такие столбцы столбцами-счётчиками. Вы можете использовать метод [[yii\db\ActiveRecord::updateCounters()|updateCounters()]] для обновления одного или нескольких столбцов-счётчиков. Например:
$post = Post::findOne(100);
// UPDATE `post` SET `view_count` = `view_count` + 1 WHERE `id` = 100
$post->updateCounters(['view_count' => 1]);
Note: Если вы используете метод [[yii\db\ActiveRecord::save()]] для обновления столбца-счётчика, вы можете прийти к некорректному результату, т.к. вполне вероятно, что этот же счётчик был сохранён сразу несколькими запросами, которые читают и записывают этот же столбец-счётчик.
Dirty-атрибуты
Когда вы вызываете [[yii\db\ActiveRecord::save()|save()]] для сохранения Active Record объекта, сохраняются только dirty-атрибуты. Атрибут считается dirty-атрибутом, если его значение было изменено после чтения из базы данных или же он был сохранён в базу данных совсем недавно. Заметьте, что валидация данных осуществляется независимо от того, имеются ли dirty-атрибуты в объекте Active Record или нет.
Active Record автоматически поддерживает список dirty-атрибутов. Это достигается за счёт хранения старых значений атрибутов и сравнения их с новыми. Вы можете вызвать метод [[yii\db\ActiveRecord::getDirtyAttributes()]] для получения текущего списка dirty-атрибутов. Вы также можете вызвать [[yii\db\ActiveRecord::markAttributeDirty()]], чтобы явно пометить атрибут в качестве dirty-атрибута.
Если вам нужны значения атрибутов, какими они были до их изменения, вы можете вызвать [[yii\db\ActiveRecord::getOldAttributes()|getOldAttributes()]] или [[yii\db\ActiveRecord::getOldAttribute()|getOldAttribute()]].
Note: Сравнение старых и новых значений будет осуществлено с помощью оператора
===, так что значение будет считаться dirty-значением даже в том случае, если оно осталось таким же, но изменило свой тип. Это часто происходит, когда модель получает пользовательский ввод из HTML-форм, где каждое значение представлено строкой. Чтобы убедиться в корректности типа данных, например для целых значений, вы можете применить фильтрацию данных:['attributeName', 'filter', 'filter' => 'intval'].
Значения атрибутов по умолчанию
Некоторые столбцы ваших таблиц могут иметь значения по умолчанию, объявленные в базе данных. Иногда вы можете захотеть предварительно заполнить этими значениями вашу веб-форму, которая соответствует Active Record объекту. Чтобы избежать повторного указания этих значений, вы можете вызвать метод [[yii\db\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] для заполнения соответствующих Active Record атрибутов значениями по умолчанию, объявленными в базе данных:
$customer = new Customer();
$customer->loadDefaultValues();
// $customer->xyz получит значение по умолчанию, которое было указано при объявлении столбца "xyz"
Приведение типов атрибутов
При заполнении результатами запроса [[yii\db\ActiveRecord]] производит автоматическое приведение типов для значений атрибутов на основе информации из схемы базы данных. Это позволяет данным, полученным из колонки таблицы объявленной как целое, заноситься в экземпляр ActiveRecord как значение целого типа PHP, булево как булево и т.д. Однако, механизм приведения типов имеет несколько ограничений:
- Числа с плавающей точкой не будут обработаны, а будут представленны как строки, в противном случае они могут потерять точность.
- Конвертация целых чисел зависит от разрядности используемой операционной системы. В частности: значения колонок, объявленных как 'unsigned integer' или 'big integer' будут приведены к целому типу PHP только на 64-х разрядных системах, в то время как на 32-х разрядных - они будут представленны как строки.
Имейте в виду, что преобразование типов производиться только в момент заполнения экземпляра ActiveRecord данными из результата запроса. При заполнении данных из HTTP запроса или непосредственно через механизм доступа к полям - автоматическая конвертация не производтся. Схема таблицы базы данных также используется при построении SQL запроса для сохранения данных ActiveRecord, обеспечивая соответсвие типов связываемых параметров в запросе. Однако, над атрибутами объекта ActiveRecord не будет производиться приведение типов в процессе сохранения.
Совет: вы можете использовать поведение [[yii\behaviors\AttributeTypecastBehavior]] для того, чтобы производить приведение типов для ActiveRecord во время валидации или сохранения.
Начиная с 2.0.14, Yii ActiveRecord поддерживает сложные типы данных, такие как JSON или многомерные массивы.
JSON в MySQL и PostgreSQL
После заполнения данных, значение из столбца JSON будет автоматически декодировано из JSON в соответствии со стандартными правилами декодирования JSON.
Чтобы сохранить значение атрибута в столбец JSON, ActiveRecord автоматически создаст объект [[yii\db\JsonExpression|JsonExpression]], который будет закодирован в строку JSON на уровне QueryBuilder.
Массивы в PostgreSQL
После заполнения данных значение из столбца Array будет автоматически декодировано из нотации PgSQL в объект [[yii\db\ArrayExpression|ArrayExpression]]. Он реализует интерфейс PHP ArrayAccess, так что вы можете использовать его в качестве массива, или вызвать ->getValue (), чтобы получить сам массив.
Чтобы сохранить значение атрибута в столбец массива, ActiveRecord автоматически создаст объект [[yii\db\Array Expression|ArrayExpression]], который будет закодирован QueryBuilder в строковое представление массива PgSQL.
Можно также использовать условия для столбцов JSON:
$query->andWhere(['=', 'json', new ArrayExpression(['foo' => 'bar'])
Дополнительные сведения о системе построения выражений см. Query Builder – добавление пользовательских условий и выражений
Обновление нескольких строк данных
Методы, представленные выше, работают с отдельными Active Record объектами, инициируя вставку или обновление данных для отдельной строки таблицы. Вместо них для обновления нескольких строк одновременно можно использовать метод [[yii\db\ActiveRecord::updateAll()|updateAll()]], который является статическим.
// UPDATE `customer` SET `status` = 1 WHERE `email` LIKE `%@example.com%`
Customer::updateAll(['status' => Customer::STATUS_ACTIVE], ['like', 'email', '@example.com']);
Подобным образом можно использовать метод [[yii\db\ActiveRecord::updateAllCounters()|updateAllCounters()]] для обновления значений столбцов-счётчиков в нескольких строках одновременно.
// UPDATE `customer` SET `age` = `age` + 1
Customer::updateAllCounters(['age' => 1]);
Удаление данных
Для удаления одной отдельной строки данных сначала получите Active Record объект, соответствующий этой строке, а затем вызовите метод [[yii\db\ActiveRecord::delete()]].
$customer = Customer::findOne(123);
$customer->delete();
Вы можете вызвать [[yii\db\ActiveRecord::deleteAll()]] для удаления всех или нескольких строк данных одновременно. Например:
Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]);
Note: будьте очень осторожны, используя метод [[yii\db\ActiveRecord::deleteAll()|deleteAll()]], потому что он может полностью удалить все данные из вашей таблицы, если вы сделаете ошибку при указании условий удаления.
Жизненные циклы Active Record
Важно понимать как устроены жизненные циклы Active Record при использовании Active Record для различных целей. В течение каждого жизненного цикла вызывается определённая последовательность методов, которые вы можете переопределять, чтобы получить возможность тонкой настройки жизненного цикла. Для встраивания своего кода вы также можете отвечать на конкретные события Active Record, которые срабатывают в течение жизненного цикла. Эти события особенно полезны, когда вы разрабатываете поведения, которые требуют тонкой настройки жизненных циклов Active Record.
Ниже мы подробно опишем различные жизненные циклы Active Record и методы/события, которые участвуют в жизненных циклах.
Жизненный цикл создания нового объекта
Когда создаётся новый объект Active Record с помощью оператора new, следующий жизненный цикл имеет место:
- Вызывается конструктор класса;
- Вызывается [[yii\db\ActiveRecord::init()|init()]]: инициируется событие [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]].
Жизненный цикл получения данных
Когда происходит получение данных посредством одного из методов получения данных, каждый вновь создаваемый объект Active Record при заполнении данными проходит следующий жизненный цикл:
- Вызывается конструктор класса.
- Вызывается [[yii\db\ActiveRecord::init()|init()]]: инициируется событие [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]].
- Вызывается [[yii\db\ActiveRecord::afterFind()|afterFind()]]: инициируется событие [[yii\db\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]].
Жизненный цикл сохранения данных
Когда вызывается метод [[yii\db\ActiveRecord::save()|save()]] для вставки или обновления объекта Active Record, следующий жизненный цикл имеет место:
- Вызывается [[yii\db\ActiveRecord::beforeValidate()|beforeValidate()]]: инициируется событие [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]]. Если метод возвращает
falseили свойство события [[yii\base\ModelEvent::isValid]] равноfalse, оставшиеся шаги не выполняются. - Осуществляется валидация данных. Если валидация закончилась неудачей, после 3-го шага остальные шаги не выполняются.
- Вызывается [[yii\db\ActiveRecord::afterValidate()|afterValidate()]]: инициируется событие [[yii\db\ActiveRecord::EVENT_AFTER_VALIDATE|EVENT_AFTER_VALIDATE]].
- Вызывается [[yii\db\ActiveRecord::beforeSave()|beforeSave()]]: инициируется событие [[yii\db\ActiveRecord::EVENT_BEFORE_INSERT|EVENT_BEFORE_INSERT]] или событие [[yii\db\ActiveRecord::EVENT_BEFORE_UPDATE|EVENT_BEFORE_UPDATE]]. Если метод возвращает
falseили свойство события [[yii\base\ModelEvent::isValid]] равноfalse, оставшиеся шаги не выполняются. - Осуществляется фактическая вставка или обновление данных в базу данных;
- Вызывается [[yii\db\ActiveRecord::afterSave()|afterSave()]]: инициируется событие [[yii\db\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] или событие [[yii\db\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]].
Жизненный цикл удаления данных
Когда вызывается метод [[yii\db\ActiveRecord::delete()|delete()]] для удаления объекта Active Record, следующий жизненный цикл имеет место:
- Вызывается [[yii\db\ActiveRecord::beforeDelete()|beforeDelete()]]: инициируется событие [[yii\db\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]]. Если метод возвращает
falseили свойство события [[yii\base\ModelEvent::isValid]] равноfalse, остальные шаги не выполняются. - Осуществляется фактическое удаление данных из базы данных.
- Вызывается [[yii\db\ActiveRecord::afterDelete()|afterDelete()]]: инициируется событие [[yii\db\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]].
Note: Вызов следующих методов НЕ инициирует ни один из вышеприведённых жизненных циклов:
- [[yii\db\ActiveRecord::updateAll()]]
- [[yii\db\ActiveRecord::deleteAll()]]
- [[yii\db\ActiveRecord::updateCounters()]]
- [[yii\db\ActiveRecord::updateAllCounters()]]
Работа с транзакциями
Есть два способа использования транзакций при работе с Active Record.
Первый способ заключается в том, чтобы явно заключить все вызовы методов Active Record в блок транзакции как показано ниже:
$customer = Customer::findOne(123);
Customer::getDb()->transaction(function($db) use ($customer) {
$customer->id = 200;
$customer->save();
// ...другие операции с базой данных...
});
// или по-другому
$transaction = Customer::getDb()->beginTransaction();
try {
$customer->id = 200;
$customer->save();
// ...другие операции с базой данных...
$transaction->commit();
} catch(\Exception $e) {
$transaction->rollBack();
throw $e;
} catch(\Throwable $e) {
$transaction->rollBack();
throw $e;
}
Note: в коде выше ради совместимости с PHP 5.x и PHP 7.x использованы два блока catch.
\Exceptionреализует интерфейс\Throwableinterface начиная с PHP 7.0. Если вы используете только PHP 7 и новее, можете пропустить блок с\Exception.
Второй способ заключается в том, чтобы перечислить операции с базой данных, которые требуют тразнакционного выполнения, в методе [[yii\db\ActiveRecord::transactions()]]. Например:
class Customer extends ActiveRecord
{
public function transactions()
{
return [
'admin' => self::OP_INSERT,
'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,
// вышеприведённая строка эквивалентна следующей:
// 'api' => self::OP_ALL,
];
}
}
Метод [[yii\db\ActiveRecord::transactions()]] должен возвращать массив, ключи которого являются именами сценариев, а значения соответствуют операциям, которые должны быть выполнены с помощью транзакций. Вы должны использовать следующие константы для обозначения различных операций базы данных:
- [[yii\db\ActiveRecord::OP_INSERT|OP_INSERT]]: операция вставки, осуществляемая с помощью метода [[yii\db\ActiveRecord::insert()|insert()]];
- [[yii\db\ActiveRecord::OP_UPDATE|OP_UPDATE]]: операция обновления, осуществляемая с помощью метода [[yii\db\ActiveRecord::update()|update()]];
- [[yii\db\ActiveRecord::OP_DELETE|OP_DELETE]]: операция удаления, осуществляемая с помощью метода [[yii\db\ActiveRecord::delete()|delete()]].
Используйте операторы | для объединения вышеприведённых констант при обозначении множества операций. Вы можете также использовать вспомогательную константу [[yii\db\ActiveRecord::OP_ALL|OP_ALL]], чтобы обозначить одной константой все три вышеприведённые операции.
Оптимистическая блокировка
Оптимистическая блокировка - это способ предотвращения конфликтов, которые могут возникать, когда одна и та же строка данных обновляется несколькими пользователями. Например, пользователь A и пользователь B одновременно редактируют одну и ту же wiki-статью. После того, как пользователь A сохранит свои изменения, пользователь B нажимает на кнопку "Сохранить" в попытке также сохранить свои изменения. Т.к. пользователь B работал с фактически-устаревшей версией статьи, было бы неплохо иметь способ предотвратить сохранение его варианта статьи и показать ему некоторое сообщение с подсказкой о том, что произошло.
Оптимистическая блокировка решает вышеприведённую проблему за счёт использования отдельного столбца для сохранения номера версии каждой строки данных. Когда строка данных сохраняется с использованием устаревшего номера версии, выбрасывается исключение [[yii\db\StaleObjectException]], которое предохраняет строку от сохранения. Оптимистическая блокировка поддерживается только тогда, когда вы обновляете или удаляете существующую строку данных, используя методы [[yii\db\ActiveRecord::update()]] или [[yii\db\ActiveRecord::delete()]] соответственно.
Для использования оптимистической блокировки:
- Создайте столбец в таблице базы данных, ассоциированной с классом Active Record, для сохранения номера версии каждой строки данных. Столбец должен быть типа big integer (в Mysql это будет
BIGINT DEFAULT 0). - Переопределите метод [[yii\db\ActiveRecord::optimisticLock()]] таким образом, чтобы он возвращал название этого столбца.
- В веб-форме, которая принимает пользовательский ввод, добавьте скрытое поле для сохранения текущей версии обновляемой строки. Убедитесь, что для вашего атрибута с версией объявлены правила валидации, и валидация проходит успешно.
- В действии контроллера, которое занимается обновлением строки данных с использованием Active Record, оберните в блок try...catch код и перехватывайте исключение [[yii\db\StaleObjectException]]. Реализуйте необходимую бизнес-логику (например, возможность слияния изменений, подсказку о том, что данные устарели) для разрешения возникшего конфликта.
Например, предположим, что столбец с версией называется version. Вы можете реализовать оптимистическую блокировку с помощью подобного кода:
// ------ код представления -------
use yii\helpers\Html;
// ...другие поля ввода
echo Html::activeHiddenInput($model, 'version');
// ------ код контроллера -------
use yii\db\StaleObjectException;
public function actionUpdate($id)
{
$model = $this->findModel($id);
try {
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
} catch (StaleObjectException $e) {
// логика разрешения конфликта версий
}
}
Работа со связными данными
Помимо работы с отдельными таблицами баз данных, Active Record также имеет возможность объединять связные данные, что делает их легко-доступными для получения через основные объекты данных. Например, данные покупателя связаны с данными заказов, потому что один покупатель может осуществить один или несколько заказов. С помощью объявления этой связи вы можете получить возможность доступа к информации о заказе покупателя с помощью выражения $customer->orders, которое возвращает информацию о заказе покупателя в виде массива объектов класса Order, которые являются Active Record объектами.
Объявление связей
Для работы со связными данными посредством Active Record вы прежде всего должны объявить связи в классе Active Record. Эта задача решается простым объявлением методов получения связных данных для каждой интересующей вас связи как показано ниже:
class Customer extends ActiveRecord
{
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
}
}
class Order extends ActiveRecord
{
public function getCustomer()
{
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
}
}
В вышеприведённом коде мы объявили связь orders для класса Customer и связь customer для класса Order.
Каждый метод получения связных данных должен быть назван в формате getXyz. Мы называем xyz (первая буква в нижнем регистре) именем связи. Помните, что имена связей чувствительны к регистру.
При объявлении связи, вы должны указать следующую информацию:
-
кратность связи: указывается с помощью вызова метода [[yii\db\ActiveRecord::hasMany()|hasMany()]] или метода [[yii\db\ActiveRecord::hasOne()|hasOne()]]. В вышеприведённом примере вы можете легко увидеть в объявлениях связей, что покупатель может иметь много заказов в то время, как заказ может быть сделан лишь одним покупателем.
-
название связного Active Record класса: указывается в качестве первого параметра для метода [[yii\db\ActiveRecord::hasMany()|hasMany()]] или для метода [[yii\db\ActiveRecord::hasOne()|hasOne()]]. Рекомендуется использовать код
Xyz::className(), чтобы получить строку с именем класса, при этом вы сможете воспользоваться возможностями авто-дополнения кода, встроенного в IDE, а также получите обработку ошибок на этапе компиляции. -
связь между двумя типами данных: указываются столбцы с помощью которых два типа данных связаны. Значения массива - это столбцы основного объекта данных (представлен классом Active Record, в котором объявляется связь), в то время как ключи массива - столбцы связанных данных.
Есть простой способ запомнить это правило: как вы можете увидеть в примере выше, столбец связной Active Record указывается сразу же после указания самого класса Active Record. Вы видите, что
customer_id- это свойство классаOrder, аid- свойство классаCustomer.
Доступ к связным данным
После объявления связей вы можете получать доступ к связным данным с помощью имён связей. Это происходит таким же образом, каким осуществляется доступ к свойству объекта объявленному с помощью метода получения связных данных. По этой причине, мы называем его свойством связи. Например:
// SELECT * FROM `customer` WHERE `id` = 123
$customer = Customer::findOne(123);
// SELECT * FROM `order` WHERE `customer_id` = 123
// $orders - это массив объектов Order
$orders = $customer->orders;
Info: когда вы объявляете связь с названием
xyzпосредством геттераgetXyz(), у вас появляется возможность доступа к свойствуxyzподобно свойству объекта. Помните, что название связи чувствительно к регистру.
Если связь объявлена с помощью метода [[yii\db\ActiveRecord::hasMany()|hasMany()]], доступ к свойству связи вернёт массив связных объектов Active Record; если связь объявлена с помощью метода [[yii\db\ActiveRecord::hasOne()|hasOne()]], доступ к свойству связи вернёт связный Active Record объект или null, если связные данные не найдены.
Когда вы запрашиваете свойство связи в первый раз, выполняется SQL-выражение как показано в примере выше. Если то же самое свойство запрашивается вновь, будет возвращён результат предыдущего SQL-запроса без повторного выполнения SQL-выражения. Для принудительного повторного выполнения SQL-запроса, вы можете удалить свойство связи с помощью операции: unset($customer->orders).
Note: Несмотря на то, что эта концепция выглядит похожей на концепцию свойств объектов, между ними есть важное различие. Для обычных свойств объектов значения свойств имеют тот же тип, который возвращает геттер. Однако метод получения связных данных возвращает объект [[yii\db\ActiveQuery]], в то время как доступ к свойству связи возвращает объект [[yii\db\ActiveRecord]] или массив таких объектов.
$customer->orders; // массив объектов `Order`
$customer->getOrders(); // объект ActiveQuery
Это полезно при тонкой настройке запросов к связным данным, что будет описано в следующем разделе.
Динамические запросы связных данных
Т.к. метод получения связных данных возвращает объект запроса [[yii\db\ActiveQuery]], вы можете в дальнейшем перед его отправкой в базу данных настроить этот запрос, используя методы построения запросов. Например:
$customer = Customer::findOne(123);
// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`
$orders = $customer->getOrders()
->where(['>', 'subtotal', 200])
->orderBy('id')
->all();
В отличие от доступа к данным с помощью свойства связи, каждый раз при выполнении такого динамического запроса посредством метода получения связных данных будет выполняться SQL-запрос, даже если тот же самый динамический запрос был отправлен ранее.
Иногда вы можете даже захотеть настроить объявление связи таким образом, чтобы вы могли более просто осуществлять динамические запросы связных данных. Например, вы можете объявить связь bigOrders как показано ниже:
class Customer extends ActiveRecord
{
public function getBigOrders($threshold = 100)
{
return $this->hasMany(Order::className(), ['customer_id' => 'id'])
->where('subtotal > :threshold', [':threshold' => $threshold])
->orderBy('id');
}
}
После этого вы сможете выполнять следующие запросы связных данных:
// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 200 ORDER BY `id`
$orders = $customer->getBigOrders(200)->all();
// SELECT * FROM `order` WHERE `customer_id` = 123 AND `subtotal` > 100 ORDER BY `id`
$orders = $customer->bigOrders;
Связывание посредством промежуточной таблицы
При проектировании баз данных, когда между двумя таблицами имеется кратность связи many-to-many, обычно вводится промежуточная таблица. Например, таблицы order и item могут быть связаны посредством промежуточной таблицы с названием order_item. Один заказ будет соотносится с несколькими товарами, в то время как один товар будет также соотноситься с несколькими заказами.
При объявлении подобных связей вы можете пользоваться методом [[yii\db\ActiveQuery::via()|via()]] или методом [[yii\db\ActiveQuery::viaTable()|viaTable()]] для указания промежуточной таблицы. Разница между методами [[yii\db\ActiveQuery::via()|via()]] и [[yii\db\ActiveQuery::viaTable()|viaTable()]] заключается в том, что первый метод указывает промежуточную таблицу с помощью названия связи, в то время как второй метод непосредственно указывает промежуточную таблицу. Например:
class Order extends ActiveRecord
{
public function getItems()
{
return $this->hasMany(Item::className(), ['id' => 'item_id'])
->viaTable('order_item', ['order_id' => 'id']);
}
}
или по-другому:
class Order extends ActiveRecord
{
public function getOrderItems()
{
return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
}
public function getItems()
{
return $this->hasMany(Item::className(), ['id' => 'item_id'])
->via('orderItems');
}
}
Использовать связи, объявленные с помощью промежуточных таблиц, можно точно также, как и обычные связи. Например:
// SELECT * FROM `order` WHERE `id` = 100
$order = Order::findOne(100);
// SELECT * FROM `order_item` WHERE `order_id` = 100
// SELECT * FROM `item` WHERE `item_id` IN (...)
// возвращает массив объектов Item
$items = $order->items;
Отложенная и жадная загрузка
В разделе Доступ к связным данным, мы показывали, что вы можете получать доступ к свойству связи объекта Active Record точно также, как получаете доступ к свойству обычного объекта. SQL-запрос будет выполнен только во время первого доступа к свойству связи. Мы называем подобный способ получения связных данных отложенной загрузкой. Например:
// SELECT * FROM `customer` WHERE `id` = 123
$customer = Customer::findOne(123);
// SELECT * FROM `order` WHERE `customer_id` = 123
$orders = $customer->orders;
// SQL-запрос не выполняется
$orders2 = $customer->orders;
Отложенная загрузка очень удобна в использовании. Однако этот метод может вызвать проблемы производительности, когда вам понадобится получить доступ к тем же самым свойствам связей для нескольких объектов Active Record. Рассмотрите следующий пример кода. Сколько SQL-запросов будет выполнено?
// SELECT * FROM `customer` LIMIT 100
$customers = Customer::find()->limit(100)->all();
foreach ($customers as $customer) {
// SELECT * FROM `order` WHERE `customer_id` = ...
$orders = $customer->orders;
}
Как вы могли заметить по вышеприведённым комментариям кода, будет выполнен 101 SQL-запрос! Это произойдёт из-за того, что каждый раз внутри цикла будет выполняться SQL-запрос при получении доступа к свойству связи orders каждого отдельного объекта Customer.
Для решения этой проблемы производительности вы можете, как показано ниже, использовать подход, который называется жадная загрузка:
// SELECT * FROM `customer` LIMIT 100;
// SELECT * FROM `orders` WHERE `customer_id` IN (...)
$customers = Customer::find()
->with('orders')
->limit(100)
->all();
foreach ($customers as $customer) {
// SQL-запрос не выполняется
$orders = $customer->orders;
}
Посредством вызова метода [[yii\db\ActiveQuery::with()]], вы указываете объекту Active Record вернуть заказы первых 100 покупателей с помощью одного SQL-запроса. В результате снижаете количество выполняемых SQL-запросов от 101 до 2!
Вы можете жадно загружать одну или несколько связей. Вы можете даже жадно загружать вложенные связи. Вложенная связь - это связь, которая объявлена внутри связного Active Record класса. Например, Customer связан с Order посредством связи orders, а Order связан с Item посредством связи items. При формировании запроса для Customer, вы можете жадно загрузить items, используя нотацию вложенной связи orders.items.
Ниже представлен код, который показывает различные способы использования метода [[yii\db\ActiveQuery::with()|with()]]. Мы полагаем, что класс Customer имеет две связи: orders и country - в то время как класс Order имеет лишь одну связь items.
// жадная загрузка "orders" и "country" одновременно
$customers = Customer::find()->with('orders', 'country')->all();
// аналог с использованием синтаксиса массива
$customers = Customer::find()->with(['orders', 'country'])->all();
// SQL-запрос не выполняется
$orders= $customers[0]->orders;
// SQL-запрос не выполняется
$country = $customers[0]->country;
// жадная загрузка связи "orders" и вложенной связи "orders.items"
$customers = Customer::find()->with('orders.items')->all();
// доступ к деталям первого заказа первого покупателя
// SQL-запрос не выполняется
$items = $customers[0]->orders[0]->items;
Вы можете жадно загрузить более глубокие вложенные связи, такие как a.b.c.d. Все родительские связи будут жадно загружены. Таким образом, когда вы вызываете метод [[yii\db\ActiveQuery::with()|with()]] с параметром a.b.c.d, вы жадно загрузите связи a, a.b, a.b.c и a.b.c.d.
Info: В целом, когда жадно загружается
Nсвязей, среди которыхMсвязей объявлено с помощью промежуточной таблицы, суммарное количество выполняемых SQL-запросов будет равноN+M+1. Заметьте, что вложенная связьa.b.c.dнасчитывает 4 связи.
Когда связь жадно загружается, вы можете настроить соответствующий запрос получения связных данных с использованием анонимной функции. Например:
// найти покупателей и получить их вместе с их странами и активными заказами
// SELECT * FROM `customer`
// SELECT * FROM `country` WHERE `id` IN (...)
// SELECT * FROM `order` WHERE `customer_id` IN (...) AND `status` = 1
$customers = Customer::find()->with([
'country',
'orders' => function ($query) {
$query->andWhere(['status' => Order::STATUS_ACTIVE]);
},
])->all();
Когда настраивается запрос на получение связных данных для какой-либо связи, вы можете указать название связи в виде ключа массива и использовать анонимную функцию в качестве соответствующего значения этого массива. Анонимная функция получит параметр $query, который представляет собой объект [[yii\db\ActiveQuery]], используемый для выполнения запроса на получение связных данных для данной связи. В вышеприведённом примере кода мы изменили запрос на получение связных данных, наложив на него дополнительное условие выборки статуса заказов.
Note: Если вы вызываете метод [[yii\db\Query::select()|select()]] в процессе жадной загрузки связей, вы должны убедиться, что будут выбраны столбцы, участвующие в объявлении связей. Иначе связные модели будут загружены неправильно. Например:
$orders = Order::find()->select(['id', 'amount'])->with('customer')->all(); // $orders[0]->customer всегда равно null. Для исправления проблемы вы должны сделать следующее: $orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();
Использование JOIN со связями
Note: Материал этого раздела применим только к реляционным базам данных, таким как MySQL, PostgreSQL, и т.д.
Запросы на получение связных данных, которые мы рассмотрели выше, ссылаются только на столбцы основной таблицы при извлечении основной информации. На самом же деле нам часто нужно ссылаться в запросах на столбцы связных таблиц. Например, мы можем захотеть получить покупателей, для которых имеется хотя бы один активный заказ. Для решения этой проблемы мы можем построить запрос с использованием JOIN как показано ниже:
// SELECT `customer`.* FROM `customer`
// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id`
// WHERE `order`.`status` = 1
//
// SELECT * FROM `order` WHERE `customer_id` IN (...)
$customers = Customer::find()
->select('customer.*')
->leftJoin('order', '`order`.`customer_id` = `customer`.`id`')
->where(['order.status' => Order::STATUS_ACTIVE])
->with('orders')
->all();
Note: Важно однозначно указывать в SQL-выражениях имена столбцов при построении запросов на получение связных данных с участием оператора JOIN. Наиболее распространённая практика - предварять названия столбцов с помощью имён соответствующих им таблиц.
Однако лучшим подходом является использование имеющихся объявлений связей с помощью вызова метода [[yii\db\ActiveQuery::joinWith()]]:
$customers = Customer::find()
->joinWith('orders')
->where(['order.status' => Order::STATUS_ACTIVE])
->all();
Оба подхода выполняют одинаковый набор SQL-запросов. Однако второй подход более прозрачен и прост.
По умолчанию, метод [[yii\db\ActiveQuery::joinWith()|joinWith()]] будет использовать конструкцию LEFT JOIN для объединения основной таблицы со связной. Вы можете указать другой тип операции JOIN (например, RIGHT JOIN) с помощью третьего параметра этого метода - $joinType. Если же вам нужен INNER JOIN, вы можете вместо этого просто вызвать метод [[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]].
Вызов метода [[yii\db\ActiveQuery::joinWith()|joinWith()]] будет жадно загружать связные данные по умолчанию. Если вы не хотите получать связные данные, вы можете передать во втором параметре $eagerLoading значение false.
Подобно методу [[yii\db\ActiveQuery::with()|with()]] вы можете объединять данные с одной или несколькими связями; вы можете настроить запрос на получение связных данных "на лету"; вы можете объединять данные с вложенными связями; вы можете смешивать использование метода [[yii\db\ActiveQuery::with()|with()]] и метода [[yii\db\ActiveQuery::joinWith()|joinWith()]]. Например:
$customers = Customer::find()->joinWith([
'orders' => function ($query) {
$query->andWhere(['>', 'subtotal', 100]);
},
])->with('country')
->all();
Иногда во время объединения двух таблиц вам может потребоваться указать некоторые дополнительные условия рядом с оператором ON во время выполнения JOIN-запроса. Это можно сделать с помощью вызова метода [[yii\db\ActiveQuery::onCondition()]] как показано ниже:
// SELECT `customer`.* FROM `customer`
// LEFT JOIN `order` ON `order`.`customer_id` = `customer`.`id` AND `order`.`status` = 1
//
// SELECT * FROM `order` WHERE `customer_id` IN (...)
$customers = Customer::find()->joinWith([
'orders' => function ($query) {
$query->onCondition(['order.status' => Order::STATUS_ACTIVE]);
},
])->all();
Вышеприведённый запрос вернёт всех покупателей и для каждого покупателя вернёт все активные заказы. Заметьте, что это поведение отличается от нашего предыдущего примера, в котором возвращались только покупатели, у которых был как минимум один активный заказ.
Info: Когда в объекте [[yii\db\ActiveQuery]] указано условие выборки с помощью метода [[yii\db\ActiveQuery::onCondition()|onCondition()]], это условие будет размещено в конструкции
ON, если запрос содержит оператор JOIN. Если же запрос не содержит оператор JOIN, такое условие будет автоматически размещено в конструкцииWHERE.
Псевдонимы связанных таблиц
Как уже было отмечено, при использовании в запросе JOIN-ов, приходится явно решать конфликты имён. Поэтому часто таблицам дают псевдонимы. Задать псевдоним для реляционного запроса можно следующим образом:
$query->joinWith([
'orders' => function ($q) {
$q->from(['o' => Order::tableName()]);
},
])
Выглядит это довольно сложно. Либо приходится задавать явно имена таблиц, либо вызывать Order::tableName(). Начиная с версии 2.0.7 вы можете задать и использовать псевдоним для связанной таблицы следующим образом:
// join the orders relation and sort the result by orders.id
$query->joinWith(['orders o'])->orderBy('o.id');
Этот синтаксис работает для простых связей. Если же необходимо использовать связующую таблицу, например $query->joinWith(['orders.product']), то вызовы joinWith вкладываются друг в друга:
$query->joinWith(['orders o' => function($q) {
$q->joinWith('product p');
}])
->where('o.amount > 100');
Обратные связи
Объявления связей часто взаимны между двумя Active Record классами. Например, Customer связан с Order посредством связи orders, а Order взаимно связан с Customer посредством связи customer.
class Customer extends ActiveRecord
{
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
}
}
class Order extends ActiveRecord
{
public function getCustomer()
{
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
}
}
Теперь рассмотрим следующий участок кода:
// SELECT * FROM `customer` WHERE `id` = 123
$customer = Customer::findOne(123);
// SELECT * FROM `order` WHERE `customer_id` = 123
$order = $customer->orders[0];
// SELECT * FROM `customer` WHERE `id` = 123
$customer2 = $order->customer;
// выведет "not the same"
echo $customer2 === $customer ? 'same' : 'not the same';
Мы думали, что $customer и $customer2 эквивалентны, но оказалось, что нет! Фактически они содержат одинаковые данные, но являются разными объектами. Когда мы получаем доступ к данным посредством $order->customer, выполняется дополнительный SQL-запрос для заполнения нового объекта $customer2.
Чтобы избежать избыточного выполнения последнего SQL-запроса в вышеприведённом примере, мы должны подсказать Yii, что customer - обратная связь относительно orders, и сделаем это с помощью вызова метода [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] как показано ниже:
class Customer extends ActiveRecord
{
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id'])->inverseOf('customer');
}
}
Теперь, после этих изменений в объявлении связи, получим:
// SELECT * FROM `customer` WHERE `id` = 123
$customer = Customer::findOne(123);
// SELECT * FROM `order` WHERE `customer_id` = 123
$order = $customer->orders[0];
// SQL-запрос не выполняется
$customer2 = $order->customer;
// выведет "same"
echo $customer2 === $customer ? 'same' : 'not the same';
Note: обратные связи не могут быть объявлены для связей, использующих промежуточную таблицу. То есть, если связь объявлена с помощью методов [[yii\db\ActiveQuery::via()|via()]] или [[yii\db\ActiveQuery::viaTable()|viaTable()]], вы не должны вызывать после этого метод [[yii\db\ActiveQuery::inverseOf()|inverseOf()]].
Сохранение связных данных
Во время работы со связными данными вам часто требуется установить связи между двумя разными видами данных или удалить существующие связи. Это требует установки правильных значений для столбцов, с помощью которых заданы связи. При использовании Active Record вам может понадобится завершить участок кода следующим образом:
$customer = Customer::findOne(123);
$order = new Order();
$order->subtotal = 100;
// ...
// установка атрибута, которой задаёт связь "customer" в объекте Order
$order->customer_id = $customer->id;
$order->save();
Active Record предоставляет метод [[yii\db\ActiveRecord::link()|link()]], который позволяет выполнить эту задачу более красивым способом:
$customer = Customer::findOne(123);
$order = new Order();
$order->subtotal = 100;
// ...
$order->link('customer', $customer);
Метод [[yii\db\ActiveRecord::link()|link()]] требует указать название связи и целевой объект Active Record, с которым должна быть установлена связь. Метод изменит значения атрибутов, которые связывают два объекта Active Record, и сохранит их в базу данных. В вышеприведённом примере, метод присвоит атрибуту customer_id объекта Order значение атрибута id объекта Customer и затем сохранит его в базу данных.
Note: Невозможно связать два свежесозданных объекта Active Record.
Преимущество метода [[yii\db\ActiveRecord::link()|link()]] становится ещё более очевидным, когда связь объявлена посредством промежуточной таблицы. Например, вы можете использовать следующий код, чтобы связать объект Order с объектом Item:
$order->link('items', $item);
Вышеприведённый код автоматически вставит строку данных в промежуточную таблицу order_item, чтобы связать объект order с объектом item.
Info: Метод [[yii\db\ActiveRecord::link()|link()]] не осуществляет какую-либо валидацию данных во время сохранения целевого объекта Active Record. На вас лежит ответственность за валидацию любых введённых данных перед вызовом этого метода.
Существует противоположная операция для [[yii\db\ActiveRecord::link()|link()]] - это операция [[yii\db\ActiveRecord::unlink()|unlink()]], она снимает существующую связь с двух объектов Active Record. Например:
$customer = Customer::find()->with('orders')->where(['id' => 123])->one();
$customer->unlink('orders', $customer->orders[0]);
По умолчанию метод [[yii\db\ActiveRecord::unlink()|unlink()]] задаст вторичному ключу (или ключам), который определяет существующую связь, значение null. Однако вы можете запросить удаление строки таблицы, которая содержит значение вторичного ключа, передав значение true в параметре $delete для этого метода.
Если связь построена на основе промежуточной таблицы, вызов метода [[yii\db\ActiveRecord::unlink()|unlink()]] инициирует очистку вторичных ключей в промежуточной таблице, или же удаление соответствующей строки данных в промежуточной таблице, если параметр $delete равен true.
Связывание объектов из разных баз данных
Active Record позволяет вам объявить связи между классами Active Record, которые относятся к разным базам данных. Базы данных могут быть разных типов (например, MySQL и PostgreSQL или MS SQL и MongoDB), и они могут быть запущены на разных серверах. Вы можете использовать тот же самый синтаксис для осуществления запросов выборки связных данных. Например:
// Объект Customer соответствует таблице "customer" в реляционной базе данных (например MySQL)
class Customer extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'customer';
}
public function getComments()
{
// у покупателя может быть много комментариев
return $this->hasMany(Comment::className(), ['customer_id' => 'id']);
}
}
// Объект Comment соответствует коллекции "comment" в базе данных MongoDB
class Comment extends \yii\mongodb\ActiveRecord
{
public static function collectionName()
{
return 'comment';
}
public function getCustomer()
{
// комментарий принадлежит одному покупателю
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
}
}
$customers = Customer::find()->with('comments')->all();
Вы можете использовать большую часть возможностей запросов получения связных данных, которые были описаны в этой главе.
Note: Применимость метода [[yii\db\ActiveQuery::joinWith()|joinWith()]] ограничена базами данных, которые позволяют выполнять запросы между разными базами с использованием оператора JOIN. По этой причине вы не можете использовать этот метод в вышеприведённом примере, т.к. MongoDB не поддерживает операцию JOIN.
Тонкая настройка классов Query
По умолчанию все запросы данных для Active Record поддерживаются с помощью класса [[yii\db\ActiveQuery]]. Для использования собственного класса запроса вам необходимо переопределить метод [[yii\db\ActiveRecord::find()]] и возвращать из него объект вашего собственного класса запроса. Например:
namespace app\models;
use yii\db\ActiveRecord;
use yii\db\ActiveQuery;
class Comment extends ActiveRecord
{
public static function find()
{
return new CommentQuery(get_called_class());
}
}
class CommentQuery extends ActiveQuery
{
// ...
}
Теперь, когда вы будете осуществлять получение данных (например, выполните find(), findOne()) или объявите связь (например, hasOne()) с объектом Comment, вы будете работать с объектом класса CommentQuery вместо ActiveQuery.
Tip: В больших проектах рекомендуется использовать собственные классы запросов, которые будут содержать в себе большую часть кода, связанного с настройкой запросов, таким образом классы Active Record удастся сохранить более чистыми.
Вы можете настроить класс запроса большим количеством различных способов для улучшения методик построения запросов. Например, можете объявить новые методы построения запросов в собственном классе запросов:
class CommentQuery extends ActiveQuery
{
public function active($state = true)
{
return $this->andWhere(['active' => $state]);
}
}
Note: Вместо вызова метода [[yii\db\ActiveQuery::where()|where()]] старайтесь во время объявления новых методов построения запросов использовать [[yii\db\ActiveQuery::andWhere()|andWhere()]] или [[yii\db\ActiveQuery::orWhere()|orWhere()]] для добавления дополнительных условий, в этом случае уже заданные условия выборок не будут перезаписаны.
Это позволит вам писать код построения запросов как показано ниже:
$comments = Comment::find()->active()->all();
$inactiveComments = Comment::find()->active(false)->all();
Вы также можете использовать новые методы построения запросов, когда объявляете связи для класса Comment или осуществляете запрос для выборки связных данных:
class Customer extends \yii\db\ActiveRecord
{
public function getActiveComments()
{
return $this->hasMany(Comment::className(), ['customer_id' => 'id'])->active();
}
}
$customers = Customer::find()->with('activeComments')->all();
// или по-другому:
$customers = Customer::find()->with([
'comments' => function($q) {
$q->active();
}
])->all();
Info: В Yii версии 1.1 была концепция с названием scope. Она больше не поддерживается в Yii версии 2.0, и вы можете использовать собственные классы запросов и собственные методы построения запросов, чтобы добиться той же самой цели.
Получение дополнительных атрибутов
Когда объект Active Record заполнен результатами запроса, его атрибуты заполнены значениями соответствующих столбцов из полученного набора данных.
Вы можете получить дополнительные столбцы или значения с помощью запроса и сохранить их внутри объекта Active Record. Например, предположим, что у нас есть таблица 'room', которая содержит информацию о доступных в отеле комнатах. Каждая комната хранит информацию о её геометрических размерах с помощью атрибутов 'length', 'width', 'height'. Представьте, что вам требуется получить список всех доступных комнат, отсортированных по их объёму в порядке убывания. В этом случае вы не можете вычислять объём с помощью PHP, потому что нам требуется сортировать записи по объёму, но вы также хотите отображать объем в списке. Для достижения этой цели, вам необходимо объявить дополнительный атрибут в вашем Active Record классе 'Room', который будет хранить значение 'volume':
class Room extends \yii\db\ActiveRecord
{
public $volume;
// ...
}
Далее вам необходимо составить запрос, который вычисляет объём комнаты и выполняет сортировку:
$rooms = Room::find()
->select([
'{{room}}.*', // получить все столбцы
'([[length]] * [[width]] * [[height]]) AS volume', // вычислить объём
])
->orderBy('volume DESC') // отсортировать
->all();
foreach ($rooms as $room) {
echo $room->volume; // содержит значение, вычисленное с помощью SQL-запроса
}
Возможность выбирать дополнительные атрибуты может быть особенно полезной для агрегирующих запросов. Представьте, что вам необходимо отображать список покупателей с количеством их заказов. Прежде всего вам потребуется объявить класс Customer со связью 'orders' и дополнительным атрибутом для хранения расчётов:
class Customer extends \yii\db\ActiveRecord
{
public $ordersCount;
// ...
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
}
}
После этого вы сможете составить запрос, который объединяет заказы и вычисляет их количество:
$customers = Customer::find()
->select([
'{{customer}}.*', // получить все атрибуты покупателя
'COUNT({{order}}.id) AS ordersCount' // вычислить количество заказов
])
->joinWith('orders') // обеспечить построение промежуточной таблицы
->groupBy('{{customer}}.id') // сгруппировать результаты, чтобы заставить агрегацию работать
->all();
Недостаток этого подхода заключается в том, что если данные для поля не загружены по результатам SQL запроса, то они должны быть вычисленны отдельно. Это означает, что запись, полученная посредством обычного запроса без дополнительных полей в разделе 'select', не может вернуть реальное значения для дополнительного поля. Это же касается и только что сохраненной записи.
$room = new Room();
$room->length = 100;
$room->width = 50;
$room->height = 2;
$room->volume; // значение будет равно `null`, т.к. поле не было заполнено
Использование магических методов [[yii\db\BaseActiveRecord::__get()|__get()]] и [[yii\db\BaseActiveRecord::__set()|__set()]] позволяет эмулировать поведение обычного поля:
class Room extends \yii\db\ActiveRecord
{
private $_volume;
public function setVolume($volume)
{
$this->_volume = (float) $volume;
}
public function getVolume()
{
if (empty($this->length) || empty($this->width) || empty($this->height)) {
return null;
}
if ($this->_volume === null) {
$this->setVolume(
$this->length * $this->width * $this->height
);
}
return $this->_volume;
}
// ...
}
Если результат запроса на выборку данных не содержит поле 'volume', то модель сможет расчитать его автоматически используя имеющиеся атрибуты.
Вы также можете вычислять агрегируемые поля используя объявленные отношения:
class Customer extends \yii\db\ActiveRecord
{
private $_ordersCount;
public function setOrdersCount($count)
{
$this->_ordersCount = (int) $count;
}
public function getOrdersCount()
{
if ($this->isNewRecord) {
return null; // нет смысла выполнять запрос на поиск по пустым ключам
}
if ($this->_ordersCount === null) {
$this->setOrdersCount($this->getOrders()->count()); // вычисляем агрегацию по требованию из отношения
}
return $this->_ordersCount;
}
// ...
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
}
}
При такой реализации, в случае когда 'ordersCount' присутствует в разделе 'select' - значение 'Customer::ordersCount' будет заполнено из результатов запроса, в противном случае - оно будет вычислено по первому требованию на основании отношения Customer::orders.
Этот подход также можно использовать для быстрого доступа к некоторым данным отношений, в особенности для агрегации. Например:
class Customer extends \yii\db\ActiveRecord
{
/**
* Объявляет виртуальное свойство для агрегируемых данных, доступное только на чтение.
*/
public function getOrdersCount()
{
if ($this->isNewRecord) {
return null; // нет смысла выполнять запрос на поиск по пустым ключам
}
return $this->ordersAggregation[0]['counted'];
}
/**
* Объявляет обычное отношение 'orders'.
*/
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
}
/**
* Объявляет новое отношение, основанное на 'orders', которое предоставляет агрегацию.
*/
public function getOrdersAggregation()
{
return $this->getOrders()
->select(['customer_id', 'counted' => 'count(*)'])
->groupBy('customer_id')
->asArray(true);
}
// ...
}
foreach (Customer::find()->with('ordersAggregation')->all() as $customer) {
echo $customer->ordersCount; // выводит агрегируемые данные из отношения без дополнительного запроса благодаря жадной загрузке
}
$customer = Customer::findOne($pk);
$customer->ordersCount; // выводит агрегируемые данные отношения через ленивую загрузку Домен — это имя сайта, которое легко запомнить
Бывают ли сайты без доменного имени?
Бывает так, что надоедает зависать в социальных сетях, смотреть чужие сайты и хочется сделать собственный. А это значит, что пришло время понять, как же всё устроено. Первым шагом будет выбор доменного имени сайта. Сайт можно сделать и без доменного имени, и он будет работать, но куда как приятнее заходить по имени www.site.ru, чем, например, 74.12.145.9. Для подбора свободного доменного имени есть много сервисов.
Как выбрать домен для сайта?
Для подбора доменных имён я использую сервис самого большого в России регистратора www.nic.ru. Для покупки доменного имени есть масса других регистраторов, у которых доменное имя можно купить дешевле или просто удобнее. Например, хостинг которым я пользуюсь, firstVDS предлагает доменные имена в зоне .ru по 150 рублей в год тогда, как nic.ru оказывает эту же услугу за 590 рублей (а продление за 890). Скорее всего, можно найти регистраторов с ценой и 100 рублей в год. Дешевле только для оптовиков и это отдельная тема.
Какие регистраторы доменов бывают?
Итак, чтобы подобрать доменное имя идём на www.nic.ru и находим свободное доменное имя. После этого идём на firstVDS и регистрируем его. Для того, чтобы зарегистрировать доменное имя необходимо ввести информацию о себе, как о владельце домена. Но там всё просто и мне кажется, что вопросов не должно возникнуть.
Напомню что, покупая услуги firstVDS по моей партнёрской ссылке, получаете скидку на первый месяц пользования услугами хостинга. Про хостинг firstVDS можно прочитать в этой статье.
Простое для понимания видео про DNS.
Neural Matching в Google: Как алгоритм понимает намерения пользователей вместо ключевых слов
- Информация о материале
- Родительская категория: Внешние сервисы
- Категория: Google
Исследования Google для ранжирования результатов поисковой выдачи
Еще в 2018 году Google опубликовал исследовательскую работу, где говорится о новом алгоритме сопоставления запросов и документов, проиндексированных в ней. Немного позднее сотрудник компании Дэнни Салливан в своем твите сделал несколько заявлений аналитического характера, дающих качественное представление о том как работает новый механизм, а также о его практическом применении. 30% запросов, поступающих в Гугл обрабатывается при участии Neural Matching.
Изменения в ранжировании Google для сайтов
По прошествии почти трех лет в интернете появилась новая услуга: адаптация контента под алгоритм Neural Matching. Речь в данном случае идет о длинных запросах пользователей или тех, что имеют сопутствующее окружение из вспомогательных слов: предлогов, союзов и прочего.
Классическое ранжировании страниц уходит на второй план: ссылки, ключевые слова и короткие фразы. Если упростить все репрезентативные материалы на тему нового алгоритма, можно сказать, что Google решил адаптировать поиск под пользователей, обращающихся к системе в формате разговорной речи. Одновременно с этим, система подстраивается под нужды потребителя, столкнувшегося с модернизацией окружающего мира, не имеющей пока речевой идентификации в устойчиво сложившихся терминах. Проще говоря, теперь часть запросов в Google сопоставляется с контентом сайтов с учетом факта того, что пользователь ищет, не владея специальной терминологией или знаниями по конкретной тематике.
Пример от Гугл. Например, дома появилось неизвестное для человека насекомое, он не может сделать краткий запрос об увиденном прямо. Но хочется знать: что это, насколько вредное или безвредное существо и т.д. Тогда появляются описательные запросы: «насекомое в доме с большим числом ног». Гугл отвечает с первой попытки - это «сороконожка».
У новой особенности поиска уже появились вполне практические применения. Используя запрос «как правильно/легко разрезать яйцо» предлагается купить яйцерезку. С другой стороны, сам Гугл как раз пытается уйти от подобных трактовок в тематиках, требующей квалификации. Если человек ищет информацию о том, как что-то можно сделать самостоятельно, то он должен в первую очередь получить ответ на этот вопрос, а после на второстепенные - что для этого надо. Согласитесь, если разрезать яйцо с помощью яйцерезки - проще, то универсального инструмента для установки окон пока нет, поэтому ответы на некоторые вопросы будут сложнее, чем ожидается. Точнее сказать, с большей долей вероятностью только 3-4 строки выдачи займут практические варианты с видео и фото. Дальнейшие позиции займут сайты, продающие соответствующий товар.
Теоретические рассуждения
В отличие от всех предшествующих алгоритмов разработчики Neural Matching отказываются от чисто семантического сопоставления запроса и текста документа. Если объяснить простыми словами, ранее и даже сейчас оптимизаторы используют тактику насыщения текста синонимами, жаргонизмами и прочими LSI. Новый механизм считает все перечисленное — лишь второстепенными факторами сопоставления запроса и документа. Что это значит на практике?
Авторы работы «Модель сопоставления с глубокой релевантностью для специального поиска» показали возможность отождествления сущности (главного слова в запросе) с текстом страниц без вспомогательных факторов поиска. Для многих понятно, как можно упростить понимание запроса пользователя, когда есть информация о его предпочтениях или в условиях повторного поиска по идентичной фразе. Новый алгоритм улучшает ситуацию даже для варианта, когда запрос с конкретного адреса выполняется впервые. Пожалуй, это основная причина для рассмотрения алгоритма Neural Matching. Учитывая факт возвращения к началу поиска, система сможет предложить лучшие результаты на самых первых строках выдачи, отодвинув менее релевантные ответы ниже, несмотря на высокое качество документов. Тогда как новый механизм повышает шансы сайтов попасть в область видимости целевой аудитории с первой попытки (когда пользователь еще не знает, как точно определяется искомый предмет). Это причина для оптимизации текстов под обозначенный алгоритм.
От патента к реальности
Объективно, оптимизаторам приходится отталкиваться не от причин. Сегодня Google использует множество собственных механизмов, публично анонсированных и по факту получивших патент. Поэтому услуги по оптимизации текстов ориентируются на работающие нововведения с отсылкой на конкретику от поисковой системы. Патенты имеют актуальность, обсуждаются, но далеко не всегда используются практике ранжирования. Они, как бы, ожидают своей очереди (или времени).
Поэтому иногда под Neural Matching подразумевается не только модель сопоставления запросов и документов. В учет идут и второстепенные факторы, которые на самом деле также имеют большое значение. Прежде, чем перейти к практическим советам, стоит рассмотреть еще один пример от Google.
Когда бессмыслица обретает смысл
Предлагаемая модель опирается на поиск ответов среди документов, где встречаются подходящие связи между словами, среди которых есть главное - сущность запроса. Авторы, указанной ранее работы, показывают, насколько могут отличаться релевантные документы.
Предлагается сопоставить такие выражения «нажрался на ночь» и «нажрался что делать». В первом случае подразумевается, что человек чрезмерно много съел. Во втором, выпил лишнего. Очевидно, что в таких ситуациях пользователю понадобятся разные ответы системы: о похудении (или что-то аналогичное) и способы выхода из похмелья. Таким образом, даже самый глупый и бессмысленный запрос имеет релевантный ответ от системы.
Практики уже увидели в данном механизме глубинные возможности для продвижения без использования синонимизации.
Что является актуальным в написании текстов
Продолжим высказанную мысль. Главное в механизме дать релевантный ответ, а не многократно повторить ключевое слово прямо или в склонениях/синонимах/прочем. На первый план в таком поиске выходит намерение человека неумело сформулировавшего запрос. Каждая фраза в таком контексте требует отдельной проработки. Но это пока. В идеале, правильные ответы совсем скоро начнут появляться для большинства подобных запросов.
Отдаленно все сказанное напоминает игру о том, как правильно сказать: «автобус ходит по маршруту», но «люди на автобусе ездят». Сюда можно подключить понятие о «биологических моторах», получивших соответствующее название из-за схожести механизма перемещения молекул с вращательным движением колеса (самый упрощенный взгляд). С точки зрения семантического поиска дать релевантный ответ было бы невозможно. Но с новым механизмом даже ошибочный запрос «биологическая машина» среди прочих ответов получает и релевантный относительно намерения, связанного с изучением конкретной тематики.
Теперь эксперты-маркетологи утверждают, что новые SEO-тексты, чтобы стать привлекательными для пользователей, должны отвечать ряду требований из новых алгоритмов ранжирования. Сюда относят такие свойства:
- насыщение контента маркерами тематики, отвечающие не запросу, а тому, что подразумевается;
- ключевые фразы формируются по-новому (требуется отдельное и глубокое изучение тематики);
- сохранение второстепенных факторов на страницах: анкоры, ссылки, прочее.
По факту последнего пункта. Изначально система выбирает лучшие по качеству сайты, отвечающие чисто семантически, в том числе анкорами и ссылками, запросу пользователя. Только на следующих этапах сравнения происходит отсеивание ресурсов с меньшим релевантным соответствием.
Старое и новое при создании контента для сайтов
Нейронное сопоставление, а также другие теоретические материалы полезны для тех, кто работает с машинным обучением и прочими прогрессивными моделями, в том числе математическими. Для людей же, которые готовят продающие тексты, становится более понятно, как надо оптимизировать контент, чтобы глазами поискового робота он выглядел полезным для потребителя.
Несколько рекомендаций от экспертов SEO-отрасли
Принципы создания контента в базовой части остаются прежними:
- Ориентир на целевую аудиторию.
- Четкость определения цели создания конкретной страницы.
- Презентация выгодных характеристик продавца на фоне конкурентов.
Заметно упрощаются условные правила создания контента:
- профессионалу достаточно изложить материал на собственном языке, делая небольшие коррективы на уровень подготовки читателя;
- копирайтеру требуется ТЗ, где определения объектов погружены в атмосферу намерений пользователя.
Что же рекомендуется охватывать во втором случае?
Ответы и вопросы — источник ключевых слов
Поиск специальных запросов в разных тематиках отличается. Где-то они лежат на поверхности и кажутся очевидными. Но в большинстве случаев требуются какие-то особенные способы:
- изучение форумов;
- чтение комментариев к статьям по теме;
- обсуждения в социальных сетях.
Площадка «Ответы» на Мэйл.ру и аналогичные проекты — альтернатива всему перечисленному. Здесь можно найти не только запросы в чистом виде, но и намерения пользователя, задающего конкретные вопросы. Воспользовавшись контентом таких проектов, можно генерировать собственные темы для публикаций под специальные запросы.
>Подсказки Google
Поисковая система дает несколько возможностей для подбора ключей прямо со страницы выдачи:
- подсказки, которые высвечиваются при вводе запроса в соответствующей строке;
- блок под быстрыми ответами «возможно вы искали» (там предлагаются варианты типа «ключевое + дополнительное слова»);
- связанные запросы (внизу страницы) и прочие способы.
Предлагаемые варианты — это готовые запросы, которые пользователи периодически вбивают в поиск. Для добавления их в семантическое ядро стоит проверить каждую фразу на частотность, конкурентность и еще раз убедиться, что для они подходят вашего текста.
В подсказках часто встречаются ключевые слова, связанные с публичными личностями:
- ФИО
- + профессия/должность
- + город
- + фото
- + семья
- + компромат
Продажи объемных и дорогостоящих объектов сопровождаются вариантами: бу, в кредит, рассрочку, недорого, купить, названия брендов и т.д.
Частичные синонимы
Тут речь идет о словах, которые определяют почти одинаковые предметы, но не выступающие заменителями друг друга. Например, «магнитола» и «автомагнитола», «мотор» и «машина» (иногда бывают синонимами), «здание» - как объект и «дом» - место, где живешь. В тематике оптимизации и продвижении сайтов часто возникает необходимость использовать: ресурсы, проекты, документы (о страницах), что не всегда в полной мере передает суть контента.
Вы можете использовать любые слова, но алгоритмы поискового робота не всегда будут считать запросы релевантными интенту пользователя.
К тому же, при описании некоторых объектов лжесинонимы могут оказаться просто неуместными: «холодильник» для дома нельзя назвать «автохолодильником». Чтобы работать с такими словами, рекомендуется использовать специальные сервисы подбора. Но стоит помнить о разумном количестве лжесинонимов в текстах.
Анализ конкурентов
Этот метод далеко не новый для отрасли СЕО-продвижения сайтов. Некоторые слишком увлекаются, копируя буквально все у лидеров ниши: сниппеты, структуру, текст. Но по-настоящему никогда нельзя сказать однозначно, какой конкретно критерий или совокупность параметров вывели проект на первые позиции в поисковую выдачу по разным запросам.
Эксперты советуют использовать SEO-анализ от сервиса Адвего. С помощью этого проекта можно собрать достаточно качественное семантическое ядро. Но обязательно надо сверяться с потребностями собственного бизнеса:
- совпадение целевых аудиторий;
- соответствие контента требованиям поисковых систем;
- дальнейшее создание текстов релевантных интенту пользователя;
- отсутствие ситуаций, когда под нужный запрос, продвигается не посадочная страница, а выбранная краулером.
Ну, и конечно, Адвего используют как дополнение к остальным методам по сбору семантики.
Особенности создания контента для сайта
Эксперты считают, что традиционный способ написания статей, даже с учетом LSI, не подходит, если тексты оптимизируются под Neural Matching. Необходимо отчетливо понимать, о чем надо писать. В реалиях современного SEO не работает простая синонимизация, расстановка ключевиков «по местам».
К примеру, могут возникакать трудности с написанием целого ряда статей. Так под запросом Domain Rank может подразумеваться вполне конкретная метрика от сервиса Ahrefs. Однако по факту часто выясняется, что пользователя интересуют как раз результаты, которые стоят за этим показателем. Одни хотят купить ссылки, другие стремятся улучшить свои параметры. В результате под каждый запрос Google ищет отдельное решение, поскольку очевидно, что потребности разных групп пользователей отличаются.

Еще один пример. Запросы «каталоги сайтов» и «сайт каталог». Особенность в том, что оба запроса имеют право на существование и фактически относятся к одной отрасли, но означают абсолютное разное. Что лишний раз подтверждает необходимость создания грамотного ТЗ для копирайтера. Поскольку простая расстановка запросов не поможет в продвижении страницы по-правильному из них. Нейросети отнесут статью к одному из намерений пользователя и будут показывать ссылку на страницу только для того варианта, который зачтется как релевантный.

В дополнение к сказанному, необходимо полностью закрывать тематику, чем меньше остается нераскрытых вопросов, тем выше рейтинг статьи.
Нейронное сопоставление и конечный потребитель контента
Если честно, в этом направлении ситуация только начинает меняться. Например, пользователь хочет узнать историю Пандоры, откуда взялся «ящик» и чем грозит факт его открытия. Но в большинстве случаев, когда используется ключевая фраза с сущностью «Пандора», поисковик предлагает сайты, продающие ювелирные украшения одноименного бренда. Даже если будет проделана огромная маркетинговая работа, при проверке, даже такие запросы, как «Пандора мировая история» всё равно транслируется исключительно торговая марка.
Чтобы получить релевантный ответ с исторической справкой, запрос надо дополнять специальными словами «Древняя Греция», «мифы», прочее. Соответственно и контент страницы также должен содержать эти термины, их описания или объяснения к ним.
Аналогичным способом надо действовать и в других отраслях. Написание статей становится трудоемким процессом, где автору надо быть компетентным в тематике, иначе все старания могут быть сведены к нулевому результату.
Заявление о доступности Helix Ultimate
- Информация о материале
- Родительская категория: JoomShaper
- Категория: Helix Ultimate
- О доступности Helix Ultimate 2.0
- Приверженность команды JoomShaper улучшению доступности
- Соответствие международным стандартам
- Дополнительные рекомендации
О доступности Helix Ultimate 2.0
Создавая Helix Ultimate 2.0, команда JoomShaper стремилась обеспечить максимальную веб-доступность. Именно поэтому эта новая HTML фреймворк-тема оптимизирована для соответствия принципам доступности и частично соответствует требованиям Раздела 508 (Section 508). Она проходит большинство тестов валидаторов, проверяющих соответствие рекомендациям WCAG 2.1 уровня AA, которые выявляют потенциальные проблемы для пользователей с ограниченными возможностями.
Внимание! Ни один автоматический инструмент не может точно определить, является ли ваш контент по-настоящему доступным. Только человек способен оценить истинную доступность веб-ресурса.
Приверженность команды JoomShaper улучшению доступности
Команда JoomShaper постоянно работает над улучшением кода Helix Ultimate 2.0 с точки зрения доступности. В будущих обновлениях шаблона Helix планируется реализовать ещё больше функций, запрошенных пользователями и рекомендованных руководствами по доступности. Например, со временем будет улучшена навигация через MegaMenu.
Если вы обнаружили проблемы с доступностью или считаете, что требования к доступности не соблюдаются, пожалуйста, сообщите об этом через форум поддержки.
Соответствие международным стандартам
Если вы создаёте сайт, который должен соответствовать следующим нормативным документам:
- Уровню A и уровню AA Руководств W3C по доступности веб-контента (WCAG 2.1)
- Разделу 508 Закона США о реабилитации (US Rehabilitation Act) в части функциональности
- Закону об американцах с ограниченными возможностями (ADA)
- Британскому регламенту 2018 года «Органы государственного сектора (веб-сайты и мобильные приложения) (№2) — Требования к доступности»
Дополнительные рекомендации
Команда JoomShaper рекомендует использовать также сторонние расширения, виджеты или скрипты, которые могут повысить уровень доступности вашего сайта. Однако помните: доступным должен быть не только код шаблона, но и весь контент внутри сайта. Когда сайты правильно спроектированы, разработаны и наполнены, все пользователи получают равный доступ к информации и функциональности.
Основные советы по улучшению доступности контента
- Используйте семантические заголовки (H1–H6 по иерархии)
- Добавляйте атрибут
altк изображениям, точно отражающий их содержание - Применяйте понятный и осмысленный текст ссылок (anchor text)
Ознакомьтесь с официальным руководством W3C «Разработка заявления о доступности», примерами и генератором заявлений:
https://www.w3.org/WAI/planning/statements/
Перевод официальной документации Helix Ultimate:
www.joomshaper.com
Типичная ошибка версионирования в Composer и как её избежать при работе с пакетами
- Информация о материале
- Родительская категория: Программы для автоматизации работы
- Категория: Composer
Распространённая ошибка при описании версий в Composer
Одна из частых ошибок, которую допускают разработчики, — это указание ограничений версий в разделе require своего пакета в виде конструкций вроде >=2.* или >=1.1.*.
Почему такая запись не имеет смысла
Если задуматься о том, что на самом деле означает выражение >=2.*, быстро становится ясно, что оно логически противоречиво. Разложим его на две части:
>=2означает, что пакет должен иметь версию 2.0.0 или выше.2.*означает, что пакет должен находиться в диапазоне от версии 2.0.0 (включительно) до 3.0.0 (исключительно).
Оба условия согласны в том, что версия должна быть не ниже 2.0.0. Однако невозможно однозначно определить, предполагали ли вы использование версии 3.0.0 или выше. Должна ли она удовлетворять условию, потому что вы указали >=2, или не должна, поскольку вы также ограничили версию шаблоном 2.*?
Как исправить и что делать вместо этого
Именно по этой причине Composer выдаёт ошибку и сообщает, что подобная запись недопустима. Чтобы решить проблему, необходимо чётко сформулировать своё намерение и использовать только один из подходов — либо операторы сравнения (>=, < и т.д.), либо шаблоны версий (*, ~, ^).
Перевод официальной документации Composer:
getcomposer.org
Как обновить информацию профиля Instagram (например, свое имя, имя пользователя или электронный адрес)?
- Информация о материале
- Родительская категория: Социальные сети
- Категория: Instagram
- Процедура обновления своего профиля в Instagram
- Какие проверки профиля Instagram могут быть?
- Instagram и законодательство России: статус и альтернативы
Процедура обновления своего профиля в Instagram
Чтобы обновить информацию профиля, возможно, вам понадобится войти в аккаунт.
Чтобы обновить информацию профиля, включая имя пользователя и электронный адрес, связанный с вашим аккаунтом:
- Перейдите в свой профиль.
- Нажмите Редактировать профиль.
- Введите информацию и
- нажмите Готово (iPhone),
- значок
∨(Android) или - Отправить (компьютер и мобильный браузер).
Какие проверки профиля Instagram могут быть?
Если у вашего аккаунта большой охват, при смене имени пользователя может требоваться проверка. Вы получите уведомление, как только проверка завершится. В ином случае имя пользователя будет изменено мгновенно. Ваши подписчики могут получить уведомление о том, что вы изменили имя пользователя.
Некоторая информация профиля доступна только вам. К такой информации относится электронный адрес, номер телефона и пол.
Узнайте, что делать, если вы забыли пароль или не можете войти в аккаунт. Если вы считаете, что ваш аккаунт взломан, узнайте подробнее о том, что можно сделать.
Instagram и законодательство России: статус и альтернативы
Instagram принадлежит компании Meta Platforms Inc., которая с 2022 года признана экстремистской организацией и запрещена на территории Российской Федерации. Это означает, что деятельность Meta, включая управление Instagram и Facebook, нелегальна в России.
С 1 сентября 2025 года вступил в силу запрет на размещение рекламы в Instagram и других соцсетях запрещённых организаций. Даже если вы технически можете получить доступ к аккаунту (например, через VPN), любое использование платформы в коммерческих, рекламных или промоактивных целях теперь несёт юридические риски — вплоть до штрафов по ст. 20.33 КоАП РФ.
Альтернативы для российских пользователей:
- VK Клипы и VK Stories — аналоги Reels и Stories от «ВКонтакте».
- Telegram — для ведения каналов, сторис (через боты) и прямых коммуникаций.
- Яндекс.Zen и Яндекс.Эфир — платформы для публикации контента и коротких видео с монетизацией.
- Rutube — видеохостинг с поддержкой коротких форматов и личных блогов.
- My World (Mail.ru Group) — отечественная соцсеть с функциями профиля, ленты и медиа.
Рекомендуем перенести все публичные и коммерческие активности на легальные российские платформы, чтобы избежать правовых последствий.
По материалам:
https://help.instagram.com/1642053262784201/
Livewire
Введение
Laravel Livewire - это библиотека, которая помогает созданию современных реактивных динамических интерфейсов с использованием Laravel Blade в качестве языка шаблонов. Laravel Livewire - это отличный стек, если вы хотите создать приложение, которое является динамическим и реактивным, но вам неудобно переходить на полноценный фреймворк JavaScript, такой как Vue.js.
При использовании Livewire маршруты вашего приложения будут отвечать типичными шаблонами Blade. Однако в этих шаблонах вы можете отображать компоненты Livewire по мере необходимости:
<div class="mt-4">
@livewire('server-list')
</div>
При использовании стека Livewire Jetstream имеет некоторые уникальные особенности, о которых вам следует знать. Мы обсудим каждую из этих функций ниже.
Документация Laravel Livewire
Перед использованием стека Livewire настоятельно рекомендуется просмотреть всю документацию Livewire.
Компоненты
При создании стека Jetstream Livewire были созданы различные компоненты Blade (кнопки, панели, входы, модули), чтобы помочь в обеспечении согласованности и простоты использования пользовательского интерфейса. Вы можете использовать или не использовать эти компоненты. Однако, если вы хотите их использовать, вам следует активировать их с помощью команды Artisan vendor:publish:
php artisan vendor:publish --tag=jetstream-views
Вы можете получить представление о том, как использовать эти компоненты, просмотрев их использование в существующих представлениях Jetstream, расположенных в вашем каталоге resources/views.
Модули
Большинство компонентов стека Jetstream Livewire не имеют связи с вашим сервером. Однако модальные компоненты Livewire, входящие в состав Jetstream, действительно взаимодействуют с серверной частью Livewire для определения своего открытого/закрытого состояния. Кроме того, Jetstream включает два типа модулей: dialog-modal и confirmation-modal. confirmation-modal может использоваться при подтверждении деструктивных действий, таких как удаления, в то время как dialog-modal является более общим модулем, который можно использовать в любое время.
Чтобы проиллюстрировать использование модулей, рассмотрим следующий модуль, который подтверждает, что пользователь желает удалить свою учетную запись:
<x-jet-confirmation-modal wire:model="confirmingUserDeletion">
<x-slot name="title">
Удалить аккаунт
</x-slot>
<x-slot name="content">
Вы уверены, что хотите удалить свою учетную запись? После удаления вашей учетной записи все ее ресурсы и данные будут удалены без возможности восстановления.
</x-slot>
<x-slot name="footer">
<x-jet-secondary-button wire:click="$toggle('confirmingUserDeletion')" wire:loading.attr="disabled">
Отменить
</x-jet-secondary-button>
<x-jet-danger-button class="ml-2" wire:click="deleteUser" wire:loading.attr="disabled">
Удалить аккаунт
</x-jet-danger-button>
</x-slot>
</x-jet-confirmation-modal>
Как видите, состояние открытия/закрытия модуля определяется свойством wire:model, которое объявлено в компоненте. Это имя свойства должно соответствовать логическому свойству в соответствующем классе PHP вашего компонента Livewire. Содержимое модального окна может быть определено путем использования трех слотов: title, content и footer.
Перевод:
https://jetstream.laravel.com/1.x/stacks/livewire.html
Почему не следует комбинировать операторы сравнения и шаблоны версий в Composer?
- Информация о материале
- Родительская категория: Программы для автоматизации работы
- Категория: Composer
- Почему не следует комбинировать операторы сравнения и шаблоны версий в Composer?
- Разбор проблемы
- Реакция Composer
- Как правильно указывать ограничения
- Заключение
Почему не следует комбинировать операторы сравнения и шаблоны версий в Composer?
Это довольно распространённая ошибка, которую допускают разработчики при описании ограничений версий в разделе require своего пакета, например: >=2.* или >=1.1.*.
Разбор проблемы
Если внимательно подумать о том, что на самом деле означает такая запись, быстро становится ясно, что она логически несостоятельна. Возьмём выражение >=2.* и разложим его на две части:
>=2означает, что пакет должен иметь версию 2.0.0 или выше.2.*означает, что пакет должен находиться в диапазоне от версии 2.0.0 (включительно) до 3.0.0 (исключительно).
Как видите, оба условия согласуются в том, что версия должна быть не ниже 2.0.0. Однако невозможно однозначно определить, предполагали ли вы использование версии 3.0.0 или выше. Должна ли она удовлетворять условию, потому что вы указали >=2, или не должна, поскольку вы также ограничили версию шаблоном 2.*?
Реакция Composer
Именно по этой причине Composer не принимает такие ограничения и выдаёт ошибку, указывая на их некорректность.
Как правильно указывать ограничения
Чтобы исправить ситуацию, необходимо чётко сформулировать своё намерение и использовать только один из подходов — либо операторы сравнения, либо шаблоны версий.
Заключение
Точность в указании версионных ограничений критически важна для стабильности и воспроизводимости зависимостей. Избегайте неоднозначных конструкций вроде >=2.* — они не только нарушают логику управления версиями, но и приводят к ошибкам на этапе установки пакетов.
Перевод официальной документации Composer:
getcomposer.org
Страница 3 из 217