I have rows, which some rows can't be delete because it referenced to other table, and the other can be delete.
What I want is delete rows which can be delete and leave the other rows which can't be delete
so far my code is
$tkota = TbKota::find()->all();
foreach($tkota as $kota){
if($kota->delete()){
echo "del success<br/>";
}else{
echo "fail ".$kota['api_id']."<br/>";
}
}
my above code produce this error
SQLSTATE[23503]: Foreign key violation: 7 ERROR: update or delete on table "tb_kota" violates foreign key constraint "fk_tb_produ_reference_tb_kota" on table "tb_produk_ekspedisi_by_vendor"
DETAIL: Key (kota_id)=(1771) is still referenced from table "tb_produk_ekspedisi_by_vendor".
The SQL being executed was: DELETE FROM "tb_kota" WHERE "kota_id"=1771
instead of show success when record deleted and show fail if record can't be delete.
what's wrong with my code?
thanks in advance.
This one will be better
use yii\db\IntegrityException;
use yii\web\NotFoundHttpException;
foreach($tkota as $kota){
$connection = \Yii::$app->db;
$transaction = $connection->beginTransaction();
try {
$kota->delete();
$transaction->commit();
return $this->redirect(['user/view', 'id' => $model->id]);
}catch (IntegrityException $e) {
$transaction->rollBack();
throw new \yii\web\HttpException(500,"YOUR MESSAGE.", 405);
}catch (\Exception $e) {
$transaction->rollBack();
throw new \yii\web\HttpException(500,"YOUR MESSAGE", 405);
}
}
foreach($tkota as $kota){
try {
if($kota->delete()){
echo "del success<br/>";
}
} catch (\Exception $e) {
echo "fail ".$kota['api_id']."<br/>";
}
}
Related
I have a problem and it is that I need to lock tables within a transaction to prevent data from being inserted into it, but when I do this when I have an error the transaction rollback does not work.
I read in mysql articles that table locks break a transaction, but is there any other way to lock tables to prevent data from being inserted into it temporarily?
https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html
try {
DB::beginTransaction();
DB::unprepared('LOCK TABLES table_name WRITE');
//
DB::unprepared('UNLOCK TABLES');
DB::commit();
} catch (\Exception $e){
DB::rollBack();
}
You can lock the tables in the outer scope, outside of the transaction.
Then, your example code would look like:
try
{
DB::unprepared('LOCK TABLES table_name WRITE');
try
{
DB::beginTransaction();
//...
DB::commit();
}
catch (\Exception $e)
{
DB::rollBack();
}
}
finally
{
DB::unprepared('UNLOCK TABLES');
}
From mysql table at first want to delete row. Only after the row deleted, i want to select sum(certain_column). Want to be sure that until row deleted, i do not execute select.
Decided to use such code like this:
try{
$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$db->beginTransaction();
try{
$stmt_delete = $db->prepare('DELETE FROM `table` WHERE `Id` = 1;');
$stmt_delete_items->execute( );
}
catch (PDOException $e){
echo "DataBase Error: " .htmlspecialchars( $e->getMessage() , ENT_QUOTES, "UTF-8").'<br>';
$db->rollBack();
exit;
}
try{
$stmt_select = $db->prepare('SELECT SUM(`Quantity`) AS `SumQuantity` FROM `table` WHERE `some_column` = 234;');
$stmt_select->execute( );
$arr_select = $stmt_select->fetchAll(PDO::FETCH_ASSOC);
}
catch (PDOException $e){
echo "DataBase Error: " .htmlspecialchars( $e->getMessage() , ENT_QUOTES, "UTF-8").'<br>';
}
$db->commit();
}
Expecting at first execute $stmt_delete and only then $stmt_select. In usual case i get such execution order. But is it guaranteed 100%? Somewhere read that delete may take more time than select... with exit; i prevent next execution only in case of error. But i need to delay $stmt_select until $stmt_delete.
Or may be better to use START TRANSACTION; ... COMMIT;. Or may be for DELETE to use LOCK TABLES ... UNLOCK TABLES.
Please advice solution to be 100% sure that select executes only after delete is finished.
I have these tables in MySQL database:
users['id', 'name'],
roles['id', 'title'] and
user_role ['user_id', 'role_id'] where both are foreign keys, CASCADE.
When it catches an exception the user remains in the table as wanted, while the row from the relation table is deleted.
try{
$user->delete();
}
catch (\Exception $e){
throw new \Dingo\Api\Exception\DeleteResourceFailedException('Error.');
}
Is this eloquent's mistake?
Now, I figured out a way to fix this but I'm not sure that it's the best practise. Is there a better way to do it?
try{
$roleId = $user->roles[0]->id;
$user->delete();
}
catch (\Exception $e){
$user->roles()->attach($roleId);
throw new \Dingo\Api\Exception\DeleteResourceFailedException('Error.');
}
If i understood your question, transactions are what you need.
Database Transactions
DB::transaction(function () {
$user->delete();
});
and in case you face a deadlock use this one
DB::transaction(function () {
$user->delete();
},5);
I am using this model code to delete a record.
public function actionDelete($id)
{
$this->loadModel($id)->delete();
// if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser
if(!isset($_GET['ajax']))
$this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin'));
}
The table containing this record has one to many relationship with other table with on delete restrict constraint.
So when deleting a record which has related records in child table it throws exception like
CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`bzuexamsystem`.`campus`, CONSTRAINT `fk_Campus_City` FOREIGN KEY (`CityID`) REFERENCES `city` (`CityID`) ON UPDATE CASCADE). The SQL statement executed was: DELETE FROM `city` WHERE `city`.`CityID`=1
Is there someway to show user friendly error message. Thanks
You need to catch exception. Something like
try{
$this->loadModel($id)->delete();
if(!isset($_GET['ajax']))
$this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin'));
}catch (CDbException $e){
if($e->getCode()===23000){
//You can have nother error handling
header("HTTP/1.0 400 Relation Restriction");
}else{
throw $e;
}
}
If you also use CGrigView in your view file, you should pass "ajaxUpdateError" function to it.
Example:
$this->widget('zii.widgets.grid.CGridView',
array(
'id' => 'model-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'ajaxUpdateError' => <<<JS
function(xhr, ts, et, err){
if(xhr.statusText==="Relation Restriction"){
$("#errorDiv").text("That model is used by something!");
}
else{
alert(err);
}
}
JS
,
'columns' =>
array(
'model_id',
'name'
)
)
);
I guess, $this->loadModel() returns a CActiveRecord object...
First at all you need to make sure the record you want to delete is really loaded. Second, use # at the start of the statement do disallow errors. Then if the CActiveRecord->delete() returns false, it means, that the record wasn't deleted:
public function actionDelete($id) {
$record = $this->loadModel($id);
if ($record !== null) {
if (#$record->delete()) {
// code when successfully deleted
} else {
// code when delete fails
}
} else {
// optional code to handle "record isn't found" case
}
}
You cannot delete rows that have restrict on the foreign key, change that to set to null, or no action depending on your requirements
so your key would be set to null on delete
and cascade on update
I've run into an issue that I'm hoping to get a little help on. I'm using the following:
Kohana 3.0.7
PostgreSQL 8.4
Transactions in PostgreSQL using
$db->query(NULL, 'BEGIN', FALSE)
$db->query(NULL, 'ROLLBACK', FALSE);
$db->query(NULL, 'COMMIT', FALSE);
The issue is that when I send a query to the database that results in a postgres error within a transaction my system freezes up. When I send the same query to the database without wrapping it in a transaction the PDO error is reported back just as expected. Here is an exmaple:
This first example works fine and duplicate key value violates unique constraint "pk_test_table" error is returned:
$query = DB::query(Database::INSERT, 'INSERT INTO test_table (test_table_id, test_table_val) VALUES (:id, :value)';
$query->param(':id', 1);
$query->param(':value', "test value");
try
{
$result = $query->execute($db);
}
catch (Exception $e)
{
echo 'Caught exception: ', $e->getMessage(), "\n";
}
This second example causes my system to freeze (I can't tell if it's an infinite loop, or some other freeze):
$db->query(NULL, 'BEGIN', FALSE);
$query = DB::query(Database::INSERT, 'INSERT INTO test_table (test_table_id, test_table_val) VALUES (:id, :value)';
$query->param(':id', 1);
$query->param(':value', "test value");
try
{
$result = $query->execute($db);
}
catch (Exception $e)
{
echo 'Caught exception: ', $e->getMessage(), "\n";
}
$db->query(NULL, 'ROLLBACK', FALSE);
As you can see the only difference is that the second example is wrapped in a transaction.
Any ideas on what is going on? Any suggestions for things to try?
I found a way to work around the issue. Not sure if this is a bug in PDO or some other part of the tool set but what I'm doing to work around is the following:
$exception_exists = FALSE;
$db->query(NULL, 'BEGIN', FALSE);
$query = DB::query(Database::INSERT, 'INSERT INTO test_table (test_table_id, test_table_val) VALUES (:id, :value)';
$query->param(':id', 1);
$query->param(':value', "test value");
try
{
$result = $query->execute($db);
}
catch (Exception $e)
{
echo 'Caught exception: ', $e->getMessage(), "\n";
$exception_exists = TRUE;
}
if (!$exception_exists)
{
$db->query(NULL, 'ROLLBACK', FALSE);
}
By adding the variable $exception_exists in the catch I can then act of that if there is no exception. If there is an exception and I try to ROLLBACK or COMMIT then I get the freezing behavior.
This works for now but I wouldn't call it elegant.