MySQL (Laravel Eloquent) - 当两列同时有两个或多个值时获取记录

Posted

技术标签:

【中文标题】MySQL (Laravel Eloquent) - 当两列同时有两个或多个值时获取记录【英文标题】:MySQL (Laravel Eloquent) - Get record when two columns have two or more values at the same time 【发布时间】:2020-08-31 08:18:36 【问题描述】:

我有一个从this post 获得的用于管理产品及其变体的数据库:

+---------------+     +---------------+
| PRODUCTS      |-----< PRODUCT_SKUS  |
+---------------+     +---------------+
| #product_id   |     | #product_id   |
|  product_name |     | #sku_id       |
+---------------+     |  sku          |
        |             |  price        |
        |             +---------------+
        |                     |
+-------^-------+      +------^------+
| OPTIONS       |------< SKU_VALUES  |
+---------------+      +-------------+
| #product_id   |      | #product_id |
| #option_id    |      | #sku_id     |
|  option_name  |      | #option_id  |
+---------------+      |  value_id   |
        |              +------v------+
+-------^-------+             |
| OPTION_VALUES |-------------+
+---------------+
| #product_id   |
| #option_id    |
| #value_id     |
|  value_name   |
+---------------+

问题是,当用户选择他想要的产品选项时,我不知道如何获得 SKU:

SKU_VALUES
==========
product_id sku_id option_id value_id
---------- ------ --------- --------
1          1      1         1        (W1SSCW; Size; Small)
1          1      2         1        (W1SSCW; Color; White)
1          2      1         1        (W1SSCB; Size; Small)
1          2      2         2        (W1SSCB; Color; Black)

假设用户选择了 ID 为 1 的产品和选项 size-small 和 color-black,我怎么能在获取PRODUCT_SKUS 表中的价格。

由于显而易见的原因,我不能这样做:

SELECT sku_id FROM SKU_VALUES 
WHERE (SKU_VALUES.option_id = 1 AND SKU_VALUES.value_id = 1) 
AND (SKU_VALUES.option_id = 2 AND SKU_VALUES.value_id = 2)

请注意,我似乎需要附加与产品可用选项数量相同数量的条件(或任何我需要的),在这种情况下,只有 2 行,因为产品有 2 个选项(尺寸和颜色),但产品可能有“n”个选项。

如果有人可以指导我进行此查询,并且可以使用 Laravel Eloquent 而不是使用 RAW 查询,我将不胜感激。

我创建的模型如下:

“产品”型号:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Producto extends Model

    protected $table = 'productos';

    protected $fillable = [
        'nombre',
        'descripcion'

    ];

public function opciones()
    return $this->hasMany('App\Models\OpcionProducto', 'producto_id');


public function skus()
    return $this->hasMany('App\Models\ProductoSku', 'producto_id');


“选项”模型:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

use App\Traits\HasCompositePrimaryKey;

class OpcionProducto extends Model

    use HasCompositePrimaryKey;

    protected $table = 'productos_opciones';

    protected $primaryKey = array('producto_id', 'opcion_id');

    protected $fillable = [
        'producto_id',
        'opcion_id',
        'nombre_opcion',
        'valor'
    ];


    public function producto()
        return $this->belongsTo('App\Models\Producto', 'producto_id');
    

    public function valores()
        return $this->hasMany('App\Models\OpcionValorProducto', 'opcion_id', 'opcion_id');
    

    public function skusValores()
        return $this->hasMany('App\Models\SkuValor', 'opcion_id', 'opcion_id');
    

“OptionValues”模型:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

use App\Traits\HasCompositePrimaryKey;

class OpcionValorProducto extends Model

    use HasCompositePrimaryKey;

    protected $primaryKey = array('producto_id', 'opcion_id', 'valor_id');

    protected $table = 'productos_opciones_valores';

    protected $fillable = [
        'producto_id',
        'opcion_id',
        'valor_id',
        'valor_variacion',
        'valor'
    ];


    public function producto()
        return $this->belongsTo('App\Models\Producto', 'producto_id');
    

    public function opcion()
        return $this->belongsTo('App\Models\OpcionProducto', 'opcion_id', 'opcion_id');
    

“Product_SKUS”型号:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

use App\Traits\HasCompositePrimaryKey;

class ProductoSku extends Model

    use HasCompositePrimaryKey;

    protected $primaryKey = array('producto_id', 'sku_id');

    protected $table = 'productos_skus';

    protected $fillable = [
        'producto_id',
        'sku_id',
        'imagen_id',
        'precio',
        'stock',
        'sku'
    ];


    public function producto()
        return $this->belongsTo('App\Models\Producto', 'producto_id');
    

    public function valoresSku()
        return $this->hasMany('App\Models\SkuValor', 'sku_id');
    

    

“SKU_VALUES”型号:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

use App\Traits\HasCompositePrimaryKey;

class SkuValor extends Model

    use HasCompositePrimaryKey;

    protected $primaryKey = array('producto_id', 'sku_id', 'opcion_id');

    protected $table = 'valores_skus';

    protected $fillable = [
        'producto_id',
        'sku_id',
        'opcion_id',
        'valor_id',
    ];


    public function producto()
        return $this->belongsTo('App\Models\Producto', 'producto_id');
    

    public function opcion()
        return $this->belongsTo('App\Models\OpcionProducto', 'opcion_id', 'opcion_id');
    

    public function sku()
        return $this->belongsTo('App\Models\ProductoSku', 'sku_id', 'sku_id');
    

【问题讨论】:

你能添加你的模型吗? @Mirdrack 当然,等一下。 @Mirdrack 完成,虽然它们是西班牙语,但我已经指定了哪个模型属于哪个表... 【参考方案1】:

在完成您的问题后,这是我想出的代码。当然,这是未经测试的。请试一试。

$skuValor = SkuValor::with('producto', 'opcion.valores', 'sku')
    ->whereHas('producto', function ($q) use ($request) 
        $q->where('id', $request->get('product_id')); // id: 1
    )
    ->whereHas('opcion', function ($q) use ($request) 
        $q->whereIn('id', $request->get('option_ids')) // id: [1, 2] where 1=size, 2=color
        ->whereHas('valores', function($q2) use ($request) 
            $q2->whereIn('id', $request->get('value_ids')); // id: [1, 3] where 1=small, 3=pink
        );
    )
    ->get();

$skuValor->sku->id; // sky id

with() :这称为Eager Loading。检索模型时加载一些关系。

whereHas():Querying Relationship Existence。此方法允许您将自定义约束添加到关系约束。

use() :传递数据以便内部(特定)查询可以使用它。请注意,我们已将 1 用于 opcion,而将另一个用于 valores。

whereIn():此方法验证给定列的值是否包含在给定数组中。

如果遇到困难,请发表评论。

【讨论】:

【参考方案2】:

这是一个使用纯 SQL 的解决方案。

这是您使用原始查询的尝试:

select sku_id 
from sku_values 
where (option_id = 1 and value_id = 1) and (option_id = 2 and value_id = 2)

这不起作用,因为您需要在共享相同sku_id 的行中搜索 行,而不是搜索每一行。这建议聚合:

select sku_id
from sku_values
where (option_id, value_id) in ((1, 1), (2, 2)) -- either one combination or the other
group by sku_id
having count(*) = 2                             -- both match

您可以通过在where 子句谓词中添加更多组合并相应地增加having 子句中的目标计数来轻松扩展查询以获得更多选项。例如,这会根据 4 个条件进行过滤:

select sku_id
from sku_values
where (option_id, value_id) in ((1, 1), (2, 2), (3, 10) (12, 17))
group by sku_id
having count(*) = 4

还可以通过在子查询中添加更多连接来按选项名称和值进行过滤:

select sv.sku_id
from sku_values sv
inner join options o 
    on  o.product_id = sv.product_id 
    and o.option_id  = sv.option_id
inner join option_values ov 
    on  ov.product_id = sv.product_id 
    and ov.option_id  = sv.option_id 
    and ov.value_id   = sv.value_id
where (o.option_name, ov.value_name) in (('Size', 'Small'), ('Color', 'Black'))
group by sv.sku_id
having count(*) = 2

现在,假设您要获取相应的产品名称和价格:您可以将上述查询与相关表连接起来。

select p.product_name, ps.price
from products p
inner join product_skus ps 
    on  ps.product_id = p.product_id
inner join (
    select sv.sku_id
    from sku_values sv
    inner join options o 
        on  o.product_id = sv.product_id 
        and o.option_id  = sv.option_id
    inner join option_values ov 
        on  ov.product_id = sv.product_id 
        and ov.option_id  = sv.option_id 
        and ov.value_id   = sv.value_id
    where (o.option_name, ov.value_name) in (('Size', 'Small'), ('Color', 'Black'))
    group by sv.sku_id
    having count(*) = 2
) x 
    on  x.sku_id = ps.sku_id

【讨论】:

感谢您的解释!,不知道使用IN 运算符的这种方式,您唯一错过的是在前两个示例中添加where 子句以过滤product_id,因为该表使用复合主键。【参考方案3】:

使用 Laravel subqueries 可以做到这一点,但我建议使用创建一个 RAW 查询来获取您需要的那些值,然后使用 Laravel 查询构建器实现它。

对于涉及许多表的查询,Eloquent 可能会导致某种低效,并且会导致您编写无法维护的代码。

额外的建议,即使您的表名是其他语言,也推荐创建完整的英文模型,您可以指定哪些字段与哪些属性相关。

【讨论】:

是的,但问题是我不知道如何获得我想要的结果,即使是原始查询。

以上是关于MySQL (Laravel Eloquent) - 当两列同时有两个或多个值时获取记录的主要内容,如果未能解决你的问题,请参考以下文章

我想将 MySQL 查询转换为 Laravel Eloquent / Laravel ORM

如何使用 Laravel 5.6 在 Laravel Eloquent 中实现嵌套 MySQL 查询

如何将许多语句 mysql 转换为 laravel eloquent?

通过laravel eloquent查询Mysql sum group

使用 Laravel Eloquent / MySQL 比较多个数据库行值

Laravel 6,MYSQL - 如何使用 Laravel Querybuilder 或 Model Eloquent 将子查询与 GroupBY 左连接?