J'ai écrit une fonction dans mon modèle pour insérer une commande dans ma base de données. J'utilise des transactions pour m'assurer que tout est valide, sinon il sera annulé.
Mon problème est que CodeIgniter ne montre aucune erreur de base de données, mais il annule la transaction, mais renvoie ensuite TRUE
pour trans_status
. Cependant, cela ne se produit que s'il y a une réduction sur la commande. S'il n'y a pas de réduction sur la commande, tout est validé et fonctionne correctement.
J'utilise actuellement CodeIgniter 3.19, PHP (7.2), MySQL (5.7) et Apache 2.4. (Travailler sur Ubuntu 18.04)
tbl_orders
order_id
et parcourt chacun des produits de la commande (joint order_id
) et insère le produit dans tbl_order_products
, order_product_id
et l'attache à un tableau d'options de présence des utilisateurs et l'insère dans tbl_order_attendance
order_id
) et l'insère dans tbl_transactions
discount_redeem_count
(nombre de codes de rabais échangeables) est diminué de 1 .[Une fonction]:
public function add_order(Order $order, array $order_products, Transaction $transaction = NULL){
$this->db->trans_start();
$order->create_order_code();
$order_array = $order->create_order_array();
$this->db->insert('tbl_orders', $order_array);
$order_id = $this->db->insert_id();
$new_order = new Order($order_id);
foreach($order_products as $key=>$value){
$order_products[$key]->set_order($new_order);
$order_product_array = $order_products[$key]->create_order_product_array();
$this->db->insert('tbl_order_products', $order_product_array);
$order_product_id = $this->db->insert_id();
$product = $order_products[$key]->get_product();
switch ($product->get_product_class()){
case 'Iteration':
$this->db->select('module_id, webcast_capacity, in_person_capacity');
$this->db->from('tbl_modules');
$this->db->where('iteration_id', $product->get_product_class_id());
$results = $this->db->get()->result_array();
break;
case 'Module':
$this->db->select('module_id, webcast_capacity, in_person_capacity');
$this->db->from('tbl_modules');
$this->db->where('module_id', $product->get_product_class_id());
$results = $this->db->get->result_array();
break;
}
if(!empty($results)){
foreach($results as $result){
$module_id = $result['module_id'];
if($result['webcast_capacity'] !== NULL && $result['in_person_capacity'] !== NULL){
$attendance_method = $order_products[$key]->get_attendance_method();
}elseif($result['webcast_capacity'] !== NULL && $result['in_person_capacity'] === NULL){
$attendance_method = 'webcast';
}elseif($result['webcast_capacity'] === NULL && $result['in_person_capacity'] !== NULL){
$attendance_method = 'in-person';
}
$order_product_attendance_array = array(
'order_product_id' => $order_product_id,
'user_id' => $order_products[$key]->get_customer(true),
'module_id' => $module_id,
'attendance_method' => $attendance_method,
);
$order_product_attendance[] = $order_product_attendance_array;
}
$this->db->insert_batch('tbl_order_product_attendance', $order_product_attendance);
}
if(!empty($order_products[$key]->get_discount())){
$discount = $order_products[$key]->get_discount();
}
}
if(!empty($transaction)){
$transaction->set_order($new_order);
$transaction_array = $transaction->create_transaction_array();
$this->db->insert('tbl_transactions', $transaction_array);
$transaction_id = $this->db->insert_id();
}
if(!empty($discount)){
$this->db->set('discount_redeem_count', 'discount_redeem_count-1', false);
$this->db->where('discount_id', $discount->get_discount_id());
$this->db->update('tbl_discounts');
}
if($this->db->trans_status() !== false){
$result['outcome'] = true;
$result['insert_id'] = $order_id;
return $result;
}else{
$result['outcome'] = false;
return $result;
}
}
Lorsque cette fonction termine avec une remise , trans_complete
et trans_status
renvoient TRUE
. Cependant, la transaction n'est jamais engagée.
J'ai vidé le contenu de $this->db->error()
après chaque requête et il n'y a aucune erreur dans aucune des requêtes.
J'ai utilisé this->db->last_query()
pour imprimer chaque requête, puis j'ai vérifié la syntaxe en ligne pour voir s'il y avait des problèmes, il n'y en avait pas.
J'ai également essayé de changer pour utiliser CodeIgniters Manual Transactions comme:
[Exemple]
$this->db->trans_begin();
// all the queries
if($this->db->trans_status() !== false){
$this->db->trans_commit();
$result['outcome'] = true;
$result['insert_id'] = $order_id;
return $result;
}else{
$this->db->trans_rollback();
$result['outcome'] = false;
return $result;
}
echo
ing et var_dump
ing tous les return insert_ids
et ils fonctionnent tous, j'ai également sorti le affected_rows()
de la requête UPDATE
et il montre qu'une ligne a été mise à jour. Cependant, rien n'est encore commis:[Valeurs Dumpées]
int(10) // order_id
int(10) // order_product_id
array(3) {
["module_id"]=> string(1) "1"
["webcast_capacity"]=> string(3) "250"
["in_person_capacity"]=> string(3) "250" } // $results array (modules)
array(1) {
[0]=> array(4) {
["order_product_id"]=> int(10
["user_id"]=> string(1) "5"
["module_id"]=> string(1) "1"
["attendance_method"]=> string(7) "webcast" } } // order_product_attendance array
int(9) // transaction_id
int(1) // affected rows
string(99) "UPDATE `tbl_discounts`
SET discount_redeem_count = discount_redeem_count- 1
WHERE `discount_id` = 1" // UPDATE query
- J'ai également essayé de remplacer la dernière requête UPDATE
par une requête complètement différente qui tente de mettre à jour une table différente avec des valeurs différentes. Cette requête AUSSI n'a pas fonctionné, ce qui me fait penser que je suis en train d'atteindre une sorte de limite de mémoire avec la transaction. Toutefois, lors de la surveillance des processus mysqld
, aucun d’entre eux ne semble avoir de difficulté ni de difficulté.
Nous avons essayé de définir log_threshold
sur 4 et avons examiné les fichiers journaux CodeIgniter qui ne montrent aucun historique de restauration.
Nous avons vérifié le journal de requêtes mySQL:
[Journal de requête]
2018-12-03T15:20:09.452725Z 3 Query UPDATE `tbl_discounts` SET discount_redeem_count = discount_redeem_count-1 WHERE `discount_id` = '1'
2018-12-03T15:20:09.453673Z 3 Quit
Cela montre qu'une commande QUIT
est envoyée directement après la requête UPDATE
. Cela initierait une restauration, cependant le trans_status
renvoie TRUE
.
J'ai également changé mon fichier my.cnf
pour mySQL afin qu'il ait innodb_buffer_pool_size=256M
et innodb_log_file_size=64M
. Il n'y a pas eu de changement dans le résultat.
UPDATE
pour utiliser un simple_query()
au lieu d'utiliser les méthodes par défaut de la classe Query Builder Class de CodeIgniter:[Requête simple]
if(!empty($discount)){
$this->db->simple_query('UPDATE `tbl_discounts` SET '.
'discount_redeem_count = discount_redeem_count-1 WHERE '.
'`discount_id` = \''.$discount['discount_id'].'\'');
}
Cependant, cela n'a pas affecté le résultat différemment .
Si vous avez une idée que je n'ai pas encore essayée ou si vous avez besoin de plus d'informations de ma part, merci de commenter et je vous répondrai rapidement.
Pourquoi trans_status
renvoie-t-il TRUE
si aucune de mes transactions n'est en cours de validation?
Afin d'essayer d'apporter plus de clarté aux utilisateurs qui viennent de trouver cette question maintenant, les dernières mises à jour du message apparaîtront en italique *
J'ai trouvé mon problème. Je tiens à remercier toutes les personnes qui ont essayé d’aider, mais celle-ci était de ma faute.
Auparavant, dans la méthode Controller qui appelle cette fonction, j'ai appelé une autre fonction qui démarre une transaction. Cette transaction n’a jamais été finalisée et a donc été reprise dans cette nouvelle transaction.
Comme la transaction n’était tout simplement pas validée et qu’il n’y avait pas d’erreur, je n’ai trouvé aucune erreur ni historique d’annulation. Cependant, dès que j'ai clôturé la transaction précédente, tout a fonctionné.
Il n'y avait aucune preuve de problèmes dans le journal de requête mySQL, le journal des erreurs mySQL ou les journaux des erreurs CodeIgniter. Je n'ai pu trouver ce problème qu'en lisant lentement l'intégralité du journal de requête mySQL.
Pour tous ceux qui rencontrent ce problème: Vérifiez vos autres transactions et assurez-vous qu'elles sont toutes clôturées.
Basé sur EDIT 5 :
Ce
$this->db->set('discount_redeem_count', 'discount_redeem_count-1', false);
devrait fonctionner (la coche en arrière ne ferait pas .. l’intérêt de passer le troisième paramètre false
est de faire en sorte que CI n’échappe pas à vos paramètres avec des backticks, ce qui empêcherait que l’instruction set soit transmise sous forme de chaîne.
J'ai fait quelques tests rapides dans mon propre code de développement avec une mise à jour similaire à celle-ci et la seule façon de le faire échouer a été de modifier la table en cours de mise à jour pour que le champ (discount_redeem_count
dans votre cas) ne soit pas numérique. Si mon domaine était, par exemple, un VARCHAR, cela ne fonctionnerait pas, mais lorsque je l'ai essayé sur un champ INT, cela fonctionnait sans problème.
Etes-vous certain que le champ discount_redeem_count
est numérique?
(Ces deux suggestions ont été essayées, en vain.)
Suggestion 1
Peut-être que c'est la vraie réponse:
trans_status () doit être exécuté lorsque vous êtes dans une transaction. Dans votre exemple, trans_complete () réinitialise l'indicateur d'état.
(Cependant, cela est triste si vous utilisez Galera ou une réplication de groupe, car une transaction peut toujours échouer lors de l'exécution de COMMIT
.)
Suggestion 2
These are `== NULL`: NULL, '', FALSE, 0
These are `!== NULL`: '', FALSE, 0
Remarquez comment vous utilisez le "triple" !==
pour certains tests avec NULL, mais utilisez le "double" ==
pour les autres.
Faites ceci pour voir ce que vous obtenez réellement:
var_dump($result['webcast_capacity']);
Peut-être essayez-vous de remplacer votre code de mise à jour par un appel à simple_query?
Changement:
if(!empty($discount)){
$this->db->set('discount_redeem_count', 'discount_redeem_count-1', false);
$this->db->where('discount_id', $discount['discount_id']);
$this->db->update('tbl_discounts');
}
À:
if(!empty($discount)){
$this->db->simple_query('UPDATE `tbl_discounts` SET '.
'discount_redeem_count = discount_redeem_count-1 WHERE '.
'`discount_id` = \''.$discount['discount_id'].'\'');
}
J'ai jeté un coup d'œil autour du code source de CodeIgniter, et il semble que la fonction de requête par défaut effectue beaucoup de tâches ménagères qui risquent de tout gâcher. Et la fonction simple_query a ce peu de documentation:
/**
* Simple Query
* This is a simplified version of the query() function. Internally
* we only use it when running transaction commands since they do
* not require all the features of the main query() function.
*
* @param string the sql query
* @return mixed
*/
Je pense à votre champ de base de données discount_redeem_count
nom.
Etes-vous sûr que discount_redeem_count
n'est pas un numéro? car voici que vous essayez de pousser une valeur de chaîne. Donc, le champ de base de données doit être var ou text.
Peut-être utile.
Merci.