Yii integrity constraint exception handling with user friendly message - exception

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

Related

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails

i have reply_qs table and postqs table.Postqs_id is foreign key in reply_qs table.when i tried to save the reply_qs form data in database,its showed this error.
ERROR:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or
update a
child row: a foreign key constraint fails (fyp2.reply_qs, CONSTRAINT
reply_qs_postqs_id_foreign FOREIGN KEY (postqs_id) REFERENCES postqs
(id) ON DELETE CASCADE ON UPDATE CASCADE) (SQL: insert into reply_qs
(reply, updated_at, created_at) values (saann, 2017-09-22 15:35:03,
2017-09-22 15:35:03))
how i can solve it? and please explain why im getting this error.
reply_qs model :
protected $table = 'reply_qs';
protected $fillable = ['reply'];
public function postqs(){
return $this->hasMany('App\Postqs');
}
postqs model :
public function reply_qs(){
return $this->hasMany('App\Reply_qs');
}
store function:
public function store(Request $request){
$data = $request->all();
$postqs=($request->id);
$reply=Reply_qs::create($data);
$reply->postqs($id);
}
migration:
Schema::create('reply_qs', function (Blueprint $table) {
$table->increments('id')->unique();
$table->text('reply');
$table->timestamps('date');
});
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
Schema::table('reply_qs',function(Blueprint $table)
{
$table->integer('postqs_id')->unsigned();
$table->foreign('postqs_id')->references('id')->on('postqs') -
>onDelete('cascade')->onUpdate('cascade');
});
DB::statement('SET FOREIGN_KEY_CHECKS=1;');
Your relationships are not correct.
Your Reply_qs model should have this relationship with Postqs
public function postqs()
{
return $this->belongsTo(Postqs::class);
}
Your store function does not look correct and should look something like this
public function store( Request $request )
{
$reply = new Reply_qs();
$reply->text = $request->get('text');
$reply->postqs_id = $request->get('postqs_id');
$reply->save();
}
In your store method, it looks like you are trying to associate the parent Postqs object with the Reply_qs.
You would do this like so
$reply->associate($post);
You need to pass the object and not the id
If you are still struggling with this double check your foreign keys are matching correctly, you may need to implement the association like so
public function postqs()
{
return $this->belongsTo(Postqs::class, 'postqs_id', 'id);
}

How to handle Integrity constraint violation on delete?

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/>";
}
}

Laravel skip constraint violation upon first time creating model

I want to know if it is possible to skip or ignore the constraint violation upon creating a user. I have a user model with a foreign key address_id.
Schema::table('user', function($table) {
$table->foreign('address_id')->references('address_id')->on('address')->onDelete('cascade');
});
Upon creating a user i want to create a user without a address yet which will be added later on.
return User::create([
'first_name' => $data['firstname'],
'last_name' => $data['lastname'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
but hence the constraint violation. It is possible to do this?
EDIT:
Thus i would like to keep the constraints but the example above runs into a constraint violation so what is the best way to work around this? Making a dummy address entry?
You can alter the DB for the time you are adding the model:
protected function turnOffDatabaseChecks()
{
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
Eloquent::unguard();
}
protected function turnOnDatabaseChecks()
{
DB::statement("SET sql_mode = 'STRICT_ALL_TABLES';");
Eloquent::reguard();
}
Then in your user model:
$this->turnOffDatabaseChecks();
$user = User::create([
'first_name' => $data['firstname'],
'last_name' => $data['lastname'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
$this->turnOnDatabaseChecks();
return $user;
However, I'd be very cautious in using this and would ask yourself why you have a foreign key constraint that you wish to ignore.

integrity violation while seeding

This is my seeder class below
<?php
use Illuminate\Database\Seeder;
class RequestTableSeeder extends Seeder
{
public function run()
{
$faker = Faker\Factory::create();
for($i=1;$i<=5;$i++){
DB::table('requests')->insert([
"location_id"=>$faker->numberBetween(1,5),
"level_id"=>$faker->numberBetween(0,1),
"subject_id"=>$faker->numberBetween(0,1),
"first_name"=>$faker->firstName,
"last_name"=>$faker->lastName,
"contact"=>$faker->unique()->phoneNumber,
"email"=>$faker->unique()->email,
"description"=>$faker->text(1000),
]);
}
}
}
Here is my levelseeder class:
<?php
use Illuminate\Database\Seeder;
class SubjectTableSeeder extends Seeder
{
public function run()
{
$faker = Faker\Factory::create();
for($i=1;$i<=5;$i++)
{
DB::table('subjects')->insert([
"name"=>$faker->text(5),
]);
}
}
}
while i try to seed from the command i get:
[PDOException]
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`tutor`.`requests`, CONSTRAINT `requests_level_id_foreign` FOREIGN KEY (`level_id`) REFERENCES `levels` (`id`) ON DELETE CASCADE)
i also checked my subject seeder class.But i could not find the error.This are my seeder class
You are trying to insert a level_id that references a row in the level table that doesn't exist!
In order for this to work your level table needs to be seeded with at least 5 records with the id of 1,2,3,4,5
If you are doing this, maybe the order of your seeders is wrong. Make sure the LevelTableSeeder runs before the RequestTableSeeder.
Further, it seems that subject_id will most likely fail next. This is intended behavior when using foreign keys which are used to ensure database integrity.

Can't store a new row - SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails

My clients table and users table are child tables of my businesses table. So both the clients and users table contain a business_id column that refers to the id column of the businesses table:
public function up()
{
Schema::create('clients', function(Blueprint $table)
{
$table->increments('id');
$table->integer('business_id')->unsigned();
$table->foreign('business_id')->references('id')->on('businesses');
$table->string('first_name');
$table->string('last_name');
Etc…
I am able to store a new user, it works fine, but I keep getting this error when trying to store a new row into my clients table :
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or
update a child row: a foreign key constraint fails
(laravel.clients, CONSTRAINT clients_business_id_foreign FOREIGN
KEY (business_id)
Client.php model
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['business_id', 'first_name', 'last_name'];
/**
* Business where this user works.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function businesses()
{
return $this->belongsTo('App\Business');
}
Business.php model
/**
* Clients that use this business.
*
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function clients()
{
return $this->hasMany('App\Client','business_id');
}
ClientsController.php
public function store(ClientsRequest $request)
{
var_dump(Auth::user()->business_id);
$clientinfo = Request::all();
Client::create($clientinfo);
$client = new Client;
$client->business_id = Auth::user()->business_id;
$client->first_name = $clientinfo['first_name'];
$client->last_name = $clientinfo['last_name'];
//$client->save();
$business->clients()->save($client);
$last_inserted_id = $data->id;
return redirect('clients/'.$last_inserted_id.'/edit');
The only thing I’m not sure is the way I’m retrieving the business_id (from the users table), via Auth. When I var_dump that value, I get :
string(1) "7"
I know the id value is 7 in my businesses table, that’s what I’m looking for, but not sure if it should be converted to integer.
And, not sure if I have to save using :
$client->save();
Or
$business->clients()->save($client);
Thanks
Both following are correct ways, but first one is more Laravel than later,
public function store(ClientsRequest $request)
{
$client = new Client([
'first_name' => $request->get('first_name'),
'last_name' => $request->get('last_name'),
]);
$client = Business::findOrFail(Auth::user()->business_id)
->clients()
->save($client);
return redirect('clients/'.$client->id.'/edit');
}
P.S. above business_id will be automatically added to client by Eloquent
Other way is like you did, where you are inserting a business_id manually.
public function store(ClientsRequest $request)
{
$clientinfo = Request::all();
$client = Client::create([
'business_id' => Auth::user()->business_id;
'first_name' => $clientinfo['first_name'];
'last_name' => $clientinfo['last_name'];
]);
return redirect('clients/'.$client->id.'/edit');
}
You are receiving error because of following statement in your code,
Client::create($clientinfo);
Above statement will attempt to create a database entry without business_id which is a foreign key and should be supplied but wasn't and hence error.
And you don't have to worry about type-conversion between int<->string, PHP is very good at it.
Read More