Как определить, обновляют ли 2 админа одновременно строку таблицы в php - PullRequest
0 голосов
/ 24 мая 2019

Веб-сайт, который я разрабатываю, имеет страницу администратора для добавления продуктов в базу данных или редактирования существующих продуктов в базе данных.Если 2 администратора хотят изменить один и тот же продукт, второй администратор должен получить уведомление о том, что продукт уже обновлялся / обновляется.Как я могу определить в php / sql, когда 2 администратора пытаются изменить один и тот же продукт?

Я ничего не пробовал, потому что понятия не имею, что попробовать и с чего начать.

Вот как выглядит моя функция для обновления продукта в базе данных:

//I know this is a VERY unsafe function, this website will never go online!
FUNCTION updateProduct($data) {
  include_once 'dbconn.php';
  try {
    $conn = connectToDb();
    if ($data['imageChanged'] == false) {
      $query = "SELECT img_url FROM products WHERE product_id=".$data['productId'];
      $result = mysqli_query($conn, $query);
      if ($result == false) {
        throw new Exception('Failed to execute: '. $query . 'Error: '. mysqli_error($conn));
      }
      $imgUrl = mysqli_fetch_row($result)[0];
    } else {
      $imgUrl = $data['image'];
    }
    $query2 = "img_url='".$imgUrl."'";
    $query = "UPDATE products
              SET product_name='".$data['productName']."', product_desc='".$data['productDesc']."', price=".$data['price'].", ". $query2 . " 
              WHERE product_id=".$data['productId'];
    $result = mysqli_query($conn, $query);
    if ($result == false) {
      throw new Exception('Failed to execute: '. $query . 'Error: '. mysqli_error($conn));
    }
  } finally {
    mysqli_close($conn);
  }
}

edit: сохранение старых данных после выполнения обновления не требуется, а также сохранение сохраняемых обновлений (в отношении вопроса это может бытьдубликат).Я хотел бы знать следующее: если admin_1 обновляет строку, а admin_2 пытается обновить эту же строку, когда транзакция admin_1 все еще продолжается, как мой сценарий может обнаружить, что это происходит?

Ответы [ 2 ]

2 голосов
/ 24 мая 2019

Обычно это делается путем добавления столбца версии, который обновляется при каждом изменении строки. Перед обновлением строки убедитесь, что значение не изменилось с момента последнего прочтения строки:

SELECT img_url, version FROM products WHERE product_id = ?;
-- Suppose this returns ('https://example.com/foo.jpg', 1)
-- Remember that the current version is 1.

Позже:

BEGIN;

SELECT version FROM products WHERE product_id = ? FOR UPDATE;
-- Check if version is still 1. If it's not, someone modified the row (execute ROLLBACK in this case). 
-- Otherwise:
UPDATE products SET img_url = ?, version = version + 1 WHERE product_id = ?;

COMMIT;

Если по какой-либо причине транзакции недоступны, альтернативой является использование простого сравнения и обмена:

UPDATE products SET img_url = ?, version = version + 1 WHERE product_id = ? AND version = 1;

Если количество обновленных строк равно нулю, строка тем временем была изменена.

Возможно, это немного быстрее, чем SELECT FOR UPDATED с последующим UPDATE. Однако наличие конфликтующей версии строки обеспечивает более богатую обратную связь с пользователем. С помощью столбца автора вы можете указать, например, , кто обновил строку, а не только то, что это произошло.

0 голосов
/ 24 мая 2019

рассмотрите следующие пункты 1) вы хотите, чтобы несколько администраторов редактировали разные продукты 2) вы не хотите, чтобы несколько администраторов редактировали один и тот же продукт

3) в вашем коде и сохраняете продукт в виде одной таблицы.На реальном сайте электронной коммерции информация о продукте разделена на несколько таблиц

, чтобы достичь этого, вам нужно установить блокировки для mysql всего приложения

mysql> select get_lock('product_id_12345', 5);
+---------------------------------+
| get_lock('product_id_12345', 5) |
+---------------------------------+
|                               1 |
+---------------------------------+
1 row in set (0.00 sec)

. Код выше устанавливает блокировку с помощью product_id,если какое-либо другое соединение пытается получить блокировку, используя тот же product_id, вы получите 0 в качестве ответа - это означает, что какой-то другой пользователь обновляет этот же product_id

mysql> 
mysql> select IS_FREE_LOCK('product_id_12345');
+----------------------------------+
| IS_FREE_LOCK('product_id_12345') |
+----------------------------------+
|                                0 |
+----------------------------------+
1 row in set (0.00 sec)

mysql> 
mysql> select IS_USED_LOCK('product_id_12345');
+----------------------------------+
| IS_USED_LOCK('product_id_12345') |
+----------------------------------+
|                               46 |
+----------------------------------+
1 row in set (0.00 sec)

mysql> 
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
|              46 |
+-----------------+
1 row in set (0.00 sec)

SAMPLE ALGORITHM

<?php
ini_set('display_errors', 1);

function updateProduct() {

$user = 'root';
$pass = 'xxx';
$DB = 'test';
$host = 'localhost';

  try 
  {
    $conn = new mysqli($host, $user, $pass, $DB);


    $data['productId'] = 'product_id_12345';
    $data['productName'] = 'test';
    $data['productDesc'] = 'testing';

    $isLockFree = 'select IS_USED_LOCK("'.$data['productId'].'") ';
    $isLockFreeResult = mysqli_query($conn, $isLockFree);
    $row=mysqli_fetch_row($isLockFreeResult);

    if(empty($row[0]))
    {
        $lock = 'select get_lock("'.$data['productId'].'")';
        $result = mysqli_query($conn, $lock);

        echo 'lock established';

        $query = "UPDATE products
                  SET product_name='".$data['productName']."', 
                      product_desc='".$data['productDesc']."', 
                      price=".$data['price']."
                  WHERE 
                  product_id=".$data['productId'];

        $result = mysqli_query($conn, $query);
        if ($result == false) 
        {
          throw new Exception('Failed to execute: '. $query . 'Error: '. mysqli_error($conn));
        }
    }
    else
    {
        echo 'sorry! could not lock. somebody is updating the product info';
    }
  } 
  finally 
  {
    mysqli_close($conn);
  }
}
updateProduct();
?>
...