glhd/special

View on GitHub
src/Support/ModelObserver.php

Summary

Maintainability
A
0 mins
Test Coverage
A
95%
<?php

namespace Glhd\Special\Support;

use BackedEnum;
use Illuminate\Database\Eloquent\Model;
use WeakMap;

class ModelObserver
{
    protected array $observed = [];

    public function __construct(
        protected KeyMap $key_map,
    ) {
    }
    
    public function observe(Model $model, BackedEnum $enum): void
    {
        if (! isset($this->observed[$model::class])) {
            $this->registerModelObserver($model::class);
        }
        
        $this->registerEnumCallback($model, $enum);
    }
    
    protected function registerEnumCallback(Model $model, BackedEnum $enum): void
    {
        $this->observed[$model::class][$model->getKey()] ??= new WeakMap();
        $this->observed[$model::class][$model->getKey()]->offsetSet($enum, true);
    }
    
    /** @param class-string<Model> $class */
    protected function registerModelObserver(string $class): void
    {
        $class::updated($this->handleChange(...));
        $class::deleted($this->handleChange(...));
        
        $this->observed[$class] = [];
    }
    
    protected function handleChange(Model $model): void
    {
        if (! $map = $this->getMap($model)) {
            return;
        }
        
        // Clear the enums from the container for this request
        foreach ($map as $enum => $observed) {
            $enum->forgetSingleton();
        }
        
        // If the model was deleted, then also clear the key map, since
        // the ID is likely to change. This is not recommended, but we'll
        // handle the case to be safe.
        if (! $model->exists) {
            $this->key_map->clear();
        }
    }
    
    /** @return ?WeakMap<\Glhd\Special\EloquentBacking, bool> */
    protected function getMap(Model $model): ?WeakMap
    {
        return $this->observed[$model::class][$model->getKey()] ?? null;
    }
}