简体   繁体   English

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

[英]MySQL (Laravel Eloquent) - Get record when two columns have two or more values at the same time

I have this database that I got from this post that manages products and its variants:我有从这篇文章中获得的用于管理产品及其变体的数据库:

+---------------+     +---------------+
| 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   |
+---------------+

The problem is, that I don't know how would I get the SKU at the moment that a user selects the options of the product he wants:问题是,当用户选择他想要的产品选项时,我不知道如何获得 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)

Let's suppose that the user selects the product with ID 1 and the options size-small and color-black, how am I able to get the sku_id (in this case I would want value 2 from sku_id ) in order to get the price that's inside the PRODUCT_SKUS table.假设用户选择了 ID 为 1 的产品以及选项 size-small 和 color-black,我如何获得sku_id (在这种情况下,我希望sku_id中的值 2 )以获得里面的价格PRODUCT_SKUS表。

I cannot do something like this for obvious reasons:由于显而易见的原因,我不能做这样的事情:

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)

NOTE that it seems that I would need to append the same number of conditions (or whatever I need) as the number of options that are available from a product, in this case there are just 2 rows because the product has 2 options (size and color), but the product may have "n" options.请注意,我似乎需要 append 与产品可用选项数量相同的条件(或我需要的任何东西),在这种情况下只有 2 行,因为产品有 2 个选项(尺寸和颜色),但产品可能有“n”个选项。

I would appreciate if someone could guide me for this query and if it's possible doing it with Laravel Eloquent instead of using RAW query.如果有人可以指导我进行此查询,并且可以使用 Laravel Eloquent 而不是使用 RAW 查询,我将不胜感激。

The models I have created are the following:我创建的模型如下:

"Product" Model: “产品”Model:

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');
}
}

"Options" Model: “选项”Model:

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" Model: “选项值”Model:

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" model: “产品_SKUS” model:

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" model: “SKU_VALUES”model:

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');
    }
}

Here is a solution using pure SQL.这是使用纯 SQL 的解决方案。

This is your attempt using a raw query:这是您使用原始查询的尝试:

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

This doesn't work because you need to search across rows sharing the same sku_id rather than on each row.这不起作用,因为您需要共享相同sku_id的行之间进行搜索,而不是在每一行上进行搜索。 This suggest aggregation:这建议聚合:

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

You can easily extend the query for more options by adding more combinations in the where clause predicate and incrementing the target count in the having clause accordingly.您可以通过在where子句谓词中添加更多组合并相应地增加having子句中的目标计数来轻松扩展查询以获得更多选项。 For example, this filters on 4 criterias:例如,这会根据 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

It is also possible to filter by option names and values by adding more joins in the subquery:也可以通过在子查询中添加更多连接来按选项名称和值进行过滤:

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

Now, say you want to get the corresponding product name and price: you can join the above query with the relevant tables.现在,假设您要获取相应的产品名称和价格:您可以将上述查询与相关表连接起来。

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

After going through your question, this is the code I came up with.在完成您的问题后,这是我想出的代码。 Of course this is un-tested.当然,这是未经测试的。 Please give this a shot.请试一试。

$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() : This is called Eager Loading . with() :这称为Eager Loading Load some relationships when retrieving a model.在检索 model 时加载一些关系。

whereHas() : Querying Relationship Existence . whereHas()查询关系是否存在 This method allow you to add customized constraints to a relationship constraint.此方法允许您将自定义约束添加到关系约束。

use() : Passing data so that inner (that particular) query can use it. use() :传递数据以便内部(特定)查询可以使用它。 Notice we have used 1 for opcion and another one for valores.请注意,我们将 1 用于 opcion,将另一个用于 valores。

whereIn() : This method verifies that a given column's value is contained within the given array. whereIn() :此方法验证给定列的值是否包含在给定数组中。

Comment if you get stuck.如果您遇到困难,请发表评论。

This is possible using Laravel subqueries but I would suggests the use create a RAW query to get those values you need and then implement it with Laravel Query builder.这可以使用 Laravel子查询来实现,但我建议使用创建一个 RAW 查询来获取您需要的那些值,然后使用 Laravel 查询构建器来实现它。

For queries that involves many tables Eloquent can result some kind of unefficient and it will take you to write unmaintainable code.对于涉及许多表的查询 Eloquent 可能会导致某种效率低下,并且会导致您编写无法维护的代码。

Extra suggestion, even when you your table names are in other languages it is recomendable create full english models, you can specify what field is related to what property.额外的建议,即使您的表名是其他语言,也建议创建完整的英文模型,您可以指定哪些字段与哪些属性相关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM