From 396c4efdf7ff70fef6d021b2e07804155421741e Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Sat, 4 Oct 2025 10:41:01 +0100 Subject: [PATCH] Improve schema performance, especially in repeaters/builders (#18068) * Cache default child schemas * safer * Return current component if the state path is the same * Add cachedComponentsByStatePath * Update HasComponents.php --- .../Components/Concerns/HasChildComponents.php | 16 +++++++++++++--- .../schemas/src/Components/Concerns/HasState.php | 5 +++++ .../schemas/src/Components/Utilities/Get.php | 14 ++++++++------ .../schemas/src/Components/Utilities/Set.php | 14 ++++++++------ packages/schemas/src/Concerns/HasComponents.php | 9 ++++++++- 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/packages/schemas/src/Components/Concerns/HasChildComponents.php b/packages/schemas/src/Components/Concerns/HasChildComponents.php index 72438b59b2..e7b01947a8 100644 --- a/packages/schemas/src/Components/Concerns/HasChildComponents.php +++ b/packages/schemas/src/Components/Concerns/HasChildComponents.php @@ -16,6 +16,11 @@ trait HasChildComponents */ protected array $childComponents = []; + /** + * @var array | null + */ + protected ?array $cachedDefaultChildSchemas = null; + /** * @param array | Closure $components */ @@ -67,8 +72,8 @@ trait HasChildComponents */ public function getChildSchema($key = null): ?Schema { - if (filled($key) && array_key_exists($key, $containers = $this->getDefaultChildSchemas())) { - return $containers[$key]; + if (filled($key) && array_key_exists($key, $this->cachedDefaultChildSchemas ??= $this->getDefaultChildSchemas())) { + return $this->cachedDefaultChildSchemas[$key]; } $key ??= 'default'; @@ -133,7 +138,7 @@ trait HasChildComponents } return [ - ...(array_key_exists('default', $this->childComponents) ? $this->getDefaultChildSchemas() : []), + ...(array_key_exists('default', $this->childComponents) ? ($this->cachedDefaultChildSchemas ??= $this->getDefaultChildSchemas()) : []), ...array_reduce( array_keys($this->childComponents), function (array $carry, string $key): array { @@ -170,6 +175,11 @@ trait HasChildComponents return ['default' => $this->getChildSchema()]; } + public function clearCachedDefaultChildSchemas(): void + { + $this->cachedDefaultChildSchemas = null; + } + protected function cloneChildComponents(): static { foreach ($this->childComponents as $key => $childComponents) { diff --git a/packages/schemas/src/Components/Concerns/HasState.php b/packages/schemas/src/Components/Concerns/HasState.php index e52e60f105..e54f11d415 100644 --- a/packages/schemas/src/Components/Concerns/HasState.php +++ b/packages/schemas/src/Components/Concerns/HasState.php @@ -552,6 +552,11 @@ trait HasState data_set($livewire, $this->getStatePath(), $this->evaluate($state)); + // For components such as repeaters and builders, the default child schemas depend on the state of the component. + // When loading state into these fields after the state is already present, the cached child schemas need to be + // cleared so that they can be re-evaluated based on the new state. `rawState()` is called during this process. + $this->clearCachedDefaultChildSchemas(); + return $this; } diff --git a/packages/schemas/src/Components/Utilities/Get.php b/packages/schemas/src/Components/Utilities/Get.php index d47e22bbc1..50259a838b 100644 --- a/packages/schemas/src/Components/Utilities/Get.php +++ b/packages/schemas/src/Components/Utilities/Get.php @@ -16,12 +16,14 @@ class Get $path = $this->component->resolveRelativeStatePath($path, $isAbsolute); - $component = $this->component->getRootContainer()->getComponentByStatePath( - $path, - withHidden: true, - withAbsoluteStatePath: true, - skipComponentChildContainersWhileSearching: $this->component, - ); + $component = ($this->component->getStatePath() === $path) + ? $this->component + : $this->component->getRootContainer()->getComponentByStatePath( + $path, + withHidden: true, + withAbsoluteStatePath: true, + skipComponentChildContainersWhileSearching: $this->component, + ); if (! $component) { return data_get($livewire, $path); diff --git a/packages/schemas/src/Components/Utilities/Set.php b/packages/schemas/src/Components/Utilities/Set.php index 99b2960361..1b6d4c3a70 100644 --- a/packages/schemas/src/Components/Utilities/Set.php +++ b/packages/schemas/src/Components/Utilities/Set.php @@ -16,12 +16,14 @@ class Set $path = $this->component->resolveRelativeStatePath($path, $isAbsolute); - $component = $this->component->getRootContainer()->getComponentByStatePath( - $path, - withHidden: true, - withAbsoluteStatePath: true, - skipComponentChildContainersWhileSearching: $this->component, - ); + $component = ($this->component->getStatePath() === $path) + ? $this->component + : $this->component->getRootContainer()->getComponentByStatePath( + $path, + withHidden: true, + withAbsoluteStatePath: true, + skipComponentChildContainersWhileSearching: $this->component, + ); $state = $this->component->evaluate($state); diff --git a/packages/schemas/src/Concerns/HasComponents.php b/packages/schemas/src/Concerns/HasComponents.php index e0773e232d..6d570bbea9 100644 --- a/packages/schemas/src/Concerns/HasComponents.php +++ b/packages/schemas/src/Concerns/HasComponents.php @@ -29,6 +29,11 @@ trait HasComponents */ protected ?array $cachedComponents = null; + /** + * @var array>> + */ + protected array $cachedComponentsByStatePath = []; + /** * @param array | Component | Action | ActionGroup | string | Htmlable | Closure $components */ @@ -37,6 +42,7 @@ trait HasComponents $this->components = $components; $this->cachedComponents = null; $this->cachedFlatComponents = []; + $this->cachedComponentsByStatePath = []; return $this; } @@ -226,7 +232,7 @@ trait HasComponents return null; }; - return $search($this); + return $this->cachedComponentsByStatePath[$withHidden][$skipComponentChildContainersWhileSearching ? spl_object_id($skipComponentChildContainersWhileSearching) : null][$statePath] ??= $search($this); } /** @@ -352,6 +358,7 @@ trait HasComponents $this->cachedComponents = null; $this->cachedFlatComponents = []; + $this->cachedComponentsByStatePath = []; } return $this;