We are using propel as orm on a mysql db.
We are changing several tables and call external services during a transaction. In between we log these actions and the response in a logging table. If an error occurs, we revert the actions but want to keep the log messages. At the moment the log messages are using the same transaction scope and would be reverted with the transaction.
Do I get a new connection and transactionscope with
$con = Propel::getConnection(DATABASE_NAME);
or do I have to check if the same connection is returned
PSEUDO CODE
public function write_log()
{
$con = Propel::getConnection(DATABASE_NAME);
$log=new Log();
$log->message('foo');
$log->save($con);
}
public function change_data()
{
write_log('start');
$con = Propel::getConnection(DATABASE_NAME);
$con->beginTransaction();
try {
//this message should stay in the database
write_log('change_db_data:'.$new_db_value);
//this should be reverted
change_db_data($new_db_value);
write_log('call webservice_1');
$response=call_webservice_1();
write_log($response);
if($response==null)
{
$con->rollback();
}
write_log('call webservice_2');
$response=call_webservice_2();
write_log($response);
if($response==null)
{
$con->rollback();
}
$con->commit();
}
catch(Exception $e){
$con->rollback();
write_log('error')
}
write_log('end');
}
Good choice picking Propel. You have two choices, either encapsulate the logging in their own transactions, or use a nested transaction, uniquely supported by Propel.
The first requires only transactionality in the write_log function:
public function write_log()
{
$con = Propel::getConnection(DATABASE_NAME);
$con->beginTransaction();
$log=new Log();
$log->message('foo');
$log->save($con);
$con->commit();
}
The second is to start a nested transaction and ensure that only the inner transaction is rolled back:
public function write_log()
{
$con = Propel::getConnection(DATABASE_NAME);
$log=new Log();
$log->message('foo');
$log->save($con);
}
public function change_data()
{
$con = Propel::getConnection(DATABASE_NAME);
$con->beginTransaction();
write_log('start');
$con->beginTransaction();
try {
//this message should stay in the database
write_log('change_db_data:'.$new_db_value);
//this should be reverted
change_db_data($new_db_value);
write_log('call webservice_1');
$response=call_webservice_1();
write_log($response);
if($response==null)
{
throw new \Exception('Null response.');
}
write_log('call webservice_2');
$response=call_webservice_2();
write_log($response);
if($response==null)
{
throw new \Exception('Null response.');
}
$con->commit();
}
catch(Exception $e){
$con->rollback();
write_log('error')
}
write_log('end');
$con->commit();
}
Related
I have created a function which takes three parameters.
My function is simple, it connects to database using pdo but when I call it the data gets inserted, but when I wrap the function inside variable, the if statement doesn't work as expected. My function is just like this
function connect($data,$username,$password){
try { $connect =new PDO($data,$username,$password);
$connect->setAttribute(PDO::ATTR_ERRMODE, ERRMODE_EXCEPTION);
$connect->exec("INSERT INTO data(fname,lname,uname, password) VALUES('raashid','din','dar','wow')");
} catch( Exception $e) {
echo "error occurred". $e->get messages();
}
return;
}
$db =connect("mysql:host=localhost","root","pass");
if($db){
echo "Yes connected";
} else {
echo " not connected";
}
I don't know why if conditional shows not connected while data gets inserted.
Any help will be appreciated.
Empty return; is the same as return null;. And null is considered falsy value, so if($db){ is false and you see the relevant message.
So, you should return another value from your function. For example true when everything is ok and false when exception occured, for example:
function connect($data,$username,$password){
try {
$connect = new PDO($data,$username,$password);
$connect->setAttribute(PDO::ATTR_ERRMODE, ERRMODE_EXCEPTION);
$connect->exec("INSERT INTO data(fname,lname,uname, password) VALUES('raashid','din','dar','wow')");
return true;
} catch( Exception $e) {
echo "error occurred". $e->getMessage();
return false;
}
}
I'm using a class for database connection, this class holds the CRUD methods, and also the database connection methods.
It's basically the core of all other classes that uses the DB.
I'm currently facing a problem on dealing with duplicate entry in unique columns, when inserting a record.
Database.class.php insertDB method:
class Database {
(...)
public function insertDB($sql,$params=null){
$con=$this->connect();
$query=$con->prepare($sql);
$query->execute($params);
$rs = $con->lastInsertId();
return $rs;
self::__destruct();
}
(...)
}
There is another class, PersonDAO.class.php, which is a class that inherits Database.class.
PersonDAO.class.php has its own method for inserting a record, which finally uses Database.class.php InsertBD method.
PersonDAO.class.php insert method: (notice it calls insertDB at the end).
class PersonDAO extends Database{
(...)
public function insert ($fields, $params=null) {
$numparams="";
for($i=0;$i<count($params);$i++) $numparams .= ",?";
$numparams=substr($numparams, 1);
$sql = "INSERT INTO Person ($fields) VALUES ($numparams)";
$t=$this->insertDB($sql,$params);
return $t;
}
(...)
}
The problem occurs in the registration form register.php, when I instantiate PersonDAO and use its insert method for inserting a duplicate entry into a column set as unique.
(...)
$person= new PersonDAO();
$fields="email,name";
$email="john#doe.com"; //already existing record. email column set as unique
$name="John Doe";
$params=array($email,$name);
try {
$rs = $person->insert($fields,$params);
} catch (PDOException $e) {
if ($e->errorInfo[1] == 1062) {
echo "Cannot insert record. Duplicate entry";
}
}
(...)
It's not catching duplicate entry exception, as if there was no errors.
var_dump($rs) contains:
string(1) "0"
But should not it catch PDOException and print "Cannot insert record. Duplicate entry"?
Is there any "Uncaught Exception ..." message ? Perhaps, You first need to catch the Exception in your Database.class.php insertDB method and then throw it.
class Database {
(...)
public function insertDB($sql,$params=null){
try{
$con=$this->connect();
$query=$con->prepare($sql);
$query->execute($params);
$rs = $con->lastInsertId();
return $rs;
self::__destruct();
}
catch(Exception $e){
throw $e;
}
}
(...)
}
Or if there is no error at all. Check
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
In a particular PHP - MySQL application i have been following a practice of creating and destroying the PDO connections from with in the public methods.
For Example:
public function updateCustomerNumber($customer_id, $customer_number){
$conn = new database_class();
$sql = "UPDATE customers set number = :CUSTOMER_NUMBER where id = :CUSTOMER_ID";
$query = $db->prepare($sql);
$query->execute(array(':CUSTOMER_NUMBER' => $customer_number, ':CUSTOMER_ID'=>$customer_id));
$conn->disconnect();
if($query->rowCount()>0){
return true;
} else {
return false;
}
}
So i have connected and disconnected the PDO Connection within the method. Database connect method:
public function connect() {
if (!$this->con) {
try {
$this->db = new PDO("mysql:host=$this->db_host;dbname=$this->db_name;charset=utf8", $this->db_user,$this->db_pass);
$this->db->exec("SET CHARACTER SET utf8");
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->con = true;
return $this->db;
} catch (PDOException $e) {
$error = $e->getMessage();
echo $error;
}
} else {
return $this->db;
}
}
Database disconnect method:
public function disconnect() {
if ($this->con) {
unset($this->db);
$this->con = NULL;
return true;
} else {
return false;
}
}
I haven't come across any issue so far except that a in a couple of method i forgot to disconnect the PDO connection at the end of the script. However, I am not sure if there are any drawbacks of following this approach.
Reason i started questioning this method is when i check the MySQL global status values.
Aborted_clients: 32560
Aborted_connects: 128080
Going by the definition, Even If we don't close connection explicitly, PHP will automatically close the connection when your script ends.
What could be the cause behind these numbers? and is this approach of opening and closing a PHP PDO connections correct ?
I am currently writing a web app in PHP and have decided to use exceptions (duh!).
I could not find an answer to whether putting try and catch blocks in all functions would be considered bad code.
I am currently using Exceptions to handle Database errors (Application errors are handled via a simple function which just adds them to an array and then they are displayed to the user). The try blocks are placed on all functions which require a database connection.
The code in question is:
public function db_conn_verify()
{
if(!isset($this->_mysqli)){
throw new Exception("Network Error: Database connection could not be established.");
} else {
return Null;
}
}
And an example function using this code:
public function get_users() {
try {
$this->db_conn_verify();
//Rest of function code
return True;
} Catch(Exception $e) {
Core::system_error('function get_users()', $e->getMessage());
return False;
}
}
Also would it be better to extend the Exception class and then use that new Exception class to handle application errors?
Thanks
I suggest to you to use something like this:
public function get_users() {
try {
if( !isset($this->_mysqli) ) {
throw new Exception("Network Error: Database connection could not be established.");
}
//Rest of function code
} Catch(Exception $e) {
Core::system_error('function get_users()', $e->getMessage());
}
}
I prefer to use my exends of Exception, but is the same. For exdending exception you can see the PHP documentation to Example #5
EDIT: For an immediate use of try-catch on database connection error you can try this:
try{
$mysqli = new mysqli("localhost", "user", "password", "database");
if ($mysqli->connect_errno) {
throw new Exception("Network Error: Database connection could not be established.");
}
} Catch(Exception $e) {
Core::system_error('function get_users()', $e->getMessage());
}
Here is Database.php:
<?php
/*Data Base Class
* MySQL - InnoDB
* PHP - PDO (PHP Data Object -So we could change databases if needed)
*/
class Database extends PDO{
private $DBH;
function __construct($host, $dbname, $user, $pass){
try {
$this->DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
} catch (PDOException $e) {
return $e->getMessage();
}
}
public function alteration_query($sql){
/* Begin a transaction, turning off autocommit */
$this->DBH->beginTransaction();
try{
$count = $this->DBH->exec($sql);
$this->DBH->commit();
return $count;
}catch (PDOException $e) {
$this->DBH->rollback();
return $e;
}
}
}
?>
Here is test.php:
<?php
require('Database.php');
$dbo = new Database('***.***.com','***','***','***');
echo $dbo->alteration_query('DELETE * from T_Table');
?>
For some reason, it won't give me an error or delete the contents of the T_table.
EDIT: The problem in your case is that the argument is called $sql, but you're using $query to execute it (in the alteration_query method). Next time, please enable error reporting, and/or use a decent IDE, that can show you these errors. Like so:
EDIT2: Set PDO's error mode to exception, this way any error would throw an exception. See updated code.
Don't catch your exceptions inside of the functions, do it outside:
<?php
/*Data Base Class
* MySQL - InnoDB
* PHP - PDO (PHP Data Object -So we could change databases if needed)
*/
class Database
{
private $DBH;
function __construct($host, $dbname, $user, $pass)
{
$this->DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
//Set PDO to throw exceptions on errors!
$this->DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function alteration_query($sql)
{
/* Begin a transaction, turning off autocommit */
$this->DBH->beginTransaction();
$count = $this->DBH->exec($query);
$this->DBH->commit();
return $count;
}
}
try {
$pdo = new Database("localhost", "dbname", "user", "pass");
$pdo->alteration_query("SELECT * FROM wrong_table");
}
catch (PDOException $e) {
die("An error has occured! " . $e->getMessage());
}
This way, you can catch the error exactly where you need it, and not force it inside of the function (which kinda beats the point of the exception).
Also, from the manual:
When the script ends or when a connection is about to be closed, if
you have an outstanding transaction, PDO will automatically roll it
back. This is a safety measure to help avoid inconsistency in the
cases where the script terminates unexpectedly--if you didn't
explicitly commit the transaction, then it is assumed that something
went awry, so the rollback is performed for the safety of your data.
Exceptions halt the execution of the function, meaning the commit() never happens, and it rolls back.
Try this line :
$this->DBH = new parent::__construct("mysql:host=$host;dbname=$dbname", $user, $pass);
Reason : __construct overwrite the main class function. I think that's why, really not sure.
If it's not working, try parrent::PDO. I am pretty sure it an overwrite problem.