0

Final Class Yiisoft\ActiveRecord\LazyLoadGuard

InheritanceYiisoft\ActiveRecord\LazyLoadGuard

Detects N+1 queries caused by lazy loading of relations in models using {@see LazyLoadGuardTrait}.

Public Methods

Hide inherited 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\ActiveRecord\LazyLoadGuard
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\ActiveRecord\LazyLoadGuard
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\ActiveRecord\LazyLoadGuard
reset() Resets the counters, the mode and the logger to their defaults. Yiisoft\ActiveRecord\LazyLoadGuard
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\ActiveRecord\LazyLoadGuard
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\ActiveRecord\LazyLoadGuard

Method Details

Hide inherited methods

check() public static method

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\ActiveRecord\ActiveRecordInterface $model, string $relationName )
$model Yiisoft\ActiveRecord\ActiveRecordInterface
$relationName string
throws Yiisoft\ActiveRecord\LazyLoadException

In the {@see \Yiisoft\ActiveRecord\LazyLoadGuardMode::Strict} mode.

                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(),
        ],
    );
}

            
getCounters() public static method

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;
}

            
registerResultSet() public static method

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.

See also Yiisoft\ActiveRecord\ActiveQuery::populate().

public static void registerResultSet ( Yiisoft\ActiveRecord\ActiveRecordInterface[] $models )
$models Yiisoft\ActiveRecord\ActiveRecordInterface[]

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;
    }
}

            
reset() public static method

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();
}

            
resetCounters() public static method

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;
}

            
set() public static method

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 ( \Yiisoft\ActiveRecord\LazyLoadGuardMode $mode, ?\Psr\Log\LoggerInterface $logger = null )
$mode \Yiisoft\ActiveRecord\LazyLoadGuardMode
$logger ?\Psr\Log\LoggerInterface

                public static function set(LazyLoadGuardMode $mode, ?LoggerInterface $logger = null): void
{
    self::$mode = $mode;
    self::$logger = $logger;
}