将订单 ID 与属于它的产品相关联的最佳方式

Best way to relate the order ID with the products that belongs to it


我正在开发一个商店系统,并且我正在使用我能够找到的唯一方法将订单 ID 与属于它的产品相关联。
当有人购买东西时,首先将订单及其详细信息添加到 table orders,包括该订单的 ID (order_id),该 ID 由 [=68= 自动递增].
我用这个给table添加订单 orders:

INSERT INTO orders SET customer_id = '{$customer_id}', customer_name = '{$user['customer_name']}', order_price = '{$total_price}', order_date = '{$date}'"

好的,订单已添加。现在,按顺序,属于该订单的产品将被添加到另一个 table,table purchased_products
我使用 PDO lastInsertId() 来获取最后从 table orders 插入 order_id,然后在另一个 table table 中添加该订单的每个产品 Foreign Key order_id 18=]。为此,我使用:

$respective_order_id = $connection->lastInsertId();

foreach($_SESSION['cart'] as $product)
{
    $sql = "INSERT INTO purchased_products SET order_id = '{$respective_order_id}', product_name = '{$product['product_name']}', product_price = '{$product['product_price']}', quantity = '{$product['quantity']}'";
}

这些代码同时运行。首先,订单将添加到 orders table 中,其 order_id 自动递增,然后该订单的所有产品也将添加到 purchased_products table 和其中每一个的 Foreign Key order_id 将具有插入 orders table 中的最后一个 order_id 的值。稍后我将可以通过使用外键 order_id.
查询它们的产品来显示任何订单。到目前为止,它运行良好。正如我所说,这是我找到的将订单 ID 分配给属于它的产品的唯一方法。我的问题是:这安全吗?如果几个人同时购买呢?是否存在更换 ID 或未 added/or 的产品走错订单的风险?如果有经验的人回答这些问题,我将不胜感激,因为这让我很害怕,我想知道我是否可以信任这种方法。

编辑
更多详情!
我的数据库连接:

try
{
    $connection = new PDO("mysql:host={$HOST};dbname={$DB_NAME}", $USERNAME, $PASS);
}

catch(PDOException $exception)
{
    echo "Connection error: " . $exception->getMessage();
}

我用来在两个table中插入数据的所有代码:

$sql = "INSERT INTO orders SET customer_id = '{$customer_id}', customer_name = '{$user['customer_name']}', order_price = '{$total_price}', order_date = '{$date}'"

$query = $connection->prepare($sql);
$query->execute();

$respective_order_id = $connection->lastInsertId();

foreach($_SESSION['cart'] as $product)
{
    $sql = "INSERT INTO purchased_products SET order_id = '{$respective_order_id}', product_name = '{$product['product_name']}', product_price = '{$product['product_price']}', quantity = '{$product['quantity']}'";

    $query = $connection->prepare($sql);
    $query->execute();
}

编辑 2 更新
有些产品可以有额外的阵列(即颜色),有些则没有。但所有默认情况下都填充了这些数组:product_nameproduct_priceproduct_quantity
我已经调整我的逻辑以使用 [= 提供的代码检查额外参数50=]peterm:

$connection->beginTransaction();

$sql = "INSERT INTO orders (customer_id, customer_name, order_price, order_date) 
        VALUES (?,?,?,?)";
$query = $connection->prepare($sql);
$query->execute(array
(
    $customer_id, 
    $user['customer_name'], 
    $total_price, 
    $date
));

$respective_order_id = $connection->lastInsertId();

$sql = "INSERT INTO purchased_products (order_id, product_name, product_price, quantity, colour) 
        VALUES (?,?,?,?,?)";

$query = $connection->prepare($sql);

foreach($_SESSION['cart'] as $product)
{
    $additional_param = array
    (
        'colour' => NULL
    );

    $colour = array_filter($product['colour']);

    if(!empty($colour))
    {
        $additional_param['colour'] = $product['colour']['colour_name'];
    }

    $query->execute(array
    (
        $respective_order_id,
        $product['product_name'],
        $product['product_price'],
        $product['quantity'],
        $additional_param['colour']
    ));
}
$connection->commit();

如果SESSION中的额外参数数组不为空,则SQLtable中的colour键将填充产品颜色。否则,将填充SQL NULL.

  1. 使用 PDO 的 lastInsertId() 依赖于 driver,但只要您使用适当的自动递增类型 object(即MySQL 中的 AUTO_INCREMENT 或 PostgreSQL 中的序列)。 MySQL driver 在后台使用 LAST_INSERT_ID()

  2. 如果您关心事务完整性,请考虑将整个操作包装到一个事务中。这样,它要么全部正确提交,要么回滚。没有孤立的订单 header 行,没有部分填充的订单等

  3. 因为您正在使用 PDO stop 进行查询字符串插值。学习和使用 prepared statements

  4. 一旦开始使用准备好的语句,您不仅可以使事情更加安全,而且可以加快订单详细信息行的插入速度。


Yes, I use AUTO_INCREMENT and also prepare in all insertions before execute: $query = $connection->prepare($sql); $query->execute(); I just don't understood your second explanation.

你现在所做的事情既无用又危险。您将查询与值连接起来(可以说您的情况无需准备),而不是在查询中使用占位符然后绑定值。

更新:这是你的样子

$colour = array_filter($product['colour']);

$connection->beginTransaction(); // Turn off auto commit and explicitly open transaction

$sql = "INSERT INTO orders (customer_id, customer_name, order_price, order_date) 
        VALUES (?,?,?,?)"; // Use placeholders in the query. You can use named placeholders if you want to
$query = $connection->prepare($sql); // Prepare the statement
$query->execute(array(
    $customer_id, 
    $user['customer_name'], 
    $total_price, 
    $date)
); // Bind the values and execute the statement

$respective_order_id = $connection->lastInsertId();

$sql = "INSERT INTO purchased_products (order_id, product_name, product_price, quantity, colour) 
        VALUES (?,?,?,?,?)";
$query = $connection->prepare($sql); // Prepare once !!!

foreach($_SESSION['cart'] as $product) {
    $query->execute(array(
        $respective_order_id,
        $product['product_name'],
        $product['product_price'],
        $product['quantity'],
        empty($colour) ? null : $product['colour']['colour_name'], //you can bind null value for nullable columns
    )); // Bind and execute multiple times
}
// Explicitly commit the transaction
$connection->commit();