From b6883d5513a5f69a38e5c3691d50aa32a6870310 Mon Sep 17 00:00:00 2001 From: markhuot Date: Wed, 15 Nov 2023 06:22:56 -0500 Subject: [PATCH] fixing tests and some breaking issues --- src/actions/GetComponentType.php | 2 +- .../NormalizeFieldDataForComponent.php | 10 ++-- src/models/Component.php | 48 +++++++++++-------- src/templates/attributes/alignment.twig | 8 ++-- src/twig/ExportTokenNode.php | 2 +- tests/EagerLoadTest.php | 27 ++++++++++- tests/ParseTwigForSchemaTest.php | 2 +- 7 files changed, 65 insertions(+), 34 deletions(-) diff --git a/src/actions/GetComponentType.php b/src/actions/GetComponentType.php index b509596..f334fc8 100644 --- a/src/actions/GetComponentType.php +++ b/src/actions/GetComponentType.php @@ -57,7 +57,7 @@ public function byType(string $type): ComponentType } if ($fqcn) { - return Craft::$container->get($fqcn, $this->context); + return Craft::$container->get($fqcn, ['context' => $this->context]); } throw new \RuntimeException('Could not find a component type definition for '.$type); diff --git a/src/actions/NormalizeFieldDataForComponent.php b/src/actions/NormalizeFieldDataForComponent.php index b5506ca..e364ced 100644 --- a/src/actions/NormalizeFieldDataForComponent.php +++ b/src/actions/NormalizeFieldDataForComponent.php @@ -33,11 +33,11 @@ public function handle(mixed $value, string $handle) } // If the field is editable, return an editable div - if ($field?->getBehavior(AttachFieldBehavior::INTERACTS_WITH_KEYSTONE)) { - if ($field->isEditableInLivePreview() && Craft::$app->getRequest()->getQueryParam('x-craft-live-preview') !== null) { - return new InlineEditData($this->component, $field, $value); - } - } +// if ($field?->getBehavior(AttachFieldBehavior::INTERACTS_WITH_KEYSTONE)) { +// if ($field->isEditableInLivePreview() && Craft::$app->getRequest()->getQueryParam('x-craft-live-preview') !== null) { +// return new InlineEditData($this->component, $field, $value); +// } +// } return $value; } diff --git a/src/models/Component.php b/src/models/Component.php index d9c6e29..ebf731d 100644 --- a/src/models/Component.php +++ b/src/models/Component.php @@ -17,7 +17,7 @@ use markhuot\keystone\events\AfterPopulateTree; use yii\base\Event; -use function markhuot\craftpest\helpers\test\dump; +use yii\db\conditions\OrCondition; use function markhuot\keystone\helpers\base\app; use function markhuot\keystone\helpers\base\throw_if; @@ -203,8 +203,12 @@ public function rules(): array ]; } - public function setPath(?string $path): void + public function setPath(string|array|null $path): void { + if (is_array($path)) { + $path = implode('/', array_filter($path)); + } + if (is_string($path)) { $path = trim($path, '/'); } @@ -272,34 +276,36 @@ public function getSlot(string $name = null): SlotCollection { $this->getType()->defineSlot($name); - if ($this->slotted !== null) { - $components = collect($this->slotted) - ->where(fn (Component $component) => $component->isDirectDiscendantOf($this, $name)) - ->each(function (Component $component) { - $components = collect($this->slotted) - ->where(fn (Component $c) => str_starts_with($c->path ?? '', $component->getChildPath() ?? '')) - ->all(); - - $component->setSlotted($components); - }); - } elseif ($this->elementId && $this->fieldId) { + if ($this->slotted === null && $this->elementId && $this->fieldId) { $components = Component::find() ->with('data') - ->where([ - 'elementId' => $this->elementId, - 'fieldId' => $this->fieldId, - 'path' => $this->getChildPath(), - 'slot' => $name, + ->where(['and', + ['elementId' => $this->elementId], + ['fieldId' => $this->fieldId], + ['slot' => $name], + new OrCondition(array_filter([ + ! $this->getChildPath() ? ['path' => null] : null, + ['like', 'path', $this->getChildPath().'%', false], + ])), ]) ->orderBy('sortOrder') ->collect(); + // dd($components); $this->afterPopulateTree($components); $this->setSlotted($components->all()); - } else { - $components = collect(); } + $components = collect($this->slotted) + ->where(fn (Component $component) => $component->isDirectDiscendantOf($this, $name)) + ->each(function (Component $component) { + $components = collect($this->slotted) + ->where(fn (Component $c) => str_starts_with($c->path ?? '', $component->getChildPath() ?? '')) + ->all(); + + $component->setSlotted($components); + }); + // As we delve through the render tree pass some state around so we know // where each child is rendering and can act accordingly. For example, // @@ -313,7 +319,7 @@ public function getSlot(string $name = null): SlotCollection ->mergeContext($this->context) ->setRenderParent($this) ) - ->toArray(); + ->values()->all(); return new SlotCollection($components, $this, $name); } diff --git a/src/templates/attributes/alignment.twig b/src/templates/attributes/alignment.twig index 6041994..d1f325f 100644 --- a/src/templates/attributes/alignment.twig +++ b/src/templates/attributes/alignment.twig @@ -34,7 +34,7 @@ diff --git a/src/twig/ExportTokenNode.php b/src/twig/ExportTokenNode.php index 4ab1e48..d3cc377 100644 --- a/src/twig/ExportTokenNode.php +++ b/src/twig/ExportTokenNode.php @@ -13,7 +13,7 @@ public function compile(\Twig\Compiler $compiler) { $compiler ->addDebugInfo($this) - ->write('$cb=function() use ($context){') + ->write('$cb=function() use ($context, $macros){') ->write($this->getAttribute('capture') ? 'ob_start();'.PHP_EOL : '$value=') ->subcompile($this->getNode('value')) ->write($this->getAttribute('capture') ? '$value=ob_get_contents();ob_end_clean();'.PHP_EOL : '') diff --git a/tests/EagerLoadTest.php b/tests/EagerLoadTest.php index be039fd..f7fe580 100644 --- a/tests/EagerLoadTest.php +++ b/tests/EagerLoadTest.php @@ -3,6 +3,31 @@ use markhuot\craftpest\factories\Asset; use markhuot\keystone\models\Component; +it('passes loaded components down so they\'re not refetched', function () { + $grandparent = Component::factory()->type('keystone/section')->create(); + $parent = Component::factory()->type('keystone/section')->path($grandparent->id)->create(); + $child = Component::factory()->type('keystone/section')->path([$grandparent->id, $parent->id])->create(); + $grandchild = Component::factory()->type('keystone/section')->path([$grandparent->id, $parent->id, $child->id])->create(); + + // Load our data in outside of our benchmark so we can ensure there are no + // database queries below this point + $grandparent->getSlot(); + + $this->beginBenchmark(); + expect($grandparent->getSlot()) + + // Check that only the parent is slotted in to the grandparent + ->toHaveCount(1) + + // Check that only the child is slotted in to the parent + ->first()->getSlot()->toHaveCount(1) + + // Check that only the grandchild is slotted in to the child + ->first()->getSlot()->first()->getSlot()->toHaveCount(1); + + $this->endBenchmark()->assertQueryCount(0); +}); + it('eager loads assets', function () { $fragment = Component::factory() ->type('keystone/fragment') @@ -20,5 +45,5 @@ $this->beginBenchmark(); $fragment->getSlot()->each(fn ($c) => $c->getProp('asset')->one()); $this->endBenchmark() - ->assertQueryCount(3); + ->assertQueryCount(4); }); diff --git a/tests/ParseTwigForSchemaTest.php b/tests/ParseTwigForSchemaTest.php index 3379a47..5e43e08 100644 --- a/tests/ParseTwigForSchemaTest.php +++ b/tests/ParseTwigForSchemaTest.php @@ -19,7 +19,7 @@ it('caches component types by modification date', function () { $fqcn = (new CompileTwigComponent('site:component-with-fields.twig', 'test/component-with-fields'))->handle(); $hash = sha1('test/component-with-fields'); - $filemtime = filemtime(Craft::$app->getView()->resolveTemplate('component-with-fields.twig', \craft\web\View::TEMPLATE_MODE_SITE)); + $filemtime = (new GetFileMTime())->handle(Craft::$app->getView()->resolveTemplate('component-with-fields.twig', \craft\web\View::TEMPLATE_MODE_SITE)); expect(App::parseEnv('@runtime/compiled_classes/ComponentType'.$hash.$filemtime.'.php'))->toBeFile(); });