Meilisearch Laravel Livewire component - unchecked checkboxes disappearing when using wire:model - php

I'm building a full page Livewire component that filters Meilisearch results.
When I check one of the filters the remaning checkboxes disappear, and I am struggling to understand why. I followed a tutorial to figure out how to build the bulk of the component and in the tutorial the behaviour didn't occur. I've deconstructed the component to the bare minimum and the behaviour happens as soon as I wire up the checkboxes.
Here is the component:
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\CourseDate;
use App\Models\Course;
class CourseBrowser extends Component
{
public $queryFilters = [];
public $priceRange = [
'min' => null,
'max' => null
];
public function mount()
{
$this->queryFilters = [
'venue' => [],
'type' => [],
'category' => [],
'days' => [],
'supplier' => []
];
}
public function render()
{
$search = CourseDate::search(
'',
function ($meilisearch, string $query, array $options) {
$filters = collect($this->queryFilters)
->filter(fn ($filter) => !empty($filter))
->recursive()
->map(function ($value, $key) {
return $value->map(fn ($value) => $key . ' = "' . $value . '"');
})
->flatten()
->join(' AND ');
$options['facets'] = ['venue', 'category', 'type', 'supplier', 'days'];
$options['filter'] = null;
if ($filters) {
$options['filter'] = $filters;
}
if ($this->priceRange['max']) {
$options['filter'] .= (isset($options['filter']) ? ' AND ' : '') . 'price <= ' . $this->priceRange['max'];
}
return $meilisearch->search($query, $options);
}
)->raw();
$coursedates = CourseDate::find(collect($search['hits'])->pluck('id'));
$minPrice = Course::all()->min('price');
$maxPrice = Course::all()->max('price');
$this->priceRange['min'] = $this->priceRange['min'] ?: $minPrice;
$this->priceRange['max'] = $this->priceRange['max'] ?: $maxPrice;
return view('livewire.course-browser')
->with(
[
'coursedates' => $coursedates,
'filters' => $search['facetDistribution'],
'minPrice' => $minPrice,
'maxPrice' => $maxPrice,
]
);
}
}
Here is the Blade view. I had originally posted an edited section showing just the checkboxes but I have edited this question to show the view in its entirety.
<div>
<div class="mb-4 text-gray-500">
Home / Courses
</div>
<div class="align-center mt-8 mb-10 flex items-center justify-between">
<div class="text-3xl font-bold">
All Courses
</div>
</div>
<div class="grid w-full grid-cols-4 gap-6">
<div class="col-span-1 rounded-sm bg-white shadow">
<div class="space-y-2 px-6 py-6">
<h6 class="text-sm font-bold uppercase">Filters</h6>
<hr class="mt-4 mb-6 border-gray-200 pb-2">
<h6 class="text-sm font-bold uppercase">Price </h6>
<div class="flex gap-1">
<input type="range"
min="{{ $minPrice }}"
max="{{ $maxPrice }}"
step="25"
class="accent-pink-700"
wire:model="priceRange.max" />
<span class="font-sm">
(£{{ $priceRange['max'] }})
</span>
</div>
#foreach ($filters as $title => $filter)
<h6 class="pt-2 text-sm font-bold uppercase">{{ Str::title($title) }}</h6>
#foreach ($filter as $option => $count)
<div wire:ignore.self
class="flex items-center space-x-2">
<input type="checkbox"
class="h-4 w-4 rounded border-gray-300 text-sm text-pink-600 focus:ring-0 focus:ring-offset-0"
wire:model="queryFilters.{{ $title }}"
id="{{ $title }}_{{ strtolower($option) }}"
value="{{ $option }}">
<label class="text-sm"
for="{{ $title }}_{{ strtolower($option) }}">{{ $option }} ({{ $count }})</label>
</div>
#endforeach
#endforeach
</div>
</div>
<div class="col-span-3">
<h6 class="text-md my-4 mb-4">{{ $coursedates->count() }} {{ Str::plural('course', $coursedates) }} matching your filters</h6>
#forelse ($coursedates as $coursedate)
<div wire:loading.class="opacity-50"
class="relative mb-6 w-full rounded-sm bg-white shadow">
<span class="absolute top-2 left-2 rounded bg-pink-800 px-3 py-1 font-bold text-white">{{ $coursedate->course->category->name }}</span>
<span class="absolute top-2 right-2 rounded bg-pink-600 px-3 py-1 font-bold text-white">{{ $coursedate->course->supplier->name }}</span>
<a href="{{ route('course.show', $coursedate->course->slug) }}">
<img src="{{ asset('storage/courses/images/' . $coursedate->course->title_image) }}"
alt="{{ $coursedate->course->slug }}"
class="h-40 w-full object-cover">
</a>
<div class="px-6 py-4">
<h6 class="mb-2 text-lg font-bold">{{ $coursedate->course->name }} </h6>
<p class="mb-6 text-sm leading-tight">{{ $coursedate->course->tagline }}</p>
{{-- Pills Container --}}
<div class="flex">
<div class="mr-1 flex items-center rounded bg-pink-300 font-bold text-pink-800">
<div class="flex h-full items-center rounded-l bg-pink-400 px-2 py-1">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="h-4 w-4 text-pink-200">
<path d="M4.5 3.75a3 3 0 00-3 3v.75h21v-.75a3 3 0 00-3-3h-15z" />
<path fill-rule="evenodd"
d="M22.5 9.75h-21v7.5a3 3 0 003 3h15a3 3 0 003-3v-7.5zm-18 3.75a.75.75 0 01.75-.75h6a.75.75 0 010 1.5h-6a.75.75 0 01-.75-.75zm.75 2.25a.75.75 0 000 1.5h3a.75.75 0 000-1.5h-3z"
clip-rule="evenodd" />
</svg>
</div>
<div class="flex h-full items-center rounded bg-pink-300 px-3 py-1">
£{{ $coursedate->course->price }}
</div>
</div>
<div class="mr-1 flex items-center rounded bg-pink-500 font-bold text-white">
<div class="flex h-full items-center rounded-l bg-pink-600 px-2 py-1">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="h-4 w-4">
<path fill-rule="evenodd"
d="M11.54 22.351l.07.04.028.016a.76.76 0 00.723 0l.028-.015.071-.041a16.975 16.975 0 001.144-.742 19.58 19.58 0 002.683-2.282c1.944-1.99 3.963-4.98 3.963-8.827a8.25 8.25 0 00-16.5 0c0 3.846 2.02 6.837 3.963 8.827a19.58 19.58 0 002.682 2.282 16.975 16.975 0 001.145.742zM12 13.5a3 3 0 100-6 3 3 0 000 6z"
clip-rule="evenodd" />
</svg>
</div>
<div class="flex h-full items-center rounded bg-pink-500 px-3 py-1">
{{ $coursedate->venue->city }}
</div>
</div>
#foreach ($coursedate->actualdates as $dates)
#if ($coursedate->course->number_of_days > 1)
<div class="mr-1 flex items-center rounded bg-pink-200 font-bold text-pink-800">
<div class="flex h-full items-center gap-1 rounded-l bg-pink-200 px-2 py-1 text-xs">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="inline-flex h-5 w-5">
<path d="M12.75 12.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM7.5 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM8.25 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM9.75 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM10.5 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM12.75 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM14.25 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM15 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM16.5 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM15 12.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM16.5 13.5a.75.75 0 100-1.5.75.75 0 000 1.5z" />
<path fill-rule="evenodd"
d="M6.75 2.25A.75.75 0 017.5 3v1.5h9V3A.75.75 0 0118 3v1.5h.75a3 3 0 013 3v11.25a3 3 0 01-3 3H5.25a3 3 0 01-3-3V7.5a3 3 0 013-3H6V3a.75.75 0 01.75-.75zm13.5 9a1.5 1.5 0 00-1.5-1.5H5.25a1.5 1.5 0 00-1.5 1.5v7.5a1.5 1.5 0 001.5 1.5h13.5a1.5 1.5 0 001.5-1.5v-7.5z"
clip-rule="evenodd" />
</svg>
{{ $dates->label }}
</div>
<div class="flex h-full items-center rounded-r bg-pink-700 px-2 py-1 text-pink-100">
{{ $dates->date->format('jS M Y') }}
</div>
</div>
#else
<div class="mr-1 flex items-center rounded bg-pink-200 text-sm font-bold text-pink-800">
<div class="flex h-full rounded-l bg-pink-200 py-1 px-2 text-sm font-bold text-pink-800">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="inline-flex h-5 w-5">
<path d="M12.75 12.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM7.5 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM8.25 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM9.75 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM10.5 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM12.75 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM14.25 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM15 17.25a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM16.5 15.75a.75.75 0 100-1.5.75.75 0 000 1.5zM15 12.75a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM16.5 13.5a.75.75 0 100-1.5.75.75 0 000 1.5z" />
<path fill-rule="evenodd"
d="M6.75 2.25A.75.75 0 017.5 3v1.5h9V3A.75.75 0 0118 3v1.5h.75a3 3 0 013 3v11.25a3 3 0 01-3 3H5.25a3 3 0 01-3-3V7.5a3 3 0 013-3H6V3a.75.75 0 01.75-.75zm13.5 9a1.5 1.5 0 00-1.5-1.5H5.25a1.5 1.5 0 00-1.5 1.5v7.5a1.5 1.5 0 001.5 1.5h13.5a1.5 1.5 0 001.5-1.5v-7.5z"
clip-rule="evenodd" />
</svg>
</div>
<div class="flex h-full items-center rounded-r bg-pink-700 px-2 py-1 text-pink-100"> {{ $dates->date->format('jS M Y') }} </div>
</div>
#endif
#endforeach
</div>
<div class="mt-6 mb-4">
</div>
</div>
</div>
#empty
No Course Dates to show
#endforelse
</div>
</div>
</div>
The component in itself works fine. But as you can see in the gif below, the non selected checkboxes disappear, so I cannot apply multiple filters.
I suspect the problem lies in the way I'm storing the filters in the array but I can't see the woods for the trees at the moment. The filtering itself currently works, and any attempt to change the format of the array results in it not working at all.
Thanks in advance.

After some perusal of the Meilisearch docs:
facetDistribution contains an object for every given facet. For each of these facets, there is another object containing all the different values and the count of matching documents. Note that zero values will not be returned: if there are no romance movies matching the query, romance is not displayed.
That suggests that the behaviour is normal 😳
On updating the search results, the facetDistribution array is updated with the filters belonging to the results that have been returned, and doesn't retain the ones with a zero value.
Before filtering:
"facetDistribution":{
"category":{
"Hair":8
},
"days":{
"1":4,
"2":4
},
"supplier":{
"Matrix":8
},
"type":{
"In Person":8
},
"venue":{
"Colchester":2,
"Ipswich":1,
"Norwich":1,
"Peterborough":1,
"Romford":3
}
},
After filtering:
"facetDistribution":{
"category":{
"Hair":2
},
"days":{
"1":1,
"2":1
},
"supplier":{
"Matrix":2
},
"type":{
"In Person":2
},
"venue":{
"Colchester":2
}
},
To be honest, I'd expected to retain the filters with a value of zero so I could re-filter and find courses that were present in Colchester OR Norwich, for example. In fact the tutorial I learned the process from appeared to show this behaviour.
I'm going to rewatch it again in case I misunderstood something!
Or perhaps the older version of Meilisearch being used in the tutorial allowed this?

There are certain bugs in Livewire, and this is also one of them.
Try enclosing the each checkbox in a div.
<div class="">
<input type="checkbox" wire:model="queryFilters.{{ $title }}"
id="{{ $title }}_{{ strtolower($option) }}"
value="{{ $option }}">
</div>
If the issue still presist, add wire:ignore.self to container
<div class="" wire:ignore.self>
......
</div>

I've dumped the contents of the array as it's being filled up on the page
Each time you check a box it adds the value into the array. Obviously In the case of the Venue group of checkboxes I can't add a second value because the unselected ones disappear. But what I'd expect is an array that looks like this for example:
$this->queryFilters = [
'venue' => ['Norwich', 'Peterborough'],
'type' => ['In Person'],
'category' => ['Hair'],
'days' => [],
'supplier' => []
];
I'd then map through the values and build the filter string for Mielesearch, which happens in the closure futher down.
"venue = "Norwich" AND venue = "Peterborough" AND type = "In Person" AND category = "Hair""

Related

Bootstrap Card image enlargement

I want the pictures in the card to enlarge when clicked. I couldn't do it anyway.
I tried a lot but I couldn't. I can enlarge normal photos with Lightbox. However, when I click on the cards, the picture does not enlarge.
The codes are a bit complicated but I hope you can help.
HomeController.php
public function store(Request $request)
{
if(Settings::find('active_upload')->value == 0){
toastr()->warning(__('main.new_entries_paused'));
return redirect('/');
}
Validator::make($request->all(), [
'title' => 'required|string|max:255',
'story' => 'required|string|min:'.Settings::find('minimum_characters')->value.'|max:'.Settings::find('maximum_characters')->value,
'tags' => 'nullable',
'category_id' => 'required',
'genders_id' => 'required',
'age' => 'required',
'photo' => 'nullable|image|mimes:jpeg,png,jpg|max:2048'
],[
'title.required' => 'Bir Başlık Giriniz.',
'title.max' => 'Başlık 255 Karakterden Uzun Olamaz.',
'story.required' => 'Boş bırakılmamalıdır.',
'story.min' => 'Girilen Metin '.Settings::find('minimum_characters')->value.' Karakterden Fazla Olmalıdır.',
'story.max' => 'Girilen Metin ' .Settings::find('maximum_characters')->value.' Karakterden Az Olmalıdır.',
'category_id.required' => 'Kategori Seçmeniz Gerekiyor.',
'photo.image' => 'Lütfen Resim Dosyası Yükleyiniz.',
'photo.mimes' => 'Dosya Biçimi jpg,png veya jpeg olmalıdır.',
])->validateWithBag('write');
// if word censored is active
if(Settings::find('words_censored')->value == 1) {
$censor = new CensorWords;
$badwords = $censor->setDictionary(base_path('/vendor/snipe/banbuilder/src/dict/dictionary.php'));
$string = $censor->censorString($request->story)['clean'];
} else {
$string = $request->story;
}
//
// create item
$create_item = Items::create([
'title' => $request->title,
'story' => $string,
'slug' => $this->make_slug($request->title),
'status' => Settings::find('new_entries')->value,
'user_id' => Auth::id(),
'category_id' => $request->category_id,
'genders_id' => $request->genders_id,
'age' => $request->age,
]);
if ($create_item) {
// if exists, upload photo
if ($request->hasFile('photo')) {
// upload original
$path = $request->file('photo')->store('photos');
// store photo
$storePhoto = Photos::create([
'item_id' => $create_item->id,
'filename' => $path
]);
}
// create tags
$tags = explode(",", $request->tags);
$create_item->tag($tags);
if(Settings::find('status_points')->value == 1){
if(Settings::find('status_points_new_entry')->value == 1){
Points::create([
'user_id' => Auth::id(),
'point_type' => "new_entry",
'score' => Settings::find('points_new_entry')->value,
'item_id' => $create_item->id
]);
}
}
if(Settings::find('new_entries')->value == 1){
toastr()->success(__('main.toast_your_post_has_been_posted'));
return redirect('/');
} else {
toastr()->warning(__('main.toast_post_in_moderation'));
return redirect('/');
}
} else {
toastr()->error(__('main.toast_there_are_problems_try_again'));
return redirect('/')->withErrors($validator, 'write')->withInput();
}
}
Items.php
public function photo()
{
return $this->hasOne(Photos::class, 'item_id');
}
item.blade.php
<div class="col-lg-3">
<div class="card mb-3 shadow #if($item->featured == 1) card-featured #endif">
<div class="card-header {{ $item->gender->bg_color }}">
<div class="col text-truncate">
<a href="{{ route('show', ['id'=>$item->id, 'slug'=>$item->slug]) }}" class="text-white text-decoration-none">
<h3 class="card-title">
{{ $item->gender->name }} - {{ __('main.years_old', ['age' => $item->age]) }} - {{ $item->category->name }}
</h3>
</a>
</div>
<div class="col-auto">
<div class="dropdown">
<a href="#" class="card-dropdown text-white" data-bs-toggle="dropdown" aria-expanded="false">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-filled " width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><circle cx="5" cy="9" r="1" /><circle cx="5" cy="15" r="1" /><circle cx="12" cy="9" r="1" /><circle cx="12" cy="15" r="1" /><circle cx="19" cy="9" r="1" /><circle cx="19" cy="15" r="1" /></svg>
</a>
<div class="dropdown-menu dropdown-menu-end">
#if(Auth::check() && Auth::id() == $item->user_id || Auth::check() && Auth::user()->isAdministrator())
<!-- actions enabled by the post user -->
<a href="{{ route('delete_user_post', $item->id) }}" onclick="return confirm('Do you confirm this operation?');" class="dropdown-item">
<svg xmlns="http://www.w3.org/2000/svg" class="icon dropdown-item-icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><line x1="4" y1="7" x2="20" y2="7" /><line x1="10" y1="11" x2="10" y2="17" /><line x1="14" y1="11" x2="14" y2="17" /><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12" /><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3" /></svg>
{{ __('main.card_delete') }}
</a>
<!-- end actions enabled by the post user -->
#endif
<a href="{{ route('report', $item->id) }}" onclick="return confirm('Do you confirm this operation?');" class="dropdown-item text-warning">
<svg xmlns="http://www.w3.org/2000/svg" class="icon dropdown-item-icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><line x1="5" y1="5" x2="5" y2="21" /><line x1="19" y1="5" x2="19" y2="14" /><path d="M5 5a5 5 0 0 1 7 0a5 5 0 0 0 7 0" /><path d="M5 14a5 5 0 0 1 7 0a5 5 0 0 0 7 0" /></svg>
{{ __('main.card_report') }}
</a>
</div>
</div>
</div>
</div>
#if($item->photo()->exists())
<div class="card-img-top img-responsive img-responsive-16by9" style="background-image: url({{ asset('storage/app/'.$item->photo->filename) }}); opacity: 0.8;"></div>
#endif
<div class="card-body">
<h3 class="card-title text-truncate text-muted">
{{ $item->title }}
</h3>
<p class="h3">
{{ Str::limit($item->story, $story_preview_chars) }}
</p>
<p>
#foreach($item->tags as $tag)
<a href="{{ route('tag', ['slug' => $tag->slug]) }}" class="badge bg-azure-lt">
{{ $tag->slug }}
</a>
#endforeach
</p>
</div>
<div class="card-footer">
<div class="row g-2 mb-2 align-items-center">
<div class="col">
<a href="javascript:void(0);" onclick="likePost({{ $item->id }})">
<svg xmlns="http://www.w3.org/2000/svg" id="like-icon-{{ $item->id }}" class="icon #if(Auth::check()) #if(Auth::user()->hasLiked($item)) icon-filled text-danger #else text-muted #endif #else text-muted #endif" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M19.5 13.572l-7.5 7.428l-7.5 -7.428m0 0a5 5 0 1 1 7.5 -6.566a5 5 0 1 1 7.5 6.572" /></svg>
</a>
</div>
<!-- share -->
<div class="col-auto">
<span class="dropdown">
<a href="" class="text-muted" data-bs-boundary="viewport" data-bs-toggle="dropdown" aria-expanded="false">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M11 7h-5a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-5" /><line x1="10" y1="14" x2="20" y2="4" /><polyline points="15 4 20 4 20 9" /></svg>
</a>
<div class="dropdown-menu">
<a class="dropdown-item text-success strong" href="whatsapp://send?text={{ url('view/'.$item->id.'/'.$item->slug) }}" data-action="share/whatsapp/share">
<svg xmlns="http://www.w3.org/2000/svg" class="icon dropdown-item-icon text-success strong" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 21l1.65 -3.8a9 9 0 1 1 3.4 2.9l-5.05 .9" /><path d="M9 10a0.5 .5 0 0 0 1 0v-1a0.5 .5 0 0 0 -1 0v1a5 5 0 0 0 5 5h1a0.5 .5 0 0 0 0 -1h-1a0.5 .5 0 0 0 0 1" /></svg>
{{ __('Whatsapp') }}
</a>
<a class="dropdown-item" href="http://www.facebook.com/sharer/sharer.php?u={{ url('view/'.$item->id.'/'.$item->slug) }}&t={{ $item->title }}" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" class="icon dropdown-item-icon text-primary" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 10v4h3v7h4v-7h3l1 -4h-4v-2a1 1 0 0 1 1 -1h3v-4h-3a5 5 0 0 0 -5 5v2h-3" /></svg>
{{ __('Facebook') }}
</a>
<a class="dropdown-item" href="https://twitter.com/share?url={{ url('view/'.$item->id.'/'.$item->slug) }}&text={{ $item->title }}" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" class="icon dropdown-item-icon text-info" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M22 4.01c-1 .49 -1.98 .689 -3 .99c-1.121 -1.265 -2.783 -1.335 -4.38 -.737s-2.643 2.06 -2.62 3.737v1c-3.245 .083 -6.135 -1.395 -8 -4c0 0 -4.182 7.433 4 11c-1.872 1.247 -3.739 2.088 -6 2c3.308 1.803 6.913 2.423 10.034 1.517c3.58 -1.04 6.522 -3.723 7.651 -7.742a13.84 13.84 0 0 0 .497 -3.753c-.002 -.249 1.51 -2.772 1.818 -4.013z" /></svg>
{{ __('Twitter') }}
</a>
</div>
</span>
</div>
<!-- end share -->
<!-- bookmark -->
<div class="col-auto">
<a href="javascript:void(0);" onclick="savePost({{ $item->id }})">
<svg xmlns="http://www.w3.org/2000/svg" id="save-icon-{{ $item->id }}" class="icon #if(Auth::check()) #if(Auth::user()->hasFavorited($item)) icon-filled #else text-muted #endif #else text-muted #endif" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 4h6a2 2 0 0 1 2 2v14l-5 -3l-5 3v-14a2 2 0 0 1 2 -2" /></svg>
</a>
</div>
<!-- end bookmark -->
</div>
<div class="row small">
<div class="col-auto d-flex align-items-center pe-2">
<span class="text-muted text-truncate" style="font-size: 14px;">
<span id="like-{{ $item->id }}">#json($item->likers()->count())</span> {{ __('main.card_likes') }}
</span>
</div>
<div class="col d-flex align-items-center pe-2" style="font-size: 14px;">
<span class="text-muted text-truncate">
{{ $item->itemView() }} {{ __('main.card_views') }}
</span>
</div>
<div class="col-auto align-items-center" style="font-size: 14px;">
<a href="{{ route('show', ['id'=>$item->id, 'slug'=>$item->slug]) }}" class="text-muted text-truncate">
{{ __('main.card_comments', ['count' => $item->comments()->count()]) }} <i class="fa-regular fa-comment"></i>
</a>
</div>
</div>
</div>
</div>
</div>
Solution
#if($item->photo()->exists())
<a href="{{ asset('storage/app/'.$item->photo->filename) }}" data-lightbox="photos">
<div class="card-img-top img-responsive img-responsive-16by9" data-lightbox="photos" style="background-image: url({{ asset('storage/app/'.$item->photo->filename) }}); opacity: 0.8;"></div>
</a>
#endif

blade components, did I do it right?

Is it true, in terms of architecture I did?
I'm not sure if I can use model access in section view.
Do you need to change something.
<!-- Navigation Links -->
<x-section :sectionNumber="1"></x-section>
section.blade.php
#php
$sections = \App\Models\BasicSection::all()->where('section_number', '=', $sectionNumber);
#endphp
<div class="hidden sm:flex sm:items-center sm:ml-10">
<x-dropdown :href="route('dashboard')" :active="request()->routeIs('dashboard')">
<x-slot name="trigger">
<button
class="flex items-center text-sm font-medium text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out">
{{ __($sectionNumber) }}
<div class="ml-1">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"/>
</svg>
</div>
</button>
</x-slot>
<x-slot name="content">
#foreach($sections as $section)
<x-dropdown-link :href="route('dashboard')">
{{ $section->name }}
</x-dropdown-link>
#endforeach
</x-slot>
</x-dropdown>
</div>

Nested comment system runs too many queries ( laravel & livewire )

I've built a nested comment system and it works fine, but I have a problem.
when I want to show the comments, it runs too many queries. for example if I have 10 comments and two replies each, laravel debugger shows 40+ queries, and I want to reduce them, here is my code :
Query
$comments = $post->mentions()->with(['author', 'reply'])->get();
Relationship in Post.php Model
public function mentions()
{
return $this->hasMany(Comment::class, 'post_id')->where('approved',1)->where('parent_id',0);
}
Comments Relationships
public function author()
{
return $this->belongsTo(User::class, 'author_id')->select('id', 'name', 'avatar', 'role');
}
public function reply()
{
return $this->hasMany(Comment::class, 'parent_id')->where('approved', 1);
}
Blade File
#foreach ($comments as $comment)
<div x-data="{reply:false}" class="break-words bg-white border-2 rounded-lg p-4 my-5" id="answer-{1}">
<div wire:ignore class="flex justify-between items-center">
<div class="flex items-center">
<img class="rounded-full w-20 border-4 #if ($comment->author->role == 'administrator') border-blue-500 #else border-gray-500 #endif " src="{{ $comment->author->avatar }}"
alt="{{ $comment->author->name }}">
<div>
<div class="flex pr-3">
<a href="/#username"
class="font-bold text-lg text-gray-800 hover:text-gray-900">{{ $comment->author->name }}</a>
#if ($comment->author->role == 'administrator')
<img class="w-5 mr-1" src="/img/verified.svg" alt="" title="admin">
#endif
</div>
<p class="font-light cursor-default text-gray-700 pt-1 pr-3">
{{ $comment->created_at->diffForHumans() }}</p>
</div>
</div>
<svg #click="reply = true" xmlns="http://www.w3.org/2000/svg" class="h-6 cursor-pointer hover:text-rose-600"
fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" />
</svg>
</div>
<div class="comment_body leading-8 pt-3 text-gray-800 cursor-default">
<div wire:ignore>
{!! nl2br($comment->body) !!}
</div>
<div x-cloak x-show="reply">
<form class="py-3" wire:submit.prevent="SubmitComment({{ $comment->id }})">
<textarea wire:model.defer="commentBody"
class="w-full focus:ring-0 bg-zinc-200 border shadow focus:bg-zinc-100 mt-3 p-4 rounded-lg"
id="editor" rows="6" placeholder=""></textarea>
#error('commentBody') <p class="text-red-600 mt-1 text-sm">{{ $message }}</p>
#enderror
<div class="flex mt-3">
<div #click="reply = false"
class="bg-red-600 rounded-lg ml-3 text-white px-3 py-2 cursor-pointer">
Cancel
</div>
<button wire:loading.remove wire:target="SubmitComment" type="submit"
class="inline-block bg-gray-800 hover:bg-gray-900 transition duration-300 px-3 py-2 text-white rounded-md">
Send
</button>
<button wire:loading="" wire:target="SubmitComment" type="button"
class="flex bg-gray-800 hover:bg-gray-900 transition duration-300 px-3 py-2 text-white rounded-md"
disabled>
<span class="flex justify-center">
<svg class="animate-spin w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
</span>
</button>
</div>
</form>
</div>
<div class="my-3">
#include('partials.comment',['comments'=>$comment->reply])
</div>
</div>
</div>
#endforeach
You can use dot seperator to get the nested relationship
$comments = $post->with(['mentions.author','mentions.reply'])
->first()
->mentions;

Laravel 8: Syntax error, unexpected 'endif' (T_ENDIF)

im doing the Laravel 8 from Scratch Tutorial but get the Error "Syntax error, unexpected 'endif' (T_ENDIF)".
Cant find where the problem is because there is not even a "#if" statement in the file which is throwing the error.
The file is the header for filtering posts.
It seems the dropdown component which is responsible for selecting a post category is the reason for the problem, because everything workes fine when i comment this out.
I have shared the Error Code on flareapp.io
Dropdown Component:
#props(['trigger'])
<div x-data="{ show:false }" #click.away="show = false">
<div #click="show = ! show">
{{ $trigger }}
</div>
<div x-show="show" class="py-2 absolute bg-gray-100 mt-2 rounded-xl w-32 z-50" style="display: none">
{{ $slot }}
</div>
</div>
Dropdown-item Component:
#props(['active' => false])
#php
$classes = 'block text-left px-3 text-sm loading-6 hover:bg-blue-500 focues:bg-blue-500 hover:text-white focus:text-white';
if ($active) {
$classes += 'bg-blue-500 text-white';
}
#endphp
<a {{ $attributes(['class' => $classes]) }}>
{{ $slot }}
</a>
Posts header:
<header class="max-w-xl mx-auto mt-20 text-center">
<h1 class="text-4xl">
Latest <span class="text-blue-500">Laravel From Scratch</span> News
</h1>
<h2 class="inline-flex mt-2">By Lary Laracore <img src="./images/lary-head.svg" alt="Head of Lary the mascot"></h2>
<p class="text-sm mt-14">
Another year. Another update. We're refreshing the popular Laravel series with new content.
I'm going to keep you guys up to speed with what's going on!
</p>
<div class="space-y-2 lg:space-y-0 lg:space-x-4 mt-8">
<!-- Category -->
<div class="relative lg:inline-flex bg-gray-100 rounded-xl">
<x-dropdown>
<x-slot name="trigger">
<button class="py-2 pl-3 pr-9 text-sm font-semibold w-full lg:w-32 text-left flex lg:inline-flex">
{{ isset($currentCategory) ? ucwords($currentCategory->name) : 'Categories' }}
<svg class="transform -rotate-90 absolute pointer-events-none" style="right: 12px;" width="22"
height="22" viewBox="0 0 22 22">
<g fill="none" fill-rule="evenodd">
<path stroke="#000" stroke-opacity=".012" stroke-width=".5" d="M21 1v20.16H.84V1z">
</path>
<path fill="#222"
d="M13.854 7.224l-3.847 3.856 3.847 3.856-1.184 1.184-5.04-5.04 5.04-5.04z"></path>
</g>
</svg>
</button>
</x-slot>
<x-dropdown-item href="/blog/" :active="request()->routeIs(" none")">
All
</x-dropdown-item>
#foreach ($categories as $category)
<x-dropdown-item href="/blog/categories/{{ $category->slug }}" :active="request()->is("
categories/{$category->slug}")"
>
{{ ucwords($category->name) }}
</x-dropdown-item>
#endforeach
</x-dropdown>
</div>
<!-- Other Filters -->
<div class="relative flex lg:inline-flex items-center bg-gray-100 rounded-xl">
<select class="flex-1 appearance-none bg-transparent py-2 pl-3 pr-9 text-sm font-semibold">
<option value="category" disabled selected>Other Filters
</option>
<option value="foo">Foo
</option>
<option value="bar">Bar
</option>
</select>
{{-- ERROR THROWN IN NEXT LINE --}}
<svg class="transform -rotate-90 absolute pointer-events-none" style="right: 12px;" width="22" height="22"
viewBox="0 0 22 22">
<g fill="none" fill-rule="evenodd">
<path stroke="#000" stroke-opacity=".012" stroke-width=".5" d="M21 1v20.16H.84V1z">
</path>
<path fill="#222" d="M13.854 7.224l-3.847 3.856 3.847 3.856-1.184 1.184-5.04-5.04 5.04-5.04z">
</path>
</g>
</svg>
</div>
<!-- Search -->
<div class="relative flex lg:inline-flex items-center bg-gray-100 rounded-xl px-3 py-2">
<form method="GET" action="#">
<input type="text" name="search" placeholder="Find something"
class="bg-transparent placeholder-black font-semibold text-sm">
</form>
</div>
</div>
</header>
Try this
#foreach ($categories as $category)
<x-dropdown-item href="/blog/categories/{{ $category->slug }}" :active="request()->is('categories/' . $category->slug)">
{{ ucwords($category->name) }}
</x-dropdown-item>
#endforeach

I can't center bootstrap svg icons

I want the svg icons to be centered, instead, I see them shifted to the right.If you can help I will be very grateful.
here is a screenshot
My welcome.blade.php
#extends('layouts.layout')
#section('title')Головна сторінка#endsection
#section ('main_content')
<div class="row py-lg-5 ">
<div class="col-lg-6 col-md-8 mx-auto">
<h1 class="fw-light" style="text-align: center;">Головна сторінка</h1>
<p class="lead text-muted" style="text-align: center;">Вітаємо на головній сторінці сайту.</p>
<hr class="my-4" style="height:2px;border-width:0;color:orange;background-color:orange">
<div class="row">
<div class="col-lg-4">
<svg xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true" width="125" height="130" fill="currentColor" focusable="false" class="bi bi-briefcase" viewBox="0 0 16 16" preserveAspectRatio="xMinYMid">
<path d="M6.5 1A1.5 1.5 0 0 0 5 2.5V3H1.5A1.5 1.5 0 0 0 0 4.5v8A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-8A1.5 1.5 0 0 0 14.5 3H11v-.5A1.5 1.5 0 0 0 9.5 1h-3zm0 1h3a.5.5 0 0 1 .5.5V3H6v-.5a.5.5 0 0 1 .5-.5zm1.886 6.914L15 7.151V12.5a.5.5 0 0 1-.5.5h-13a.5.5 0 0 1-.5-.5V7.15l6.614 1.764a1.5 1.5 0 0 0 .772 0zM1.5 4h13a.5.5 0 0 1 .5.5v1.616L8.129 7.948a.5.5 0 0 1-.258 0L1 6.116V4.5a.5.5 0 0 1 .5-.5z"/>
</svg>
<h2 style="text-align: center;">Бізнес</h2>
<p style="text-align: center;">Таблиця з бізнесами.</p>
<p><a class="btn btn-outline-warning" href="/business">Перейти на сторінку »</a></p>
</div>
<div class="col px-md-5"></div>
<div class="col-lg-4">
<svg xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true" width="125" height="130" fill="currentColor" focusable="false" class="bi bi-person-circle" viewBox="0 0 16 16" preserveAspectRatio="xMinYMid">
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
</svg>
<h2 style="text-align: center;">Користувачі</h2>
<p style="text-align: center;">Таблиця з користувачами.</p>
<p><a class="btn btn-outline-warning" href="/users">Перейти на сторінку »</a></p>
</div>
</div>
</div>
</div>
#endsection
Put every svg in a div first and in that div add the following classes:
<div class='d-flex justify-content-center'> <svg>{Your Svg Icon}</svg> </div>

Categories