So I will start by admitting that a month or two ago I knew zip about Laravel, and I have been learning it through some great videos on this very platform + the help of AI LLMs. While this has accelerated my progress it has also left me with quite a few gaps in my knowledge. Like for example, how to properly split application logic while not making it impossible to track down. Let me give you an example:
public function showByCategory(Request $request, $categorySlug, $subCategorySlug = null, $itemSlug = null)
{
$category = Category::where('name', $categorySlug)->firstOrFail();
$subCategory = null;
$item = null;
if ($subCategorySlug) {
$subCategory = SubCategory::where('name', $subCategorySlug)
->where('category_id', $category->id)
->firstOrFail();
}
if ($itemSlug && $subCategory) {
$item = Item::where('name', $itemSlug)
->where('sub_category_id', $subCategory->id)
->firstOrFail();
}
$listingsQuery = Listing::query();
if ($item) {
$listingsQuery->where('item_id', $item->id);
} elseif ($subCategory) {
$listingsQuery->whereHas('item', function ($query) use ($subCategory) {
$query->where('sub_category_id', $subCategory->id);
});
} else {
$listingsQuery->whereHas('item', function ($query) use ($category) {
$query->whereHas('sub_category', function ($subQuery) use ($category) {
$subQuery->where('category_id', $category->id);
});
});
}
$user = $request->user();
$listings = $listingsQuery
->with('item.sub_category')
->withCount([
'favoritedBy as is_favorited' => function ($query) use ($user) {
if ($user) {
$query->where('user_id', $user->id);
}
}
])
->orderBy('updated_at', 'desc')
->paginate(20);
if ($listings->currentPage() > $listings->lastPage()) {
abort(404);
}
$listings->getCollection()->transform(function ($listing) {
$listing->is_favorited = $listing->is_favorited > 0;
return $listing;
});
return Inertia::render('CategoryListings', [
'listings' => $listings,
'currentCategoryBG' => $category->bg_name,
'currentCategory' => $categorySlug,
'currentSubCategory' => $subCategory ? $subCategory->name : null,
'currentSubCategoryBG' => $subCategory ? $subCategory->bg_name : null,
'currentItem' => $item ? $item->name : null,
'currentItemBG' => $item ? $item->bg_name : null
]);
}
This method is in my "ListingController" and it's used to show all the listings from any given category/subcategory or item. It's quite cumbersome and there is a whole lot of querying going on. Today I read on a random forum thread that "queries should always be stored in the Model they're..querying" but given the fact that this is a custom query that's pretty much only going to be used once I don't see how it would be better if it were stored in the Listing model. Right now I am only using models to define the fillable and protected properties, if something should be cast a certain way and the relationships between different tables. Here is the Listing Model as an example:
<?php
namespace App\Models;
use App\Condition;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Listing extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'item_id',
'price',
'sold',
'title',
'expires_at',
'rating',
'photos',
'description',
'views',
'negoatiable',
'condition',
];
protected $casts = [
'sold' => 'boolean',
'expires_at' => 'date',
'photos' => 'array',
'condition' => Condition::class,
];
public function user()
{
return $this->belongsTo(User::class);
}
public function item()
{
return $this->belongsTo(Item::class);
}
public function favoritedBy()
{
return $this->belongsToMany(User::class, 'favorite_listings')->withTimestamps();
}
public function reports()
{
return $this->hasMany(ListingReport::class);
}
}
I want to know if my project structure is bad, if I should have more methods in my models instead of my controllers, or if it's just a matter of taste? I don't want to be one of those devs that's done things the wrong way for years only to realize it when they arrive in a professional environment and feel like a dummy.