From b1a146bdde719b31ff7643aad357ffcc62e031f7 Mon Sep 17 00:00:00 2001 From: Mercury <63693594+itinerare@users.noreply.github.com> Date: Mon, 6 Jan 2025 15:06:47 -0500 Subject: [PATCH] fix(world): implement eager loading, refactor queries (#1180) - only load extra item info (artists, shops) if extra fields are enabled - add "with" property to relevant models --- app/Http/Controllers/WorldController.php | 36 +++++++++++++------ app/Models/Item/Item.php | 35 ++++++++++++++---- app/Models/Species/Subtype.php | 1 + config/lorekeeper/extensions.php | 26 +++++++------- resources/views/world/_item_entry.blade.php | 8 ++--- .../views/world/_species_entry.blade.php | 2 +- resources/views/world/item_page.blade.php | 8 ++--- resources/views/world/items.blade.php | 12 +------ 8 files changed, 79 insertions(+), 49 deletions(-) diff --git a/app/Http/Controllers/WorldController.php b/app/Http/Controllers/WorldController.php index 8968a6b3bc..52b47eeee4 100644 --- a/app/Http/Controllers/WorldController.php +++ b/app/Http/Controllers/WorldController.php @@ -10,7 +10,6 @@ use App\Models\Item\ItemCategory; use App\Models\Rarity; use App\Models\Shop\Shop; -use App\Models\Shop\ShopStock; use App\Models\Species\Species; use App\Models\Species\Subtype; use App\Models\User\User; @@ -78,6 +77,11 @@ public function getRarities(Request $request) { */ public function getSpecieses(Request $request) { $query = Species::query(); + + if (config('lorekeeper.extensions.species_trait_index.enable')) { + $query->withCount('features'); + } + $name = $request->get('name'); if ($name) { $query->where('name', 'LIKE', '%'.$name.'%'); @@ -96,14 +100,14 @@ public function getSpecieses(Request $request) { * @return \Illuminate\Contracts\Support\Renderable */ public function getSubtypes(Request $request) { - $query = Subtype::query(); + $query = Subtype::query()->with('species'); $name = $request->get('name'); if ($name) { $query->where('name', 'LIKE', '%'.$name.'%'); } return view('world.subtypes', [ - 'subtypes' => $query->with('species')->visible(Auth::check() ? Auth::user() : null)->orderBy('sort', 'DESC')->orderBy('id')->paginate(20)->appends($request->query()), + 'subtypes' => $query->visible(Auth::check() ? Auth::user() : null)->orderBy('sort', 'DESC')->orderBy('id')->paginate(20)->appends($request->query()), ]); } @@ -147,8 +151,10 @@ public function getFeatureCategories(Request $request) { * @return \Illuminate\Contracts\Support\Renderable */ public function getFeatures(Request $request) { - $query = Feature::visible(Auth::check() ? Auth::user() : null)->with('category')->with('rarity')->with('species'); + $query = Feature::visible(Auth::check() ? Auth::user() : null)->with('category', 'rarity', 'species', 'subtype'); + $data = $request->only(['rarity_id', 'feature_category_id', 'species_id', 'subtype_id', 'name', 'sort']); + if (isset($data['rarity_id']) && $data['rarity_id'] != 'none') { $query->where('rarity_id', $data['rarity_id']); } @@ -230,6 +236,7 @@ public function getFeatures(Request $request) { public function getSpeciesFeatures($id) { $categories = FeatureCategory::orderBy('sort', 'DESC')->get(); $rarities = Rarity::orderBy('sort', 'ASC')->get(); + $species = Species::visible(Auth::check() ? Auth::user() : null)->where('id', $id)->first(); if (!$species) { abort(404); @@ -241,6 +248,7 @@ public function getSpeciesFeatures($id) { $features = count($categories) ? $species->features() ->visible(Auth::check() ? Auth::user() : null) + ->with('rarity', 'subtype') ->orderByRaw('FIELD(feature_category_id,'.implode(',', $categories->pluck('id')->toArray()).')') ->orderByRaw('FIELD(rarity_id,'.implode(',', $rarities->pluck('id')->toArray()).')') ->orderBy('has_image', 'DESC') @@ -256,6 +264,7 @@ public function getSpeciesFeatures($id) { ->groupBy(['feature_category_id', 'id']) : $species->features() ->visible(Auth::check() ? Auth::user() : null) + ->with('rarity', 'subtype') ->orderByRaw('FIELD(rarity_id,'.implode(',', $rarities->pluck('id')->toArray()).')') ->orderBy('has_image', 'DESC') ->orderBy('name') @@ -286,7 +295,7 @@ public function getSpeciesFeatures($id) { * @return \Illuminate\Contracts\Support\Renderable */ public function getSpeciesFeatureDetail($speciesId, $id) { - $feature = Feature::where('id', $id)->first(); + $feature = Feature::where('id', $id)->with('species', 'subtype', 'rarity')->first(); if (!$feature) { abort(404); @@ -308,6 +317,10 @@ public function getSpeciesFeatureDetail($speciesId, $id) { public function getItems(Request $request) { $query = Item::with('category')->released(Auth::user() ?? null); + if (config('lorekeeper.extensions.item_entry_expansion.extra_fields')) { + $query->with('artist', 'shopStock')->withCount('shopStock'); + } + $categoryVisibleCheck = ItemCategory::visible(Auth::check() ? Auth::user() : null)->pluck('id', 'name')->toArray(); // query where category is visible, or, no category and released $query->where(function ($query) use ($categoryVisibleCheck) { @@ -366,9 +379,13 @@ public function getItems(Request $request) { * @return \Illuminate\Contracts\Support\Renderable */ public function getItem($id) { - $categories = ItemCategory::orderBy('sort', 'DESC')->get(); + $item = Item::where('id', $id)->released(Auth::user() ?? null)->with('category'); - $item = Item::where('id', $id)->released(Auth::user() ?? null)->first(); + if (config('lorekeeper.extensions.item_entry_expansion.extra_fields')) { + $item->with('artist', 'shopStock')->withCount('shopStock'); + } + + $item = $item->first(); if (!$item) { abort(404); @@ -384,8 +401,6 @@ public function getItem($id) { 'imageUrl' => $item->imageUrl, 'name' => $item->displayName, 'description' => $item->parsed_description, - 'categories' => $categories->keyBy('id'), - 'shops' => Shop::whereIn('id', ShopStock::where('item_id', $item->id)->pluck('shop_id')->unique()->toArray())->orderBy('sort', 'DESC')->get(), ]); } @@ -395,7 +410,8 @@ public function getItem($id) { * @return \Illuminate\Contracts\Support\Renderable */ public function getCharacterCategories(Request $request) { - $query = CharacterCategory::query(); + $query = CharacterCategory::query()->with('sublist'); + $name = $request->get('name'); if ($name) { $query->where('name', 'LIKE', '%'.$name.'%')->orWhere('code', 'LIKE', '%'.$name.'%'); diff --git a/app/Models/Item/Item.php b/app/Models/Item/Item.php index d2f9d13148..fab8a78f24 100644 --- a/app/Models/Item/Item.php +++ b/app/Models/Item/Item.php @@ -5,6 +5,7 @@ use App\Models\Model; use App\Models\Prompt\Prompt; use App\Models\Shop\Shop; +use App\Models\Shop\ShopStock; use App\Models\User\User; class Item extends Model { @@ -26,6 +27,16 @@ class Item extends Model { * @var string */ protected $table = 'items'; + + /** + * The relationships that should always be loaded. + * + * @var array + */ + protected $with = [ + 'tags', + ]; + /** * Validation rules for creation. * @@ -86,6 +97,13 @@ public function artist() { return $this->belongsTo(User::class, 'artist_id'); } + /** + * Get shop stock for this item. + */ + public function shopStock() { + return $this->hasMany(ShopStock::class, 'item_id'); + } + /********************************************************************************************** SCOPES @@ -343,17 +361,18 @@ public function getResellAttribute() { } /** - * Get the shops attribute as an associative array. + * Get the shops that stock this item. * - * @return array + * @return \Illuminate\Database\Eloquent\Collection */ public function getShopsAttribute() { - if (!$this->data) { + if (!config('lorekeeper.extensions.item_entry_expansion.extra_fields') || !$this->shop_stock_count) { return null; } - $itemShops = $this->data['shops']; - return Shop::whereIn('id', $itemShops)->get(); + $shops = Shop::whereIn('id', $this->shopStock->pluck('shop_id')->toArray())->orderBy('sort', 'DESC')->get(); + + return $shops; } /** @@ -367,7 +386,11 @@ public function getPromptsAttribute() { } $itemPrompts = $this->data['prompts']; - return Prompt::whereIn('id', $itemPrompts)->get(); + if (count($itemPrompts)) { + return Prompt::whereIn('id', $itemPrompts)->get(); + } else { + return null; + } } /** diff --git a/app/Models/Species/Subtype.php b/app/Models/Species/Subtype.php index 51fadd4e6c..a207153f31 100644 --- a/app/Models/Species/Subtype.php +++ b/app/Models/Species/Subtype.php @@ -29,6 +29,7 @@ class Subtype extends Model { protected $appends = [ 'name_with_species', ]; + /** * Validation rules for creation. * diff --git a/config/lorekeeper/extensions.php b/config/lorekeeper/extensions.php index 65f0183599..97106f28cb 100644 --- a/config/lorekeeper/extensions.php +++ b/config/lorekeeper/extensions.php @@ -20,25 +20,25 @@ */ // Navbar News Notif - Juni - 'navbar_news_notif' => 0, + 'navbar_news_notif' => 0, // Species Trait Index - Mercury - 'species_trait_index' => [ + 'species_trait_index' => [ 'enable' => 0, 'trait_modals' => 0, // Enables modals when you click on a trait for more info instead of linking to the traits page - Moif ], // Character Status Badges - Juni - 'character_status_badges' => 0, + 'character_status_badges' => 0, // Character TH Profile Link - Juni - 'character_TH_profile_link' => 0, + 'character_TH_profile_link' => 0, // Design Update Voting - Mercury - 'design_update_voting' => 0, + 'design_update_voting' => 0, // Item Entry Expansion - Mercury - 'item_entry_expansion' => [ + 'item_entry_expansion' => [ 'extra_fields' => 0, 'resale_function' => 0, 'loot_tables' => [ @@ -49,33 +49,33 @@ ], // Group Traits By Category - Uri - 'traits_by_category' => 0, + 'traits_by_category' => 0, // Scroll To Top - Uri - 'scroll_to_top' => 0, // 1 - On, 0 - off + 'scroll_to_top' => 0, // 1 - On, 0 - off // Character Reward Expansion - Uri - 'character_reward_expansion' => [ + 'character_reward_expansion' => [ 'expanded' => 1, 'default_recipient' => 0, // 0 to default to the character's owner (if a user), 1 to default to the submission user. ], // MYO Image Hide/Remove - Mercury // Adds an option when approving MYO submissions to hide or delete the MYO placeholder image - 'remove_myo_image' => 0, + 'remove_myo_image' => 0, // Auto-populate New Image Traits - Mercury // Automatically adds the traits present on a character's active image to the list when uploading a new image for an extant character. - 'autopopulate_image_features' => 0, + 'autopopulate_image_features' => 0, // Staff Rewards - Mercury - 'staff_rewards' => [ + 'staff_rewards' => [ 'enabled' => 0, 'currency_id' => 1, ], // Organised Traits Dropdown - Draginraptor - 'organised_traits_dropdown' => 0, + 'organised_traits_dropdown' => 0, // Previous & Next buttons on Character pages - Speedy // Adds buttons linking to the previous character as well as the next character on all character pages. diff --git a/resources/views/world/_item_entry.blade.php b/resources/views/world/_item_entry.blade.php index 62fcde7ca5..bb00d4fe7b 100644 --- a/resources/views/world/_item_entry.blade.php +++ b/resources/views/world/_item_entry.blade.php @@ -68,7 +68,7 @@
@endif {!! $description !!} - @if (((isset($item->uses) && $item->uses) || (isset($item->source) && $item->source) || $shops->count() || (isset($item->data['prompts']) && $item->data['prompts'])) && config('lorekeeper.extensions.item_entry_expansion.extra_fields')) + @if (((isset($item->uses) && $item->uses) || (isset($item->source) && $item->source) || $item->shop_stock_count || (isset($item->data['prompts']) && $item->data['prompts'])) && config('lorekeeper.extensions.item_entry_expansion.extra_fields'))Purchaseable At:
Uses: {!! $item->uses !!}
@endif - @if ((isset($item->source) && $item->source) || $shops->count() || (isset($item->data['prompts']) && $item->data['prompts'])) + @if ((isset($item->source) && $item->source) || $item->shop_stock_count || (isset($item->data['prompts']) && $item->data['prompts'])){!! $item->data['release'] !!}
Purchaseable At: