Final Class Yiisoft\ActiveRecord\LazyLoadGuard
| Inheritance | Yiisoft\ |
|---|
Detects N+1 queries caused by lazy loading of relations in models using {@see LazyLoadGuardTrait}.
Public Methods
| Method | Description | Defined By |
|---|---|---|
| check() | Registers a lazy load of the relation and reports it according to the mode, starting from the second record of the same result set that loads the relation, since one record loading a relation isn't an N+1 problem. | Yiisoft\ |
| getCounters() | Returns the number of registered lazy loads [model_class::relation_name => count, ...], including the ones
that weren't reported because they belong to result sets of their own. |
Yiisoft\ |
| registerResultSet() | Groups the models of one result set, so that the guard can tell a relation loaded by several records of the same result set, which is an N+1 query, from the same relation loaded by records that were each fetched by a query of their own, which has nothing to eager load across. | Yiisoft\ |
| reset() | Resets the counters, the mode and the logger to their defaults. | Yiisoft\ |
| resetCounters() | Clears the counters and the registered result sets, keeping the mode and the logger. Call it between the requests of a long-running worker, so that the result sets of one request aren't counted against the next. | Yiisoft\ |
| set() | Sets the mode of the guard and the logger to report to. The logger is only used in the {@see LazyLoadGuardMode::Log} mode, since {@see LazyLoadGuardMode::Strict} carries the same information in the exception it throws. | Yiisoft\ |
Method Details
Registers a lazy load of the relation and reports it according to the mode, starting from the second record of the same result set that loads the relation, since one record loading a relation isn't an N+1 problem.
| public static void check ( Yiisoft\ | ||
| $model | Yiisoft\ |
|
| $relationName | string | |
| throws | Yiisoft\ |
In the {@see \ |
|---|---|---|
public static function check(ActiveRecordInterface $model, string $relationName): void
{
$modelClass = $model::class;
$relation = $modelClass . '::' . $relationName;
self::$counters[$relation] = (self::$counters[$relation] ?? 0) + 1;
$resultSets = self::resultSets();
// A record fetched by a query of its own has nothing to eager load across.
if (!isset($resultSets[$model])) {
return;
}
/** @psalm-var LazyLoadResultSet $resultSet */
$resultSet = $resultSets[$model];
$count = $resultSet->registerLazyLoad($model, $relation);
if ($count === 1) {
return;
}
$message = "Relation \"$relation\" is lazy loaded by $count records of one result set."
. " Eager load it with \"with('$relationName')\" to run one query instead of $count.";
if (self::$mode === LazyLoadGuardMode::Strict) {
throw new LazyLoadException($message, $modelClass, $relationName);
}
self::$logger?->warning(
$message,
[
'model' => $modelClass,
'relation' => $relationName,
'count' => $count,
'trace' => (new Exception())->getTraceAsString(),
],
);
}
Returns the number of registered lazy loads [model_class::relation_name => count, ...], including the ones
that weren't reported because they belong to result sets of their own.
| public static integer[] getCounters ( ) |
public static function getCounters(): array
{
return self::$counters;
}
Groups the models of one result set, so that the guard can tell a relation loaded by several records of the same result set, which is an N+1 query, from the same relation loaded by records that were each fetched by a query of their own, which has nothing to eager load across.
| public static void registerResultSet ( Yiisoft\ | ||
| $models | Yiisoft\ |
The models populated from one result set. |
public static function registerResultSet(array $models): void
{
$resultSets = null;
$resultSet = null;
foreach ($models as $model) {
if (!self::isGuarded($model::class)) {
continue;
}
$resultSets ??= self::resultSets();
$resultSet ??= new LazyLoadResultSet();
$resultSets[$model] = $resultSet;
}
}
Resets the counters, the mode and the logger to their defaults.
| public static void reset ( ) |
public static function reset(): void
{
self::$mode = LazyLoadGuardMode::Log;
self::$logger = null;
self::resetCounters();
}
Clears the counters and the registered result sets, keeping the mode and the logger. Call it between the requests of a long-running worker, so that the result sets of one request aren't counted against the next.
| public static void resetCounters ( ) |
public static function resetCounters(): void
{
self::$counters = [];
self::$resultSets = null;
}
Sets the mode of the guard and the logger to report to. The logger is only used in the {@see LazyLoadGuardMode::Log} mode, since {@see LazyLoadGuardMode::Strict} carries the same information in the exception it throws.
| public static void set ( \ | ||
| $mode | \ |
|
| $logger | ?\ |
|
public static function set(LazyLoadGuardMode $mode, ?LoggerInterface $logger = null): void
{
self::$mode = $mode;
self::$logger = $logger;
}
User Contributed Notes
Leave a comment
Join the conversation to share a note.