Laravel关联查询:如何屏蔽无关的Product模型?
问题:Laravel关联查询时过滤无关联数据
我在Laravel项目中通过getProductListAll方法查询Product列表,关联了property和prices_roket(对应ProductPrice模型的hasMany关联)。现在发现没有关联prices_roket数据的Product也被包含在返回结果里,需要过滤掉这类产品。之前尝试用map方法移除prices_roket中的product属性,但没达到过滤无关联产品的目的。
相关代码
Controller代码
public function getProductListAll() { $products = Product::with(['property'=>function($q){ $q->select(['features','for_who','product_id']); }]) ->without('products') ->whereStatus(1) ->where('show', 1) ->where('code', '!=', 1) ->where('is_roketable', true) ->with(['prices_roket' => function ($q) { return $q->whereNull('user_id'); }]); $products = $products->whereIn('product_type', ['education', 'consultancy', 'startup']); $data['products'] = $products->select([ 'id', 'name', 'image', 'commission', 'price', 'buy_now', 'type', 'short_desc', 'pricing_desc', 'offer_status', 'period_status', 'period', 'period_frequency', 'endpoint', 'product_type', 'unique_id', 'discount_show_status', 'annual_payment', 'pre_register', 'privacy_status', 'privacy', 'sg_project', 'last_pre_register_date', 'product_desc', 'product_consulting', 'product_status', ])->get(); return response()->json($data); }
Product模型关联方法
public function prices_roket() { return $this->hasMany(ProductPrice::class)->select(['id', 'product_id', 'price', 'user_id', 'offer_id']) ->orderBy('price')->whereNull('offer_id'); }
尝试的处理代码
$data['products'] = $data['products']->map(function($product) { if(!$product->prices_roket) { return $product; } $product->prices_roket = $product->prices_roket ->map(function($pr) { $pr->forget('product'); return $pr; }); return $product; });
Product模型完整代码
protected $table = 'products'; protected $fillable = [ 'id', 'code', 'name', 'category_id', 'description', 'image', 'commission', 'price', 'buy_now', 'route', 'btn_text', 'type', 'status', 'short_desc', 'pricing_desc', 'author', 'author_email', 'author_web', 'author_logo', 'addon_id', 'show', 'discount_code_status', 'discount', 'discount_status', 'offer_status', 'period_status', 'period', 'period_frequency', 'sort', 'remaning_status', 'action', 'subscription', 'key', 'endpoint', 'product_type', 'unique_id', 'discount_show_status', 'annual_payment', 'auto_assign_user_id', 'relational_product_status', 'supplier_id', 'form_id', 'contents', 'auto_invoice', 'pre_register', 'privacy_status', 'privacy', 'sg_crm_code', 'sg_supplier', 'sg_project', 'activated_at', 'passived_at', 'last_pre_register_date', 'supplier_commision', 'popup_show', 'earn_money', 'product_desc', 'start_popup_time', 'finish_popup_time', 'product_consulting', 'product_status', 'banner_one', 'banner_two', 'banner_three', 'banner_four', 'is_roketable' ]; protected $hidden = [ 'auto_assign_user_id' ]; protected $dates = []; protected $casts = [ 'contents' => 'array' ]; protected static function boot() { parent::boot(); static::creating(function ($model) { if ($model->category_id) { $category = ProductCategory::find($model->category_id); if ($category) { $model->type = $category->name; } } $model->code = initials($model->name) . '01'; }); static::updating(function ($model) { if ($model->category_id) { $category = ProductCategory::find($model->category_id); if ($category) { $model->type = $category->name; } } }); // static::addGlobalScope(new MyProductScope); } public function scopeMyProduct($query) { $user = auth()->user(); if ($user && !$user->isAdmin() && $user->role != 'business') { $ids = DB::table('user_products')->where('user_id', $user->id)->pluck('product_id'); $query->whereIn('products.id', $ids); } } protected $appends = [ 'image_url', 'author_logo_url', 'data', 'education', 'unique', 'description_string' ];
ProductPrice模型
protected $table = 'product_prices'; protected $fillable = [ 'price', 'description', 'product_id', 'product_price_id', 'user_id', 'offer_id', 'sort', 'tax', 'person_count_status', 'person_price', 'person_text', 'free', 'visibility', 'currency', 'meeting_required', 'subscription_price', 'annual_price', 'is_analysis', 'invoice_required', 'product_question_group_id', 'send_mail', 'price_type', 'discounted_price', 'project_code', 'person_count_accept', 'foreign_web_link', 'foreign_web_site_status', 'portal_product_show_name', 'edanisman_money', 'reportable' ]; protected $casts = [ 'free' => 'boolean', 'person_count_status' => 'boolean', 'meeting_required' => 'boolean', 'is_analysis' => 'boolean', 'invoice_required' => 'boolean', 'invoice_required' => 'boolean', 'reportable' => 'boolean', // 'annual_price' => 'decimal', ]; protected $dates = []; protected $hidden = []; protected $appends = [ 'real_price', 'real_person_price', 'main_price', 'text_desc', 'package_name', 'remaning_price', 'remaning_months', 'discount_status', 'discount_type', 'annual_discount_rate', 'currency_symbol', 'currency_rate', 'user_has_price', ];
解决方案
要过滤掉无prices_roket关联的Product,不能在查询后用map处理(map仅修改关联数据,不会过滤模型本身),需要在查询阶段通过whereHas约束关联存在且符合条件:
修改后的Controller代码:
public function getProductListAll() { $products = Product::with(['property' => function($q){ $q->select(['features','for_who','product_id']); }]) ->without('products') ->whereStatus(1) ->where('show', 1) ->where('code', '!=', 1) ->where('is_roketable', true) // 新增:过滤存在符合条件的prices_roket关联的产品 ->whereHas('prices_roket', function ($q) { $q->whereNull('user_id')->whereNull('offer_id'); }) ->with(['prices_roket' => function ($q) { return $q->whereNull('user_id'); }]); $products = $products->whereIn('product_type', ['education', 'consultancy', 'startup']); $data['products'] = $products->select([ 'id', 'name', 'image', 'commission', 'price', 'buy_now', 'type', 'short_desc', 'pricing_desc', 'offer_status', 'period_status', 'period', 'period_frequency', 'endpoint', 'product_type', 'unique_id', 'discount_show_status', 'annual_payment', 'pre_register', 'privacy_status', 'privacy', 'sg_project', 'last_pre_register_date', 'product_desc', 'product_consulting', 'product_status', ])->get(); // 如需移除prices_roket中的product属性,可保留这段处理 $data['products'] = $data['products']->map(function($product) { $product->prices_roket = $product->prices_roket->map(function($pr) { $pr->forget('product'); return $pr; }); return $product; }); return response()->json($data); }
说明
whereHas用于筛选存在指定关联且关联数据满足条件的模型,这里的条件和prices_roket关联定义、with中的约束保持一致,确保返回的Product都有符合要求的关联数据。- 之前的map代码仅用于移除关联数据中的
product属性,无法过滤无关联的Product,因此必须在查询阶段添加约束。
内容的提问来源于stack exchange,提问作者mehmet
相关产品推荐
相关产品推荐

