Repository: crimsonstrife/streamer.live
Pull request #131: fix: apply html_entity_decode to collection and product text fields f…
Author: login: crimsonstrife
Labels: name: enhancement
description: New feature or request
name: php
description: Pull requests that update Php code
Created: 2025-03-19T18:57:41Z | Merged: 2025-03-19T19:30:55Z

…or consistent decoding

## Summary by Sourcery

Bug Fixes:
- Fixes inconsistent decoding of HTML entities in product and collection names and descriptions.


Diff:
diff --git a/_ide_helper_models.php b/_ide_helper_models.php
new file mode 100644
index 00000000..be62edf5
--- /dev/null
+++ b/_ide_helper_models.php
@@ -0,0 +1,404 @@
+<?php
+
+// @formatter:off
+// phpcs:ignoreFile
+/**
+ * A helper file for your Eloquent Models
+ * Copy the phpDocs from this file to the correct Model,
+ * And remove them from this file, to prevent double declarations.
+ *
+ * @author Barry vd. Heuvel <barryvdh@gmail.com>
+ */
+
+namespace App\Models{
+    /**
+     *
+     *
+     * @property int $id
+     * @property string $provider_id
+     * @property string $provider
+     * @property string $name
+     * @property string $slug
+     * @property string|null $description
+     * @property \Illuminate\Support\Carbon|null $created_at
+     * @property \Illuminate\Support\Carbon|null $updated_at
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Product> $products
+     * @property-read int|null $products_count
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection newModelQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection newQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection query()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection whereCreatedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection whereDescription($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection whereId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection whereName($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection whereProvider($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection whereProviderId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection whereSlug($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Collection whereUpdatedAt($value)
+     * @mixin \Eloquent
+     */
+    class Collection extends \Eloquent
+    {
+    }
+}
+
+namespace App\Models{
+    /**
+     *
+     *
+     * @property int $id
+     * @property \Illuminate\Support\Carbon|null $created_at
+     * @property \Illuminate\Support\Carbon|null $updated_at
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Event newModelQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Event newQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Event query()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Event whereCreatedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Event whereId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Event whereUpdatedAt($value)
+     * @mixin \Eloquent
+     */
+    class Event extends \Eloquent implements \Guava\Calendar\Contracts\Eventable
+    {
+    }
+}
+
+namespace App\Models{
+    /**
+     *
+     *
+     * @property int $id
+     * @property string|null $name
+     * @property string|null $email
+     * @property string|null $ip_address
+     * @property string|null $user_agent
+     * @property bool $is_spammer
+     * @property \Illuminate\Support\Carbon|null $created_at
+     * @property \Illuminate\Support\Carbon|null $updated_at
+     * @property \Illuminate\Support\Carbon|null $deleted_at
+     * @property int|null $deleted_by
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Reaction> $reactions
+     * @property-read int|null $reactions_count
+     * @method static Builder<static>|Guest createOrUpdate(\App\Data\GuestData $data)
+     * @method static Builder<static>|Guest newModelQuery()
+     * @method static Builder<static>|Guest newQuery()
+     * @method static Builder<static>|Guest onlyTrashed()
+     * @method static Builder<static>|Guest query()
+     * @method static Builder<static>|Guest whereCreatedAt($value)
+     * @method static Builder<static>|Guest whereDeletedAt($value)
+     * @method static Builder<static>|Guest whereDeletedBy($value)
+     * @method static Builder<static>|Guest whereEmail($value)
+     * @method static Builder<static>|Guest whereId($value)
+     * @method static Builder<static>|Guest whereIpAddress($value)
+     * @method static Builder<static>|Guest whereIsSpammer($value)
+     * @method static Builder<static>|Guest whereName($value)
+     * @method static Builder<static>|Guest whereUpdatedAt($value)
+     * @method static Builder<static>|Guest whereUserAgent($value)
+     * @method static Builder<static>|Guest withTrashed()
+     * @method static Builder<static>|Guest withoutTrashed()
+     * @mixin \Eloquent
+     */
+    class Guest extends \Eloquent implements \App\Contracts\CommenterContract
+    {
+    }
+}
+
+namespace App\Models{
+    /**
+     *
+     *
+     * @property int $id
+     * @property int|null $blog_author_id
+     * @property int|null $blog_category_id
+     * @property string $title
+     * @property string $slug
+     * @property string|null $excerpt
+     * @property string|null $banner
+     * @property string $content
+     * @property \Illuminate\Support\Carbon|null $published_at
+     * @property \Illuminate\Support\Carbon|null $created_at
+     * @property \Illuminate\Support\Carbon|null $updated_at
+     * @property-read mixed $banner_url
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \LakM\Comments\Models\Comment> $comments
+     * @property-read int|null $comments_count
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Reaction> $reactions
+     * @property-read int|null $reactions_count
+     * @property \Illuminate\Database\Eloquent\Collection<int, \Spatie\Tags\Tag> $tags
+     * @property-read int|null $tags_count
+     * @method static Builder<static>|Post draft()
+     * @method static Builder<static>|Post newModelQuery()
+     * @method static Builder<static>|Post newQuery()
+     * @method static Builder<static>|Post published()
+     * @method static Builder<static>|Post query()
+     * @method static Builder<static>|Post whereBanner($value)
+     * @method static Builder<static>|Post whereBlogAuthorId($value)
+     * @method static Builder<static>|Post whereBlogCategoryId($value)
+     * @method static Builder<static>|Post whereContent($value)
+     * @method static Builder<static>|Post whereCreatedAt($value)
+     * @method static Builder<static>|Post whereExcerpt($value)
+     * @method static Builder<static>|Post whereId($value)
+     * @method static Builder<static>|Post wherePublishedAt($value)
+     * @method static Builder<static>|Post whereSlug($value)
+     * @method static Builder<static>|Post whereTitle($value)
+     * @method static Builder<static>|Post whereUpdatedAt($value)
+     * @method static Builder<static>|Post withAllTags(\ArrayAccess|\Spatie\Tags\Tag|array|string $tags, ?string $type = null)
+     * @method static Builder<static>|Post withAllTagsOfAnyType($tags)
+     * @method static Builder<static>|Post withAnyTags(\ArrayAccess|\Spatie\Tags\Tag|array|string $tags, ?string $type = null)
+     * @method static Builder<static>|Post withAnyTagsOfAnyType($tags)
+     * @method static Builder<static>|Post withAnyTagsOfType(array|string $type)
+     * @method static Builder<static>|Post withoutTags(\ArrayAccess|\Spatie\Tags\Tag|array|string $tags, ?string $type = null)
+     * @mixin \Eloquent
+     */
+    class Post extends \Eloquent implements \App\Contracts\CommentableContract
+    {
+    }
+}
+
+namespace App\Models{
+    /**
+     *
+     *
+     * @property int $id
+     * @property string $provider_id
+     * @property string $provider
+     * @property string $name
+     * @property string $slug
+     * @property string|null $description
+     * @property string|null $state
+     * @property string|null $access
+     * @property numeric $price
+     * @property numeric|null $compare_at_price
+     * @property string|null $external_url
+     * @property \Illuminate\Support\Carbon|null $created_at
+     * @property \Illuminate\Support\Carbon|null $updated_at
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Collection> $collections
+     * @property-read int|null $collections_count
+     * @property-read string $primary_image_url
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProductImage> $images
+     * @property-read int|null $images_count
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProductVariant> $variants
+     * @property-read int|null $variants_count
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product newModelQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product newQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product query()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereAccess($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereCompareAtPrice($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereCreatedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereDescription($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereExternalUrl($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereName($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product wherePrice($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereProvider($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereProviderId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereSlug($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereState($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereUpdatedAt($value)
+     * @mixin \Eloquent
+     */
+    class Product extends \Eloquent
+    {
+    }
+}
+
+namespace App\Models{
+    /**
+     *
+     *
+     * @property int $id
+     * @property int|null $product_id
+     * @property int|null $variant_id
+     * @property string|null $provider_id
+     * @property string $url
+     * @property string|null $local_path
+     * @property int|null $width
+     * @property int|null $height
+     * @property \Illuminate\Support\Carbon|null $created_at
+     * @property \Illuminate\Support\Carbon|null $updated_at
+     * @property-read string $image_url
+     * @property-read \App\Models\Product|null $product
+     * @property-read \App\Models\ProductVariant|null $variant
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage newModelQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage newQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage query()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage whereCreatedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage whereHeight($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage whereId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage whereLocalPath($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage whereProductId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage whereProviderId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage whereUpdatedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage whereUrl($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage whereVariantId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductImage whereWidth($value)
+     * @mixin \Eloquent
+     */
+    class ProductImage extends \Eloquent
+    {
+    }
+}
+
+namespace App\Models{
+    /**
+     *
+     *
+     * @property int $id
+     * @property string $provider_id
+     * @property int $product_id
+     * @property string $name
+     * @property string|null $sku
+     * @property numeric $price
+     * @property numeric|null $compare_at_price
+     * @property string|null $stock_status
+     * @property int $stock_count
+     * @property int|null $weight
+     * @property string|null $weight_unit
+     * @property int|null $length
+     * @property int|null $width
+     * @property int|null $height
+     * @property string|null $dimension_unit
+     * @property \Illuminate\Support\Carbon|null $created_at
+     * @property \Illuminate\Support\Carbon|null $updated_at
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProductImage> $images
+     * @property-read int|null $images_count
+     * @property-read \App\Models\Product $product
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant newModelQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant newQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant query()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereCompareAtPrice($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereCreatedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereDimensionUnit($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereHeight($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereLength($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereName($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant wherePrice($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereProductId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereProviderId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereSku($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereStockCount($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereStockStatus($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereUpdatedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereWeight($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereWeightUnit($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|ProductVariant whereWidth($value)
+     * @mixin \Eloquent
+     */
+    class ProductVariant extends \Eloquent
+    {
+    }
+}
+
+namespace App\Models{
+    /**
+     * Class Reaction
+     *
+     * This class represents a reaction model that can be associated with various entities in the application.
+     * It supports polymorphic relationships for the owner and the re-actable entities.
+     *
+     * @package App\Models
+     * @property int $id
+     * @property string $owner_type
+     * @property int $owner_id
+     * @property string $reactable_type
+     * @property int $reactable_id
+     * @property ReactionType $type
+     * @property int $point_value
+     * @property \Illuminate\Support\Carbon|null $created_at
+     * @property \Illuminate\Support\Carbon|null $updated_at
+     * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $owner
+     * @property-read \Illuminate\Database\Eloquent\Model|\Eloquent $reactable
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction newModelQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction newQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction query()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction whereCreatedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction whereId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction whereOwnerId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction whereOwnerType($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction wherePointValue($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction whereReactableId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction whereReactableType($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction whereType($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|Reaction whereUpdatedAt($value)
+     * @mixin \Eloquent
+     */
+    class Reaction extends \Eloquent
+    {
+    }
+}
+
+namespace App\Models{
+    /**
+     *
+     *
+     * @property int $id
+     * @property string $username
+     * @property string|null $first_name
+     * @property string|null $last_name
+     * @property string $display_name
+     * @property string $email
+     * @property \Illuminate\Support\Carbon|null $email_verified_at
+     * @property \Illuminate\Support\Carbon $birthdate
+     * @property string|null $pronouns
+     * @property string|null $location
+     * @property string $password
+     * @property string|null $two_factor_secret
+     * @property string|null $two_factor_recovery_codes
+     * @property string|null $two_factor_confirmed_at
+     * @property string|null $remember_token
+     * @property int|null $current_team_id
+     * @property string|null $profile_photo_path
+     * @property \Illuminate\Support\Carbon|null $created_at
+     * @property \Illuminate\Support\Carbon|null $updated_at
+     * @property string|null $custom_fields
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \Mchev\Banhammer\Models\Ban> $bans
+     * @property-read int|null $bans_count
+     * @property-read string $filament_banhammer_title
+     * @property-read string|null $full_name
+     * @property-read string $name
+     * @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
+     * @property-read int|null $notifications_count
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \Spatie\Permission\Models\Permission> $permissions
+     * @property-read int|null $permissions_count
+     * @property-read string $profile_photo_url
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \Spatie\Permission\Models\Role> $roles
+     * @property-read int|null $roles_count
+     * @property-read \Illuminate\Database\Eloquent\Collection<int, \Laravel\Sanctum\PersonalAccessToken> $tokens
+     * @property-read int|null $tokens_count
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User banned(bool $banned = true)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User bannedByType(string $className)
+     * @method static \Database\Factories\UserFactory factory($count = null, $state = [])
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User newModelQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User newQuery()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User notBanned()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User permission($permissions, $without = false)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User query()
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User role($roles, $guard = null, $without = false)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereBansMeta(string $key, $value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereBirthdate($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCreatedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCurrentTeamId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCustomFields($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereDisplayName($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmail($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmailVerifiedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereFirstName($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereId($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereLastName($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereLocation($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePassword($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereProfilePhotoPath($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePronouns($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRememberToken($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereTwoFactorConfirmedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereTwoFactorRecoveryCodes($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereTwoFactorSecret($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUpdatedAt($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUsername($value)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User withoutPermission($permissions)
+     * @method static \Illuminate\Database\Eloquent\Builder<static>|User withoutRole($roles, $guard = null)
+     * @mixin \Eloquent
+     */
+    class User extends \Eloquent implements \Filament\Models\Contracts\HasAvatar
+    {
+    }
+}
diff --git a/app/Http/Controllers/StoreController.php b/app/Http/Controllers/StoreController.php
index c5f66d46..e036c709 100644
--- a/app/Http/Controllers/StoreController.php
+++ b/app/Http/Controllers/StoreController.php
@@ -33,7 +33,8 @@ public function index()
     public function showCollection($slug)
     {
         // Find collection in the database
-        $collection = Collection::where('slug', $slug)->firstOrFail();
+        $collection = Collection::where('slug', $slug)
+            ->firstOrFail();
 
         // Get products associated with this collection
         $products = $collection->products()->with('images', 'variants')->get();
@@ -46,8 +47,10 @@ public function showCollection($slug)
      */
     public function showProduct($slug)
     {
-        // Find product in the database
-        $product = Product::where('slug', $slug)->with('images', 'variants')->firstOrFail();
+        // Retrieve the product using the slug and eager load the relations
+        $product = Product::where('slug', $slug)
+            ->with('images', 'variants')
+            ->firstOrFail();
 
         return view('store.product', compact('product'));
     }
diff --git a/app/Models/Collection.php b/app/Models/Collection.php
index feaad947..eb76d373 100644
--- a/app/Models/Collection.php
+++ b/app/Models/Collection.php
@@ -48,4 +48,14 @@ public function products(): BelongsToMany
     {
         return $this->belongsToMany(Product::class, 'collection_products');
     }
+
+    public function getNameAttribute()
+    {
+        return html_entity_decode($this->attributes['name']);
+    }
+
+    public function getDescriptionAttribute()
+    {
+        return html_entity_decode($this->attributes['description']);
+    }
 }
diff --git a/app/Models/Product.php b/app/Models/Product.php
index ec6331a4..b1819072 100644
--- a/app/Models/Product.php
+++ b/app/Models/Product.php
@@ -100,4 +100,14 @@ public function getPrimaryImageUrlAttribute(): string
         $image = $this->images()->first();
         return $image ? Storage::url($image->local_path ?? $image->url) : '/default-image.jpg';
     }
+
+    public function getNameAttribute()
+    {
+        return html_entity_decode($this->attributes['name']);
+    }
+
+    public function getDescriptionAttribute()
+    {
+        return html_entity_decode($this->attributes['description']);
+    }
 }
diff --git a/app/Models/ProductVariant.php b/app/Models/ProductVariant.php
index f5fa7328..2af30d80 100644
--- a/app/Models/ProductVariant.php
+++ b/app/Models/ProductVariant.php
@@ -90,4 +90,9 @@ public function images(): HasMany
     {
         return $this->hasMany(ProductImage::class, 'variant_id');
     }
+
+    public function getNameAttribute()
+    {
+        return html_entity_decode($this->attributes['name']);
+    }
 }
diff --git a/resources/views/store/collection.blade.php b/resources/views/store/collection.blade.php
index 365848f5..7bc63478 100644
--- a/resources/views/store/collection.blade.php
+++ b/resources/views/store/collection.blade.php
@@ -7,7 +7,7 @@
 
     <div class="container py-6">
         <h3 class="mb-4 text-lg font-semibold">
-            {{ __('Products in ') }}{{$collection['name'] ?? 'this collection'}}
+            {{ __('Products in ') }}{{$collection->name ?? 'this collection'}}
         </h3>
 
         @if ($collection->products->isEmpty())
@@ -22,9 +22,9 @@
                     @endphp
                     <div class="mb-4 col-md-4">
                         <div class="shadow-sm card">
-                            <img src="{{ $image }}" class="card-img-top" alt="{!! html_entity_decode($product->name) !!} Image">
+                            <img src="{{ $image }}" class="card-img-top" alt="{{ $product->name }} Image">
                             <div class="card-body">
-                                <h5 class="card-title">{!! html_entity_decode($product->name) !!}</h5>
+                                <h5 class="card-title">{{ $product->name }}</h5>
                                 <p class="card-text text-muted">
                                     {{ number_format($product->price, 2) }} USD
                                 </p>
diff --git a/resources/views/store/product.blade.php b/resources/views/store/product.blade.php
index 80da9971..46b5783c 100644
--- a/resources/views/store/product.blade.php
+++ b/resources/views/store/product.blade.php
@@ -9,7 +9,7 @@ class="w-full max-w-md rounded">
             <img src="{{ asset('images/default-product.png') }}" alt="Default Image" class="w-full max-w-md rounded">
         @endif
 
-        <p class="mt-4">{!! html_entity_decode($product->description) !!}</p>
+        <p class="mt-4">{{ $product->description }}</p>
 
         @if ($product->variants->isNotEmpty())
             <form method="POST" action="{{ route('store.cart.add') }}">
@@ -20,7 +20,7 @@ class="w-full max-w-md rounded">
                 <select name="variant_id" id="variant" class="mt-2 form-control">
                     @foreach ($product->variants as $variant)
                         <option value="{{ $variant->id }}">
-                            {!! html_entity_decode($variant->name) !!} - {{ number_format($variant->price, 2) }} USD
+                            {{ $variant->name }} - {{ number_format($variant->price, 2) }} USD
                         </option>
                     @endforeach
                 </select>


Reviews:
user: login: sourcery-ai[bot]
body: Hey @crimsonstrife - I've reviewed your changes - here's some feedback:

**Overall Comments**:
- Consider using a model observer or mutator to automatically decode the HTML entities when the model is retrieved.

<details>
<summary>Here's what I looked at during the review</summary>

- 🟡 **General issues**: 2 issues found
- 🟢 **Security**: all looks good
- 🟢 **Testing**: all looks good
- 🟢 **Complexity**: all looks good
- 🟢 **Documentation**: all looks good
</details>

***

<details>
<summary>Sourcery is free for open source - if you like our reviews please consider sharing them ✨</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)
- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)

</details>

<sub>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T18:59:17Z
user: login: crimsonstrife
state: COMMENTED
author_association: OWNER
submitted_at: 2025-03-19T19:02:48Z
user: login: sourcery-ai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T19:03:00Z
user: login: sourcery-ai[bot]
body: Hey @crimsonstrife - I've reviewed your changes - here's some feedback:

**Overall Comments**:
- Consider if you should apply this same decoding to other text fields in your models.
- It looks like you're decoding the name attribute in ProductVariant as well, so you should probably decode the description there too for consistency.

<details>
<summary>Here's what I looked at during the review</summary>

- 🟡 **General issues**: 3 issues found
- 🟢 **Security**: all looks good
- 🟢 **Testing**: all looks good
- 🟢 **Complexity**: all looks good
- 🟢 **Documentation**: all looks good
</details>

***

<details>
<summary>Sourcery is free for open source - if you like our reviews please consider sharing them ✨</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20got%20an%20instant%20code%20review%20from%20%40SourceryAI%2C%20and%20it%20was%20brilliant%21%20It%27s%20free%20for%20open%20source%20and%20has%20a%20free%20trial%20for%20private%20code.%20Check%20it%20out%20https%3A//sourcery.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https://sourcery.ai)
- [Facebook](https://www.facebook.com/sharer/sharer.php?u=https://sourcery.ai)

</details>

<sub>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T19:20:11Z

Review comments:
diff_hunk: @@ -33,11 +33,17 @@ public function index()
     public function showCollection($slug)
     {
         // Find collection in the database
-        $collection = Collection::where('slug', $slug)->firstOrFail();
+        $collection = Collection::where('slug', $slug)
+            ->with('products.images', 'products.variants')
path: app/Http/Controllers/StoreController.php
user: login: sourcery-ai[bot]
body: **question (performance):** Double-check nested eager loading of products.

Since you are later retrieving products with their images and variants (using ->with('images', 'variants')), confirm that eager-loading 'products.images' and 'products.variants' at the collection level is necessary and not duplicating effort. If the intention is to use the collection's relationship for other processing, it may be clearer to keep these separate.
created_at: 2025-03-19T18:59:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -33,11 +33,17 @@ public function index()
     public function showCollection($slug)
     {
         // Find collection in the database
-        $collection = Collection::where('slug', $slug)->firstOrFail();
+        $collection = Collection::where('slug', $slug)
+            ->with('products.images', 'products.variants')
+            ->firstOrFail();
 
         // Get products associated with this collection
         $products = $collection->products()->with('images', 'variants')->get();
 
+        // Apply html_entity_decode to text fields to ensure consistent decoding
+        $collection->name = html_entity_decode($collection->name);
+        $collection->description = html_entity_decode($collection->description);
path: app/Http/Controllers/StoreController.php
user: login: sourcery-ai[bot]
body: **suggestion:** Consider centralizing HTML entity decoding.

Since similar html_entity_decode calls are made in both collection and product loads, you might consider abstracting this logic (e.g., via model accessors or a dedicated helper) to keep the controller lean and ensure consistency across models.
created_at: 2025-03-19T18:59:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -33,11 +33,17 @@ public function index()
     public function showCollection($slug)
     {
         // Find collection in the database
-        $collection = Collection::where('slug', $slug)->firstOrFail();
+        $collection = Collection::where('slug', $slug)
+            ->with('products.images', 'products.variants')
+            ->firstOrFail();
 
         // Get products associated with this collection
         $products = $collection->products()->with('images', 'variants')->get();
 
+        // Apply html_entity_decode to text fields to ensure consistent decoding
+        $collection->name = html_entity_decode($collection->name);
+        $collection->description = html_entity_decode($collection->description);
path: app/Http/Controllers/StoreController.php
user: login: crimsonstrife
body: > **suggestion:** Consider centralizing HTML entity decoding.
> 
> Since similar html_entity_decode calls are made in both collection and product loads, you might consider abstracting this logic (e.g., via model accessors or a dedicated helper) to keep the controller lean and ensure consistency across models.

Do you have a recommendation on implementing this?
created_at: 2025-03-19T19:02:48Z
author_association: OWNER
diff_hunk: @@ -33,11 +33,17 @@ public function index()
     public function showCollection($slug)
     {
         // Find collection in the database
-        $collection = Collection::where('slug', $slug)->firstOrFail();
+        $collection = Collection::where('slug', $slug)
+            ->with('products.images', 'products.variants')
+            ->firstOrFail();
 
         // Get products associated with this collection
         $products = $collection->products()->with('images', 'variants')->get();
 
+        // Apply html_entity_decode to text fields to ensure consistent decoding
+        $collection->name = html_entity_decode($collection->name);
+        $collection->description = html_entity_decode($collection->description);
path: app/Http/Controllers/StoreController.php
user: login: sourcery-ai[bot]
body: Okay, great question! There are a few ways we could centralize this. Here are a couple of options, with my recommendation first:

**1. Model Accessors (Recommended):**

   *   **How it works:** Define a custom accessor in both the `Collection` and `Product` models. Accessors allow you to modify attribute values when they are retrieved from the model.

   *   **Example (in `Collection.php`):**

       ```php
       // In Collection model
       public function getNameAttribute($value)
       {
           return html_entity_decode($value);
       }

       public function getDescriptionAttribute($value)
       {
           return html_entity_decode($value);
       }
       ```

       Do the same in `Product.php` for the `name` and `description` attributes.

   *   **Usage:**  After defining the accessors, you don't need to explicitly call `html_entity_decode` in your controller.  The decoding will happen automatically whenever you access `$collection->name`, `$collection->description`, `$product->name`, or `$product->description`.  This keeps your controller code clean:

       ```php
       // Controller code becomes simpler:
       $collection = Collection::where('slug', $slug)->firstOrFail();
       // No need to decode here, it's done automatically by the accessor
       ```

   *   **Benefits:**
        *   Clean and readable controller code.
        *   Consistent decoding logic across the application.
        *   Easy to maintain and update.
        *   Eloquent handles the attribute retrieval, so you don't need to worry about when the decoding happens.

   *   **Considerations:**  If you have other places where you're directly accessing the raw (un-decoded) values from the database, you'll need to be mindful that the accessors will *always* return the decoded values.  If you need the raw values in some cases, you might need a different approach.

**2. Helper Function:**

   *   **How it works:** Create a helper function (e.g., in `app/Helpers.php` or a dedicated file) that performs the HTML entity decoding.

   *   **Example:**

       ```php
       // app/Helpers.php (or similar)
       if (! function_exists('decode_html')) {
           function decode_html($value) {
               return html_entity_decode($value);
           }
       }
       ```

   *   **Usage:**

       ```php
       // In your controller:
       $collection->name = decode_html($collection->name);
       $collection->description = decode_html($collection->description);
       ```

   *   **Benefits:**
        *   Reusable across the application.
        *   Simple to implement.

   *   **Drawbacks:**
        *   You still need to remember to call the helper function in your controller and other places.
        *   Less elegant than using accessors.

**Why I recommend Accessors:**

Accessors provide a more elegant and maintainable solution because they encapsulate the decoding logic within the models themselves. This ensures that the `name` and `description` attributes are *always* decoded when accessed, regardless of where they are used in the application.  This reduces the risk of forgetting to decode the values in certain places and leads to more consistent data handling.

To implement the accessor approach, you would:

1.  Add the `getNameAttribute` and `getDescriptionAttribute` methods to both the `Collection` and `Product` models.
2.  Remove the `html_entity_decode` calls from your controller.

Let me know if you'd like a more detailed example or have any other questions!

created_at: 2025-03-19T19:03:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -48,4 +48,14 @@ public function products(): BelongsToMany
     {
         return $this->belongsToMany(Product::class, 'collection_products');
     }
+
+    public function getNameAttribute()
+    {
+        return html_entity_decode($this->name);
path: app/Models/Collection.php
user: login: sourcery-ai[bot]
body: **issue (bug_risk):** Potential recursion in getNameAttribute accessor.

Calling $this->name inside the getNameAttribute accessor could trigger the same accessor recursively. Consider retrieving the underlying value directly from the model’s attributes (e.g. using $this->attributes['name']) to prevent infinite recursion.
created_at: 2025-03-19T19:20:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -100,4 +100,14 @@ public function getPrimaryImageUrlAttribute(): string
         $image = $this->images()->first();
         return $image ? Storage::url($image->local_path ?? $image->url) : '/default-image.jpg';
     }
+
+    public function getNameAttribute()
+    {
+        return html_entity_decode($this->name);
path: app/Models/Product.php
user: login: sourcery-ai[bot]
body: **issue (bug_risk):** Accessor for name in Product may cause recursive calls.

Using $this->name in the getNameAttribute accessor risks invoking the same accessor again. It is safer to access the raw attribute value via $this->attributes['name'].
created_at: 2025-03-19T19:20:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -90,4 +90,9 @@ public function images(): HasMany
     {
         return $this->hasMany(ProductImage::class, 'variant_id');
     }
+
+    public function getNameAttribute()
+    {
+        return html_entity_decode($this->name);
path: app/Models/ProductVariant.php
user: login: sourcery-ai[bot]
body: **issue (bug_risk):** Avoid recursion in ProductVariant's getNameAttribute.

Similar to the other models, directly accessing $this->name here could lead to a recursive call of the accessor. Consider using $this->attributes['name'] to retrieve the raw value.
created_at: 2025-03-19T19:20:11Z
author_association: CONTRIBUTOR

PR comments:
user: login: sourcery-ai[bot]
created_at: 2025-03-19T18:57:46Z
body: <!-- Generated by sourcery-ai[bot]: start review_guide -->

## Reviewer's Guide by Sourcery

This pull request ensures consistent decoding of HTML entities in product and collection names and descriptions by applying `html_entity_decode` at the model level and removing redundant decoding in the Blade templates. It modifies the `Collection`, `Product`, and `ProductVariant` models to include attribute accessors that decode the `name` and `description` fields. Additionally, it updates the `collection.blade.php` and `product.blade.php` templates to remove the `html_entity_decode` calls, relying on the model attribute accessors for decoding.

#### Updated class diagram for the Collection model

```mermaid
classDiagram
    class Collection {
        -id
        -provider_id
        -provider
        -name
        -slug
        -description
        -created_at
        -updated_at
        +products()
        +getNameAttribute()
        +getDescriptionAttribute()
    }
```

#### Updated class diagram for the Product model

```mermaid
classDiagram
    class Product {
        -id
        -provider_id
        -provider
        -name
        -slug
        -description
        -state
        -access
        -price
        -compare_at_price
        -external_url
        -created_at
        -updated_at
        +collections()
        +images()
        +variants()
        +getPrimaryImageUrlAttribute()
        +getNameAttribute()
        +getDescriptionAttribute()
    }
```

#### Updated class diagram for the ProductVariant model

```mermaid
classDiagram
    class ProductVariant {
        -id
        -provider_id
        -product_id
        -name
        -sku
        -price
        -compare_at_price
        -stock_status
        -stock_count
        -weight
        -weight_unit
        -length
        -width
        -height
        -dimension_unit
        -created_at
        -updated_at
        +product()
        +images()
        +getNameAttribute()
    }
```

### File-Level Changes

| Change | Details | Files |
| ------ | ------- | ----- |
| Applied `html_entity_decode` to the `name` and `description` attributes of the `Collection` model. | <ul><li>Added a `getNameAttribute` method to decode the `name` attribute.</li><li>Added a `getDescriptionAttribute` method to decode the `description` attribute.</li></ul> | `app/Models/Collection.php` |
| Applied `html_entity_decode` to the `name` and `description` attributes of the `Product` model. | <ul><li>Added a `getNameAttribute` method to decode the `name` attribute.</li><li>Added a `getDescriptionAttribute` method to decode the `description` attribute.</li></ul> | `app/Models/Product.php` |
| Applied `html_entity_decode` to the `name` attribute of the `ProductVariant` model. | <ul><li>Added a `getNameAttribute` method to decode the `name` attribute.</li></ul> | `app/Models/ProductVariant.php` |
| Removed redundant `html_entity_decode` calls in Blade templates. | <ul><li>Removed `html_entity_decode` from product name and image alt text in `store/collection.blade.php`.</li><li>Removed `html_entity_decode` from product description and variant name in `store/product.blade.php`.</li></ul> | `resources/views/store/collection.blade.php`<br/>`resources/views/store/product.blade.php` |

---

<details>
<summary>Tips and commands</summary>

#### Interacting with Sourcery

- **Trigger a new review:** Comment `@sourcery-ai review` on the pull request.
- **Continue discussions:** Reply directly to Sourcery's review comments.
- **Generate a GitHub issue from a review comment:** Ask Sourcery to create an
  issue from a review comment by replying to it. You can also reply to a
  review comment with `@sourcery-ai issue` to create an issue from it.
- **Generate a pull request title:** Write `@sourcery-ai` anywhere in the pull
  request title to generate a title at any time. You can also comment
  `@sourcery-ai title` on the pull request to (re-)generate the title at any time.
- **Generate a pull request summary:** Write `@sourcery-ai summary` anywhere in
  the pull request body to generate a PR summary at any time exactly where you
  want it. You can also comment `@sourcery-ai summary` on the pull request to
  (re-)generate the summary at any time.
- **Generate reviewer's guide:** Comment `@sourcery-ai guide` on the pull
  request to (re-)generate the reviewer's guide at any time.
- **Resolve all Sourcery comments:** Comment `@sourcery-ai resolve` on the
  pull request to resolve all Sourcery comments. Useful if you've already
  addressed all the comments and don't want to see them anymore.
- **Dismiss all Sourcery reviews:** Comment `@sourcery-ai dismiss` on the pull
  request to dismiss all existing Sourcery reviews. Especially useful if you
  want to start fresh with a new review - don't forget to comment
  `@sourcery-ai review` to trigger a new review!
- **Generate a plan of action for an issue:** Comment `@sourcery-ai plan` on
  an issue to generate a plan of action for it.

#### Customizing Your Experience

Access your [dashboard](https://app.sourcery.ai) to:
- Enable or disable review features such as the Sourcery-generated pull request
  summary, the reviewer's guide, and others.
- Change the review language.
- Add, remove or edit custom review instructions.
- Adjust other review settings.

#### Getting Help

- [Contact our support team](mailto:support@sourcery.ai) for questions or feedback.
- Visit our [documentation](https://docs.sourcery.ai) for detailed guides and information.
- Keep in touch with the Sourcery team by following us on [X/Twitter](https://x.com/SourceryAI), [LinkedIn](https://www.linkedin.com/company/sourcery-ai/) or [GitHub](https://github.com/sourcery-ai).

</details>

<!-- Generated by sourcery-ai[bot]: end review_guide -->
author_association: CONTRIBUTOR
user: login: deepsource-io[bot]
created_at: 2025-03-19T19:00:11Z
body: <div><p>Here's the code health analysis summary for commits <code>0b4d918..f6b0b84</code>. <a href="https://app.deepsource.com/gh/crimsonstrife/streamer.live/run/be30e628-4cc7-4ee5-bf72-2a584596a145/">View details on DeepSource</a>&nbsp;↗.</p><h3>Analysis Summary</h3><table><thead><tr><th>Analyzer</th><th>Status</th><th>Summary</th><th>Link</th></tr></thead><tbody><tr><td><img src="https://static.deepsource.com/analyzer_logos/javascript.svg" alt="DeepSource JavaScript Logo" width="16px" height="16px"/><strong>JavaScript</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/crimsonstrife/streamer.live/run/be30e628-4cc7-4ee5-bf72-2a584596a145/javascript/">View Check</a>&nbsp;↗</td></tr><tr><td><img src="https://static.deepsource.com/analyzer_logos/php.svg" alt="DeepSource PHP Logo" width="16px" height="16px"/><strong>PHP</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/crimsonstrife/streamer.live/run/be30e628-4cc7-4ee5-bf72-2a584596a145/php/">View Check</a>&nbsp;↗</td></tr></tbody></table><hr/><blockquote><div>💡 If you’re a repository administrator, you can configure the quality gates from the <a href="https://app.deepsource.com/gh/crimsonstrife/streamer.live/settings/reporting">settings</a>.</div></blockquote></div>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-19T19:01:53Z
body: # [Qodana](https://qodana.cloud/projects/dXXnG/reports/wVW02) for PHP

**9985 new problems** were found

| Inspection name | Severity | Problems |
| --- | --- | --- |
| `Undefined variable` | 🔴 Failure | 17 |
| `Class hierarchy checks` | 🔴 Failure | 2 |
| `Undefined class constant` | 🔴 Failure | 1 |
| `Comma expression` | 🔶 Warning | 3819 |
| `Undefined class` | 🔶 Warning | 1410 |
| `Undefined function` | 🔶 Warning | 1018 |
| `Undefined namespace` | 🔶 Warning | 775 |
| `Undefined method` | 🔶 Warning | 596 |
| `Potentially invalid constructor usage` | 🔶 Warning | 411 |
| `Equality operator may cause type coercion` | 🔶 Warning | 324 |
| `Unused local symbol` | 🔶 Warning | 142 |
| `Unused assignment` | 🔶 Warning | 64 |
| `Redundant character escape` | 🔶 Warning | 52 |
| `Statement with empty body` | 🔶 Warning | 29 |
| `Duplicate declaration` | 🔶 Warning | 29 |
| `Referencing mutable variable from closure` | 🔶 Warning | 29 |
| `Undefined property` | 🔶 Warning | 28 |
| `Infinite recursion` | 🔶 Warning | 26 |
| `Redundant local variable` | 🔶 Warning | 15 |
| `Bitwise operator usage` | 🔶 Warning | 12 |
| `Void function return value used` | 🔶 Warning | 11 |
| `Unreachable code` | 🔶 Warning | 10 |
| `Single character alternation` | 🔶 Warning | 8 |
| `Assignment used as condition` | 🔶 Warning | 6 |
| `Pointless arithmetic expression` | 🔶 Warning | 6 |
| `Fallthrough in 'switch' statement` | 🔶 Warning | 4 |
| `Dynamic property declaration` | 🔶 Warning | 4 |
| `Mismatched query and update of collection` | 🔶 Warning | 3 |
| `Object is 'null' or 'undefined'` | 🔶 Warning | 3 |
| `Loop statement that doesn't loop` | 🔶 Warning | 3 |
| `Parameter type` | 🔶 Warning | 3 |
| `Potentially invalid reference to 'this' from closure` | 🔶 Warning | 2 |
| `Suspicious variable/parameter name combination` | 🔶 Warning | 2 |
| `Extension is missing in composer.json` | 🔶 Warning | 2 |
| `Begin or end anchor in unexpected position` | 🔶 Warning | 2 |
| `Variable is assigned to itself` | 🔶 Warning | 2 |
| `Exception used for local control-flow` | 🔶 Warning | 1 |
| `Consecutive commas in array literal` | 🔶 Warning | 1 |
| `Variable is declared and being used in different 'case' clauses` | 🔶 Warning | 1 |
| `Optional before required parameter` | 🔶 Warning | 1 |
| `Statement has empty body` | 🔶 Warning | 1 |
| `Pointless statement or boolean expression` | 🔶 Warning | 1 |
| `Unnecessary non-capturing group` | 🔶 Warning | 1 |
| `'throw' inside 'finally' block` | 🔶 Warning | 1 |
| `Unnecessary 'return' statement` | 🔶 Warning | 1 |
| `Fully qualified name usage` | ◽️ Notice | 496 |
| `Unused 'match' condition` | ◽️ Notice | 137 |
| `Unnecessary curly braces syntax for variable` | ◽️ Notice | 95 |
| `Missing return type declaration` | ◽️ Notice | 66 |
| `Deprecated symbol used` | ◽️ Notice | 57 |
| `Parameters number mismatch declaration` | ◽️ Notice | 44 |
| `Missing property's type declaration` | ◽️ Notice | 38 |
| `Unnecessary fully qualified name` | ◽️ Notice | 35 |
| `Expression statement which is not assignment or call` | ◽️ Notice | 33 |
| `Unused import` | ◽️ Notice | 14 |
| `Missing parameter's type declaration` | ◽️ Notice | 12 |
| `Regular expression can be simplified` | ◽️ Notice | 10 |
| `Unhandled exception` | ◽️ Notice | 9 |
| `Duplicated code fragment` | ◽️ Notice | 8 |
| `Duplicate arm in 'match' expression` | ◽️ Notice | 8 |
| `Comparison of expressions having incompatible types` | ◽️ Notice | 7 |
| `Missing @throws tag(s)` | ◽️ Notice | 5 |
| `Redundant optional argument` | ◽️ Notice | 5 |
| `Unused local variable` | ◽️ Notice | 5 |
| `Redundant 'static' in final class` | ◽️ Notice | 4 |
| `Result of method call returning a promise is ignored` | ◽️ Notice | 2 |
| `Undefined property assignment` | ◽️ Notice | 2 |
| `Unresolved include` | ◽️ Notice | 2 |
| `Non-installed Composer packages` | ◽️ Notice | 2 |
| `Unnecessary local variable` | ◽️ Notice | 2 |
| `Unused parameter` | ◽️ Notice | 2 |
| `Condition inside logical expression checked by subsequent condition` | ◽️ Notice | 1 |
| `'isset' can be replaced with coalesce` | ◽️ Notice | 1 |
| `'mixed' type can be replaced with 'array'` | ◽️ Notice | 1 |
| `Ranges in class can be merged` | ◽️ Notice | 1 |
| `Parameter's value is always the same` | ◽️ Notice | 1 |
| `Unnecessary 'return/continue' statement` | ◽️ Notice | 1 |



☁️ [View the detailed Qodana report](https://qodana.cloud/projects/dXXnG/reports/wVW02)

<details>
<summary>Contact Qodana team</summary>

Contact us at [qodana-support@jetbrains.com](mailto:qodana-support@jetbrains.com)
  - Or via our issue tracker: https://jb.gg/qodana-issue
  - Or share your feedback: https://jb.gg/qodana-discussions
</details>
<!-- JetBrains/qodana-action@v2024.3.4 : Qodana for PHP -->
author_association: NONE
user: login: crimsonstrife
created_at: 2025-03-19T19:19:11Z
body: @sourcery-ai review
author_association: OWNER