Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion resources/js/components/Search/Autocomplete.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
9 changes: 9 additions & 0 deletions resources/js/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
2 changes: 0 additions & 2 deletions resources/js/instantsearch.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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')),
Expand Down
19 changes: 5 additions & 14 deletions resources/views/components/highlight.blade.php
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
@props(['attribute', 'item' => 'item', 'highlightTag' => 'mark'])
@props(['attribute', 'item' => 'item'])

@if (in_array($attribute, config('rapidez.searchkit.highlight_attributes')))
<ais-highlight {{ $attributes->merge([
'v-bind:hit' => $item,
'attribute' => $attribute,
'highlighted-tag-name' => $highlightTag,
]) }}></ais-highlight>
{{--
TODO: This doesn't render the HTML correctly
See: /gear/fitness-equipment.html
--}}
@else
<span {{ $attributes }} v-text="{{ $item }}.{{ $attribute }}"></span>
@endif
<span
{{ $attributes }}
v-html="window.stripHtmlTags(window.htmlDecode({{ $item }}?._highlightResult?.{{ $attribute }}?.value || {{ $item }}.{{ $attribute }}))"
></span>
2 changes: 1 addition & 1 deletion resources/views/listing/partials/filter/select.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
<span v-html="window.stripHtmlTags(item.label)"></span>
<span class="block ml-0.5 text-xs" data-testid="listing-filter-count">
(@{{ item.count }})
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ class="flex items-center gap-1 py-2 px-3 text-sm rounded-full bg hover:bg-emphas
<template v-if="false"></template>
@include('rapidez::listing.partials.filter.selected.boolean')
@include('rapidez::listing.partials.filter.selected.swatch')
<template v-else>
@{{ refinement.label }}
</template>
<span v-html="window.stripHtmlTags(refinement.label)"></span>
</a>
</li>
</template>
Expand Down
1 change: 0 additions & 1 deletion resources/views/listing/products.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
'SortBy.vue',
'Stats.vue',
'Hits.vue',
'Highlight.vue',
'Pagination.vue',
'Hits.js'
])
Expand Down
30 changes: 15 additions & 15 deletions src/Models/AbstractAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -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->getAttributeFromArray('value');
$class = config('rapidez.attribute-models')[$this->backend_model] ?? null;

if ($class) {
Expand All @@ -47,47 +48,46 @@ 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) {
$value = $this->rawValue ?? $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
{
return $this->label;
return (string) $this->label;
}

public function toArray()
{
return $this->transformed_value;
return $this->value;
}
}
4 changes: 1 addition & 3 deletions src/Models/EavAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/Models/Traits/Product/HasSuperAttributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion tests/Unit/ProductTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.');
Expand Down
Loading