From 3a4e4fe8cc126f8405ca91a6ebac8188130a17ee Mon Sep 17 00:00:00 2001 From: indy koning Date: Wed, 10 Dec 2025 09:29:16 +0100 Subject: [PATCH 1/6] RAP-1695 - Added customer type id, renamed value functions, render htmlspecialchars --- resources/js/filters.js | 9 +++++++ resources/js/instantsearch.js | 2 -- .../views/components/highlight.blade.php | 19 ++++---------- .../listing/partials/filter/select.blade.php | 2 +- .../partials/filter/selected.blade.php | 4 +-- src/Models/AbstractAttribute.php | 25 +++++++++---------- src/Models/EavAttribute.php | 4 +-- 7 files changed, 29 insertions(+), 36 deletions(-) diff --git a/resources/js/filters.js b/resources/js/filters.js index a7983d474..a2378bd52 100644 --- a/resources/js/filters.js +++ b/resources/js/filters.js @@ -22,3 +22,12 @@ window.url = function (path = '') { return (window.config.base_url || window.origin) + path } + +window.stripHtmlTags = function (html, safeTags = ['mark']) { + safeTags = safeTags.map((tag) => tag.replace(/[^a-zA-Z0-9-]/g, '')).filter(Boolean); + return html.replace(new RegExp('<(?!/?(?:' + safeTags.join('|') + ')>)(?:.|\n)*?>', 'gm'), '') // Safe tags are only allowed if they have NO attributes +} + +window.htmlDecode = function (input) { + return new DOMParser().parseFromString(input, "text/html")?.documentElement?.textContent ?? input; +} diff --git a/resources/js/instantsearch.js b/resources/js/instantsearch.js index 55809f789..b6ef2a951 100644 --- a/resources/js/instantsearch.js +++ b/resources/js/instantsearch.js @@ -1,6 +1,5 @@ import { defineAsyncComponent } from 'vue' import { addQuery } from './stores/useSearchHistory' -import highlight from 'vue-instantsearch/vue3/es/src/components/Highlight.vue.js' document.addEventListener('vue:loaded', function (event) { const vue = event.detail.vue @@ -17,7 +16,6 @@ document.addEventListener('vue:loaded', function (event) { 'ais-configure', defineAsyncComponent(() => import('vue-instantsearch/vue3/es/src/components/Configure.js')), ) - vue.component('ais-highlight', highlight) vue.component( 'ais-autocomplete', defineAsyncComponent(() => import('vue-instantsearch/vue3/es/src/components/Autocomplete.vue.js')), diff --git a/resources/views/components/highlight.blade.php b/resources/views/components/highlight.blade.php index 343ede54d..9fa549b37 100644 --- a/resources/views/components/highlight.blade.php +++ b/resources/views/components/highlight.blade.php @@ -1,15 +1,6 @@ -@props(['attribute', 'item' => 'item', 'highlightTag' => 'mark']) +@props(['attribute', 'item' => 'item']) -@if (in_array($attribute, config('rapidez.searchkit.highlight_attributes'))) - merge([ - 'v-bind:hit' => $item, - 'attribute' => $attribute, - 'highlighted-tag-name' => $highlightTag, - ]) }}> - {{-- - TODO: This doesn't render the HTML correctly - See: /gear/fitness-equipment.html - --}} -@else - -@endif + diff --git a/resources/views/listing/partials/filter/select.blade.php b/resources/views/listing/partials/filter/select.blade.php index 0f715b0ab..5ef26fa1a 100644 --- a/resources/views/listing/partials/filter/select.blade.php +++ b/resources/views/listing/partials/filter/select.blade.php @@ -20,7 +20,7 @@ class="relative -mx-1 -mt-1" class="items-baseline flex text-base/5" :class="item.isRefined ? 'text' : 'text-muted hover:text'" > - @{{ item.label }} + (@{{ item.count }}) diff --git a/resources/views/listing/partials/filter/selected.blade.php b/resources/views/listing/partials/filter/selected.blade.php index 04c805e7b..996e49889 100644 --- a/resources/views/listing/partials/filter/selected.blade.php +++ b/resources/views/listing/partials/filter/selected.blade.php @@ -40,9 +40,7 @@ class="flex items-center gap-1 py-2 px-3 text-sm rounded-full bg hover:bg-emphas @include('rapidez::listing.partials.filter.selected.boolean') @include('rapidez::listing.partials.filter.selected.swatch') - + diff --git a/src/Models/AbstractAttribute.php b/src/Models/AbstractAttribute.php index e95239fa0..ceb0a64b3 100644 --- a/src/Models/AbstractAttribute.php +++ b/src/Models/AbstractAttribute.php @@ -32,9 +32,10 @@ public function getTable() return $this->table; } - protected function value(): Attribute + protected function rawValue(): Attribute { return Attribute::get(function ($value) { + $value ??= $this->getAttribute('value'); $class = config('rapidez.attribute-models')[$this->backend_model] ?? null; if ($class) { @@ -47,38 +48,36 @@ protected function value(): Attribute }); } - // TODO: Maybe rename this? As this value is mostly used. - // And maybe add the option value keys in the array? - protected function transformedValue(): Attribute + protected function value(): Attribute { - return Attribute::get(function () { + return Attribute::get(function ($value) { if ($this->frontend_input === 'select' || $this->frontend_input === 'multiselect') { - if (is_iterable($this->value)) { + if (is_iterable($value)) { return Arr::map( - iterator_to_array($this->value), + iterator_to_array($value), fn ($value) => $this->options[$value]?->value ?? $value, ); } - return $this->options[$this->value]?->value ?? $this->value; + return $this->options[$value]?->value ?? $value; } - return $this->value; + return $value; }); } protected function label(): Attribute { return Attribute::get(function () { - return is_iterable($this->transformedValue) - ? implode(', ', iterator_to_array($this->transformedValue)) - : $this->transformedValue; + return is_iterable($this->value) + ? implode(', ', iterator_to_array($this->value)) + : $this->value; }); } protected function sortOrder(): Attribute { - return Attribute::get(fn () => $this->options[$this->value]->sort_order ?? null); + return Attribute::get(fn () => $this->options[$this->rawValue]->sort_order ?? null); } public function __toString(): string diff --git a/src/Models/EavAttribute.php b/src/Models/EavAttribute.php index ebfacd597..9cf785862 100644 --- a/src/Models/EavAttribute.php +++ b/src/Models/EavAttribute.php @@ -32,9 +32,7 @@ public static function getCachedCustomer() return Cache::memo()->rememberForever('customer_eav_attributes', function () { return EavAttribute::query() ->leftJoin('customer_eav_attribute', 'customer_eav_attribute.attribute_id', '=', 'eav_attribute.attribute_id') - // TODO: This also needs an entity type check or - // it should just join instead of left join - // but not sure if it's used currently? + ->where('entity_type_id', self::ENTITY_TYPE_CUSTOMER) ->get(); }); } From 1e76efa0d6b9fb28775dd47cfc71ef15c1b3790f Mon Sep 17 00:00:00 2001 From: indykoning Date: Wed, 10 Dec 2025 08:30:16 +0000 Subject: [PATCH 2/6] Apply fixes from Prettier --- resources/js/filters.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/js/filters.js b/resources/js/filters.js index a2378bd52..13829d3bc 100644 --- a/resources/js/filters.js +++ b/resources/js/filters.js @@ -24,10 +24,10 @@ window.url = function (path = '') { } window.stripHtmlTags = function (html, safeTags = ['mark']) { - safeTags = safeTags.map((tag) => tag.replace(/[^a-zA-Z0-9-]/g, '')).filter(Boolean); + safeTags = safeTags.map((tag) => tag.replace(/[^a-zA-Z0-9-]/g, '')).filter(Boolean) return html.replace(new RegExp('<(?!/?(?:' + safeTags.join('|') + ')>)(?:.|\n)*?>', 'gm'), '') // Safe tags are only allowed if they have NO attributes } window.htmlDecode = function (input) { - return new DOMParser().parseFromString(input, "text/html")?.documentElement?.textContent ?? input; + return new DOMParser().parseFromString(input, 'text/html')?.documentElement?.textContent ?? input } From bfafdfec07352b709348f9d48fea026b2f9ee4ba Mon Sep 17 00:00:00 2001 From: indy koning Date: Fri, 12 Dec 2025 09:54:00 +0100 Subject: [PATCH 3/6] get rawValue in value cast --- src/Models/AbstractAttribute.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Models/AbstractAttribute.php b/src/Models/AbstractAttribute.php index ceb0a64b3..2773ffd80 100644 --- a/src/Models/AbstractAttribute.php +++ b/src/Models/AbstractAttribute.php @@ -51,6 +51,7 @@ protected function rawValue(): Attribute protected function value(): Attribute { return Attribute::get(function ($value) { + $value = $this->rawValue ?? $value; if ($this->frontend_input === 'select' || $this->frontend_input === 'multiselect') { if (is_iterable($value)) { return Arr::map( From 29a820d9735697fe57dabc0faeb3733a96c7abe6 Mon Sep 17 00:00:00 2001 From: indy koning Date: Fri, 12 Dec 2025 10:01:18 +0100 Subject: [PATCH 4/6] Use getter skipping mutators --- src/Models/AbstractAttribute.php | 2 +- tests/Unit/ProductTest.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Models/AbstractAttribute.php b/src/Models/AbstractAttribute.php index 2773ffd80..746cd0ea8 100644 --- a/src/Models/AbstractAttribute.php +++ b/src/Models/AbstractAttribute.php @@ -35,7 +35,7 @@ public function getTable() protected function rawValue(): Attribute { return Attribute::get(function ($value) { - $value ??= $this->getAttribute('value'); + $value ??= $this->getAttributeFromArray('value'); $class = config('rapidez.attribute-models')[$this->backend_model] ?? null; if ($class) { diff --git a/tests/Unit/ProductTest.php b/tests/Unit/ProductTest.php index 8b5c211db..4cdb3a95e 100644 --- a/tests/Unit/ProductTest.php +++ b/tests/Unit/ProductTest.php @@ -161,7 +161,8 @@ public function product_has_custom_attributes() { $product = Product::find(10); - $this->assertEqualsCanonicalizing([8, 11], iterator_to_array($product->activity->value), 'Activity attribute on product 10 did not return the right values.'); + $this->assertEqualsCanonicalizing([8, 11], iterator_to_array($product->activity->rawValue), 'Activity attribute on product 10 did not return the right values.'); + $this->assertEqualsCanonicalizing(['Gym', 'Yoga'], iterator_to_array($product->activity->value), 'Activity attribute on product 10 did not return the right values.'); $this->assertEquals('Gym, Yoga', $product->activity->label, 'Activity attribute on product 10 did not yield the right text output.'); $this->assertEquals('Savvy Shoulder Tote', $product->name->label, 'Name attribute on product 10 did not return the right value.'); From bc4edb6a176c482135f5d1e5bff897485de4891b Mon Sep 17 00:00:00 2001 From: indy koning Date: Fri, 12 Dec 2025 10:12:37 +0100 Subject: [PATCH 5/6] Fixed toArray --- src/Models/AbstractAttribute.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Models/AbstractAttribute.php b/src/Models/AbstractAttribute.php index 746cd0ea8..d437202e5 100644 --- a/src/Models/AbstractAttribute.php +++ b/src/Models/AbstractAttribute.php @@ -83,11 +83,11 @@ protected function sortOrder(): Attribute public function __toString(): string { - return $this->label; + return (string) $this->label; } public function toArray() { - return $this->transformed_value; + return $this->value; } } From fe8e7a363aa005a1245ac3b8e5c076ebb867ec88 Mon Sep 17 00:00:00 2001 From: indy koning Date: Fri, 12 Dec 2025 10:23:24 +0100 Subject: [PATCH 6/6] Use rawValue for superAttributes --- resources/js/components/Search/Autocomplete.vue | 1 - resources/views/listing/products.blade.php | 1 - src/Models/Traits/Product/HasSuperAttributes.php | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/resources/js/components/Search/Autocomplete.vue b/resources/js/components/Search/Autocomplete.vue index b13a6c396..f56b310f8 100644 --- a/resources/js/components/Search/Autocomplete.vue +++ b/resources/js/components/Search/Autocomplete.vue @@ -5,7 +5,6 @@ import InstantSearchMixin from './InstantSearchMixin.vue' import InstantSearch from 'vue-instantsearch/vue3/es/src/components/InstantSearch' import Hits from 'vue-instantsearch/vue3/es/src/components/Hits.js' import Configure from 'vue-instantsearch/vue3/es/src/components/Configure.js' -import highlight from 'vue-instantsearch/vue3/es/src/components/Highlight.vue.js' import Autocomplete from 'vue-instantsearch/vue3/es/src/components/Autocomplete.vue.js' import Index from 'vue-instantsearch/vue3/es/src/components/Index.js' import Stats from 'vue-instantsearch/vue3/es/src/components/Stats.vue.js' diff --git a/resources/views/listing/products.blade.php b/resources/views/listing/products.blade.php index 45380eb93..c008d470d 100644 --- a/resources/views/listing/products.blade.php +++ b/resources/views/listing/products.blade.php @@ -5,7 +5,6 @@ 'SortBy.vue', 'Stats.vue', 'Hits.vue', - 'Highlight.vue', 'Pagination.vue', 'Hits.js' ]) diff --git a/src/Models/Traits/Product/HasSuperAttributes.php b/src/Models/Traits/Product/HasSuperAttributes.php index 8d051e3a7..3055e085b 100644 --- a/src/Models/Traits/Product/HasSuperAttributes.php +++ b/src/Models/Traits/Product/HasSuperAttributes.php @@ -41,7 +41,7 @@ public function superAttributeValues(): Attribute $child->entity_id => $child->{$attribute->attribute_code}, ]) ->sortBy('sort_order') - ->groupBy('value') + ->groupBy('rawValue') ->map(fn ($children, $value) => (object) [ 'children' => $children->pluck('entity_id'), 'value' => $value,