I am trying to update but it is adding a new row - php

this is my code for updating:
PS: empid is a foreign key but i think that shouldnt be the reason and the code is in CakePHP
if($this->request->is('post'))
{
$this->request->data["Leave"]["empid"] = $this->request->data["id"];
$this->Leave->empid = $this->request->data["Leave"]["empid"];
$this->request->data["Leave"]["leave_start"] = $this->request->data["start_date"];
$this->request->data["Leave"]["leave_end"] = $this->request->data["end_date"];
$this->request->data["Leave"]["leave_taken"] = $this->request->data["leave_taken"];
if($this->Leave->save($this->request->data['Leave']))
{
return $this->redirect(array('action' => 'manage_leave'));
}
}
// This code is inserting a new row instead of updating and also not adding any value in the new row

May be your trying to update the foreign table data using simple save.
Update multiple records for foreign key
Model::updateAll(array $fields, mixed $conditions)
Example
$this->Ticket->updateAll(
array('Ticket.status' => "'closed'"),
array('Ticket.customer_id' => 453)
);
Simple save for the primary key
Make sure that your HTML has empid
echo $this->Form->input('Leave.empid', array('type' => 'hidden'));
Save Model
$this->Leave->empid = $this->request->data["Leave"]["empid"]; //2
$this->Leave->save($this->request->data);
In between, you can also try to set the model data and check the $this->Leave->validates() and $this->Leave->validationError if they are giving any validation errors.
// Create: id isn't set or is null
$this->Recipe->create();
$this->Recipe->save($this->request->data);
// Update: id is set to a numerical value
$this->Recipe->id = 2;
$this->Recipe->save($this->request->data);
You can find more information about all Saving your data
Hope this helps you :)

And in case if $empid is primary key of corresponding table of Leave model (e.g leaves), Just replace:
$this->Leave->empid = $this->request->data["Leave"]["empid"];
By
$this->Leave->id = $this->request->data["Leave"]["empid"];

Related

Laravel/Mysql: unable to set field to null

I am working on a laravel project and trying to duplicate a record in mysql db.
after replication I want to set a field value to null(appointment_status).
everything is working except the new record's value (appointment_status) is the same as the original record even tho I set it to null.
$newAppointment = $appointment->replicate();
//push to get the id for the cloned record
$newAppointment->push();
$newAppointment->duplicated_from_id = $appointment->id;
$newAppointment->appointment_status = null;
$newAppointment->save();
//updating the old appointment
$appointment->duplicated_to_id = $newAppointment->id;
$appointment->save();
Instead of whole code-block, try something like this:
// replicate as the new record with some different fields
$newAppointment = $appointment->replicate()->fill([
'duplicated_from_id' => $appointment->id,
'appointment_status' => null,
])->save();
// update some fields of initial original record
$appointment->update([
'duplicated_to_id' => $newAppointment->id,
]);
For more check out this.

yii1 update DB query giving error

I want to update one field in my db. I have the below query, but I am getting error in this.
$list = Test::model()->find(array(
'select'=>'name',
'condition'=>'id=:id AND name=:name AND provider="fb"',
'params'=>array(
':id'=>Yii::app()->user->id,
':name'=> $name,
),
));
$list->name = $user_name[$k]['name'];
if($list->save())
{
echo "done";exit;
}
else
{
$error = $list->getErrors();
var_dump($error);exit;
}
Error:
'Column name must be either a string or an array
This happens when you don't have a primary key in your table and you try to save some data in table. check if you have set primary key and it set properly. more details

Update a row using idiorm and php

I have this function to update a record, but i cannot it fails and send me a "Primary key ID missing from row or is null" message, how can I fix it?
public static function update_child($data)
{
try
{
$update= ORM::for_table("dm_child",DM_TAG)
->where_equal($data["id_child"]);
$update -> set(array(
"gender" => $data["gender"]
"age_year" =>$data["year"]
"age_month" => $data["month"]
));
$update -> save();
}
catch(PDOException $ex)
{
ORM::get_db()->rollBack();
throw $ex;
}
}
Idiorm assumes that the name of the primary key is 'id', which is not that, in your case.
Therefore you have to explicitly specify it to Idiorm:
<?php
ORM::configure('id_column_overrides', array(
'dm_child' => 'id_child',
'other_table' => 'id_table',
));
See Docs>Configuration.
The answer is indeed the one provided by #iNpwd for changing the default 'id' column name for queries on a per table basis:
ORM::configure('id_column_overrides', array(
'table_name' => 'column_name_used_as_id',
'other_table' => array('pk_1', 'pk_2') // a compound primary key
));
The thing that was biting me on getting it to recognize my query was WHERE I was changing the ORM::configure values. I was not in the correct file.
A deeper link to specifically the ID Column configuration: http://idiorm.readthedocs.org/en/latest/configuration.html#id-column
I just met this problem 2 minutes ago. The real reason is, you forgot select id field in querying.
demo:
$demo = ORM::for_table('demo')->select('field_test')->find_one($id);
$demo->field_test = 'do';
$demo->save();
You will get the error.
change to :
$demo = ORM::for_table('demo')->select('field_test')->select('id')->find_one($id);
It will fix the problem.
Some tips in documents:
https://github.com/j4mie/idiorm/blob/master/test/ORMTest.php
/**
* These next two tests are needed because if you have select()ed some fields,
* but not the primary key, then the primary key is not available for the
* update/delete query - see issue #203.
* We need to change the primary key here to something other than id
* becuase MockPDOStatement->fetch() always returns an id.
*/
I've never used idiorm, so cannot guarantee that my answer will work for you, but from this page and under "Updating records", we have an example which is similar but slightly different to yours.
// The 5 means the value of 5 in the primary-key column
$person = ORM::for_table('person')->find_one(5);
// The following two forms are equivalent
$person->set('name', 'Bob Smith');
$person->age = 20;
// This is equivalent to the above two assignments
$person->set(array(
'name' => 'Bob Smith',
'age' => 20
));
// Syncronise the object with the database
$person->save();
I'm sure I'll learn the reason behind this, but let me tell you all I understand at the moment, and how I "fixed" it.
Here is the beginning of idiorm's save function:
public function save() {
$query = array();
// remove any expression fields as they are already baked into the query
$values = array_values(array_diff_key($this->_dirty_fields, $this->_expr_fields));
if (!$this->_is_new) { // UPDATE
// If there are no dirty values, do nothing
if (empty($values) && empty($this->_expr_fields)) {
return true;
}
$query = $this->_build_update();
$id = $this->id(true);
Right there, on that last line, when trying to access the $this->id, you are getting an exception thrown:
throw new Exception('Primary key ID missing from row or is null');
$this does not contain an id property. I'm not really sure how it could. The example given both on their homepage and in the docs doesn't do anything special to address this. In fact I am copying them 1:1 and still yielding the same error as you.
So, all that said, I fixed this error by just adding in my own id:
$crop = ORM::for_table('SCS_Crop')->find_one($id);
$crop->id = $id;
$crop->Name = "Foo";
$crop->save();
This also happens when the id field name is ambiguous, e.g. when joining two tables both having an id column. This is the case with referenced tables
Model::factory('tableOne')
->left_outer_join('tableTwo', array('tableOne.tableTwo_id', '=', 'tableTwo.id'))
->find_one($id);
In these cases set an alias to the ID column of the parent tableOne to later access it while saving. Make sure that you also select other columns you need - e.g. by ->select('*'):
Model::factory('tableOne')
->select('*')
->select('tableOne.id', 'id')
->left_outer_join('tableTwo', array('tableOne.tableTwo_id', '=', 'tableTwo.id'))
->find_one($id);
if in table primary key/ field name not id then following id column overrides required
default id (primary_key) to replace with other id name (primary_key)
ORM::configure('id_column_overrides', array(
'user' => 'user_id',
));
$update = ORM::for_table('user')->find_one(1);
$update->name = "dev";
try{
$update->save();
}catch(Exception $e){
echo $e;
}
print_r($update);

Yii INSERT ... ON DUPLICATE UPDATE

I am working on a Yii project. How can I use the ON DUPLICATE feature of MySQL ( http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html ) when doing a save() on a Yii model?
My MySQL is as follows:
CREATE TABLE `ck_space_calendar_cache` (
`space_id` int(11) NOT NULL,
`day` date NOT NULL,
`available` tinyint(1) unsigned NOT NULL DEFAULT '0',
`price` decimal(12,2) DEFAULT NULL,
`offer` varchar(45) DEFAULT NULL,
`presale_date` date DEFAULT NULL,
`presale_price` decimal(12,2) DEFAULT NULL,
`value_x` int(11) DEFAULT NULL,
`value_y` int(11) DEFAULT NULL,
PRIMARY KEY (`space_id`,`day`),
KEY `space` (`space_id`),
CONSTRAINT `space` FOREIGN KEY (`space_id`) REFERENCES `ck_space` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
My PHP is a follows:
$cache = new SpaceCalendarCache();
$cache->attributes = $day; //Some array with attributes
$cache->save();
If there is a duplicate in my primary key (sapce_id,day), I don't want it to complain, I just want it to update with the latest data.
I know how to do it in raw SQL, I was just wondering if there is a clean Yii way to do it.
You are using models in Yii, its quite simple .. try to load you model where you suspect to have duplicate entries, if you find the entry the model is loaded else null is return. now if your model is null simply create new model. rest is your normal code to insert a new record.
//try to load model with available id i.e. unique key
$model = someModel::model()->findByPk($id);
//now check if the model is null
if(!$model) $model = new someModel();
//Apply you new changes
$model->attributes = $attributes;
//save
$model->save();
Refer to post controllers update method in sample app Yii blog. I might be wrong with spelling of function names, sorry for that.
I'm repeating two main points from previous answers I think you should keep:
Don't (try to) use "on duplicate key update" since its MySQL-only, as txyoji points out.
Prefer the select->if not found then insert->else insert demonstrated by Uday Sawant.
There's another point here, though: Concurrency. Although for low traffic applications the probability that you'll get in trouble is minimal (still never zero), I think we always be careful about this.
From a transactional point of view, "INSERT .. ON DUPLICATE UPDATE" is not equivalent to selecting into your application's memory and then inserting or updating. The first is a single transaction, then second is not.
Here's a bad scenario:
You do select your record using findByPk() which returns null
Some other transaction (from some other user request) inserts a record with the id you just failed to select
At the next instant you try to insert it again
In this case you'll either get an exception (if you're working with a unique key, as you do here) or a duplicate entry. Duplicate entries are much harder to pick up (usually nothing seems weird until your users see duplicate records).
The solution here is to set a strict isolation level, for example "serializable", and then begin a transaction.
Here's an example for yii:
Yii::app()->db->createCommand('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');
$trn = Yii::app()->db->beginTransaction();
try {
// Try to load model with available id i.e. unique key
// Since we're in serializable isolation level, even if
// the record does not exist the RDBMS will lock this key
// so nobody can insert it until you commit.
// The same shold for the (most usual) case of findByAttributes()
$model = someModel::model()->findByAttributes(array(
'sapce_id' => $sapceId,
'day' => $day
));
//now check if the model is null
if (!$model) {
$model = new someModel();
}
//Apply you new changes
$model->attributes = $attributes;
//save
$model->save();
// Commit changes
$trn->commit();
} catch (Exception $e) {
// Rollback transaction
$trn->rollback();
echo $e->getMessage();
}
You can see more about isolation levels at least in the following links and see what every isolation level has to offer in data integrity in exchange for concurrency
http://technet.microsoft.com/en-us/library/ms173763.aspx
http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
I overrode beforeValidate() where I checked if a duplicate exists. If one does, I set $this->setIsNewRecord(false);
Seems to work. Not sure how performant it is.
The "On Duplicate Key Update" feature is specific to MySQL's dialect of SQL. Its unlikely to be implemented in any data abstraction layer. ZendDB and Propel don't have an equivalent.
You can simulate the behavior by attempting an insert in a try/catch and update if insert fails with the proper error code. (duplicate key error).
I agree with #txyoji's analysis of the problem, but I would use a different solution.
You can extend the save() method of the model to look for an existing record, and update it, or insert a new row if it doesn't.
you have to use try catch like that:
try{
$model->save();
}
catch(CDbException $e){
$model->isNewRecord = false;
$model->save();
}
Ooops, sorry.. this answer for yii2
If you dont use yii model, this function generates mysql syntax insert on duplicates key update
static function insertDuplicate($table, $columns, $duplicates, $values="",$ignores=false){
$params=array();
$names=array();
$tipe="VALUES";
$ignore=($ignores===true)?"IGNORE":"";
$placeholders=array();
if(is_array($columns)){
if(!isset($columns[0])){
foreach($columns as $name=>$value)
{
$names[]=$name;
if($value instanceof CDbExpression)
{
$placeholders[] = $value->expression;
foreach($value->params as $n => $v)
$params[$n] = $v;
}
else
{
$placeholders[] = ':' . $name;
$params[':' . $name] = $value;
}
}
}else{
$names=$columns;
}
$myColumn=implode(', ',$names);
if($values!=""){
$myValue=$values;
}else{
$myValue='('.implode(', ', $placeholders).')';
}
}else{
$myColumn=$columns;
$myValue=$values;
}
if($values!=""){
if(substr(strtoupper($values),0,6)=="SELECT"){
$tipe="";
}
}
$d = array();
if(is_array($duplicates)){
if(!isset($duplicates[0])){
foreach($duplicates as $duplicate=>$act)
{
if($act=="increase"){
$dup=$table.".".$duplicate . ' = '.$table.".".$duplicate.' + VALUES('.$duplicate.')';
}elseif($act=="decrease"){
$dup=$table.".".$duplicate . ' = '.$table.".".$duplicate.' - VALUES('.$duplicate.')';
}else{
$dup=$table.".".$duplicate . ' = VALUES('.$duplicate.')';
}
$d[] = $dup;
}
}else{
foreach($duplicates as $duplicate){
$dup=$duplicate . ' = VALUES('.$duplicate.')';
$d[] = $dup;
}
}
$myDuplicate= implode(', ', $d);
}else{
$myDuplicate=$duplicates;
}
$sql='INSERT '.$ignore.' INTO ' . $table
. ' (' . $myColumn . ') '.$tipe.' '
. $myValue . ' ON DUPLICATE KEY UPDATE ' .$myDuplicate;
return Yii::$app->db->createCommand($sql)->bindValues($params)->execute();
}
Place that function into someclass, and dont forget use
use yii\db\Command;
in that class
That function can insert on key update, update increment, update decrement, update multi from a value, and update from select
Usage :
//to update available=1 and price into 100
someclass::insertDuplicate(
'ck_space_calendar_cache',
['sapce_id'=>1,'day'=>'2022-09-01','available'=>1,'price'=>100],
['available','price']
);
//to update price increase by 100, (if price is decrease then change it to decrease)
someclass::insertDuplicate(
'ck_space_calendar_cache',
['sapce_id'=>1,'day'=>'2022-09-01','price'=>100],
['price'=>'increase']
);
//to update mass with a value
someclass::insertDuplicate(
'ck_space_calendar_cache',
['sapce_id','day','price'],
['price'],
'(1,'2022-09-01',100),(2,'2022-09-01',300),(3,'2022-09-01',100)'
);
//to update mass with select from another table
someclass::insertDuplicate(
'ck_space_calendar_cache',
['sapce_id','day','price'],
['price'],
'SELECT otherid as sapce_id, otherday as day, otherprice as price from other WHERE otherprice>100'
);

Duplicate key error during upsert [Explanation]

I am executing the below statement in a class. This code is from
$query = array('_id' => $id, 'lock' => 0);
$update = array('$set' => array('lock' => 1));
$options = array('safe' => true, 'upsert' => true);
$result = $this->_mongo->update($query, $update, $options);
if ($result['ok'] == 1) {
return true;
}
However I do not understand how I would get a duplicate key error.
Can someone explain the possible scenarios and likelihood that I will receive this error?
I have been researching this extensively, cannot find my answer anywhere. So if it is on SO or any other website please share!
Thanks in advance.
Since you're doing an upsert and including _id in your query, you shouldn't be getting any duplicates on that key. This makes me think that you've created a unique index on lock, which isn't going to work for more than 2 documents because you only have 2 values for that field.
If you haven't put a unique index on lock, then you must have a unique index on a field you aren't showing here. That won't work either because on an insert, your upsert is going to set _id and lock only, any other field with an index will be inserted as null. If one of those fields has a unique index, then only a single document can have a null in that field. So when you try and insert another null for that field, you'll get a duplicate key error.

Categories