سلام دوستان
من سوالی دارم که شاید کمی سخت باشه توضیحش.
فرض بفرمایید من یک جدولproduct دارم یک جدول price و یک جدول special. حالا میخوام محصولات رو با paginat(9) بگیرم اما بر اساس finalPrice که از رابطه درصدی از جداول price و special بدست میاد sort کنم. حالا مشکل اینجاست که 9 تا 9تا محصولات رو به ما میده و ما 9 تای اول رو sort میکنیم که ممکنه بزرگترین یا کوچکترین finalPrice رو نداشته باشه. من یه چیزایی راجع به جدول مجازی شنیدم. آیا راه حل جدول مجازی هست یا را حل دیگه ای داره.
@gomnam
@zafari.ma.8
اول از همه به این موضوع توجه داشته باشید که دو تا دستور داریم بنام های orderBy و sortBy که با هم تفاوت اساسی دارند.
OrderBy مرتب سازی در لایه دیتابیس هست. یعنی باعث تغییر شکل کوئری SQL که روی دیتابیس اجرا میشه خواهد شد و عملیات مرتب سازی قبل از واکشی اطلاعات انجام میشه. اما SortBy یک متد مربوط به کالکشن های لاراول هست. یعنی روی مجموعه ای از اطلاعات که به هرشکلی از قبل واکشی و آماده شدهاند اجرا میشه.
حالا وقتی شما قصد استفاده از paginate رو داشته باشید، به شکل اصولی و اگر نخواسته باشید اول همه (!) اطلاعات رو دریافت کنید و بعد مرتب سازی و فیلتر کنید، این اقدام باید روی لایه دیتابیس هندل بشه. پس SortBy اصلا به کار نمیاد.
اما چالش اصلی که پیش میاد مرتب سازی در لایه دیتابیس بر حسب یک فاکتوری هست که بصورت مشخص در همون جدول وجود نداره و باید با یک فرمولی و متناسب با سایر فیلدها یا جداول محاسبه بشه.
بحثش یک مقدار مفصل و تخصصیه. اما همون طوری که حدس زدید باید ابتدا به نحوی اون فاکتور مورد نظرتون رو تولید کنید و بعد در نهایت با استفاده از اون عملیات مرتب سازی رو انجام بدید. یکی از روش هاش استفاده از OrderByRaw هست که میتونید SQL خام بنویسید. اما روش جدیدتر و بهترش استفاده از join یا subquery هاست.
مثلا میشه اینجوری:
//با استفاده از subquery:
$users = User::orderByDesc(Login::select('created_at')
->whereColumn('logins.user_id', 'users.id')
->latest()
->take(1)
)->get();
//یا با استفاده از join:
$users = User::select('users.*')
->join('logins', 'logins.user_id', '=', 'users.id')
->groupBy('users.id')
->orderByRaw('max(logins.created_at) desc')
->get();
پیشنهاد میکنم مقاله زیر رو بصورت کامل مطالعه کنید. خیلی مفیده:
https://reinink.ca/articles/ordering-database-queries-by-relationship-columns-in-laravel
@mhyeganeh ممنونم از توضیحاتتون
روش join رو در نظر داشتم. این رو هم میدونم که باید از دیتا بیس مرتب سازی انجام بشه. مشکل اصلی من این هست که مثلا میخوام بر حسب finalPrice مرتب سازی بشه که تو هیچ جدولی ندارمش و باید بیام با استفاده از price خام و درصد تخفیف discount و انجام عملیات جبری بدست بیارمش که فکر میکنم توی کوئری این کار رو نمیشه کرد. من کدی که تا حالا نوشتم رو میذارم. یه مشکل دیگه هم هست که با فیلتر هایی که برای سایز و رنگ نوشتم به مشکل میخورم. مشکلم این هست که توی vue گفتم که هروقت products خالی بود یعنی محصولات تموم شدن دیگه query نزن. یعنی اگر در 9 تای اول رنگ یا سایز فیتر شده وجود نداشته باشه دیگه بقیه رو چک نمیکنه و تموم میشه و هیچی نشون نمیده. البته راه حلایی به ذهنم رسید ولی نشد.
public function getProducts(Request $request)
{
$categoryId = $request->categoryId;
$category = Category::find($categoryId);
$products = $category->Product()->where('status_id', 2)->orderBy('id', 'desc')->paginate(9);
foreach ($products as $product) {
if ($product->price()) {
$product['price'] = $product->price()->get()->last()->price;
} else {
$product['price'] = 0;
}
if (!empty($product->specific()->get()->last()->discount)) {
$product['discount'] = $product->specific()->get()->last()->discount;
} else {
$product['discount'] = 0;
}
$product['priceDiscount'] = ($product['price'] * $product['discount']) / 100;
$product['finalPrice'] = $product['price'] - $product['priceDiscount'];
}
foreach ($products as $product) {
if ($product->price()) {
$product['price'] = $product->price()->get()->last()->price;
} else {
$product['price'] = 0;
}
if (!empty($product->specific()->get()->last()->discount)) {
$product['discount'] = $product->specific()->get()->last()->discount;
} else {
$product['discount'] = 0;
}
$product['priceDiscount'] = ($product['price'] * $product['discount']) / 100;
$product['finalPrice'] = $product['price'] - $product['priceDiscount'];
$product['sizes'] = $product->stock()->get();
}
$colors = $request->colors;
$sizes = $request->sizes;
$filterProducts=collect();
if ($sizes != null || $colors != null) {
foreach ($products as $product) {
$checkProductColor = true;
$checkProductSize = true;
if ($colors != null){
foreach ($colors as $color){
if ($product['color_id'] == $color){
$checkProductColor = true;
break;
}else{
$checkProductColor = false;
}
}
}
if($sizes != null && $checkProductColor == true) {
foreach ($product['sizes'] as $productSize) {
foreach ($sizes as $size) {
if ($productSize['size_id'] == $size) {
$checkProductSize = true;
break;
}else{
$checkProductSize = false;
}
}
if ($checkProductSize == true){
break;
}
}
}
if ($checkProductSize == false || $checkProductColor == false){}
else{
$filterProducts [] = $product;
}
}
}
if ($sizes != null || $colors != null) {
if (isset($filterProducts[0])) {
$products = $filterProducts;
}else{
$products = collect();
}
}
$sortType = $request->sortType;
if ($sortType != null){
$collection = collect();
if ($sortType == 'finalPriceAsc'){
$products = $products->sortBy('finalPrice');
foreach ($products as $product){
$collection[] = $product;
}
}else{
$products = $products->sortByDesc($sortType);
foreach ($products as $product){
$collection[] = $product;
}
}
$products = $collection;
}
$min = $request->min;
$max = $request->max;
$products=$products->whereBetween('finalPrice',[$min,$max]);
return response()->json($products);
}
میخوام بر حسب finalPrice مرتب سازی بشه که تو هیچ جدولی ندارمش و باید بیام با استفاده از price خام و درصد تخفیف discount و انجام عملیات جبری بدست بیارمش که فکر میکنم توی کوئری این کار رو نمیشه کرد.
اول اینکه حقیقتا کار دشواری هست و نیازمند حوصله و تحقیق زیادی هست. ولی مطمئن باشید شدنیه. با هر فرمول و روشی که مدنظرتون باشه میشه با استفاده از دستورات SQL به جواب برسید و اون فیلد مجازی رو ایجاد کنید. سعی کنید به ریزترین زیرمجموعه های ممکن تقسیمش کنید و گام به گام با سرچ جلو برید. حتما به نتیجه خواهید رسید.
یک راهکار جایگزین دیگه که شاید بد نباشه بهش فکر کنید ذخیره نتیجه FinalPrice محاسبه شده در یک فیلد مجزا هست. یا به نحوی ساز و کاری برای Cache کردن نتیجه تا هر سری کل عملیات لازم نباشه اجرا بشه.
@mhyeganeh
ممنونم بابت زمانی که برام گذاشتید. میدونید مهمترین چالشی که مساله رو پیچیده کرده این هست که تخفیف همیشه نیست. یا وجود نداره یا اینکه تایمش گذشته. یعنی باید چک بشه که تخفیفی اصلا در کار هست یا نه و اگر هست حالا تازه بیا محاسبات جبریش رو انجام بده و finalPrice رو بدست بیا و بر اساس اون query بزن. هدف از سوال کردنم این بود که فکر کردم شاید راه حل سرراست تری وجود داره و من خبر ندارم.
آیا مایل به ارسال نوتیفیکیشن و اخبار از طرف راکت هستید ؟