would like to update attached_blog_id field to set NULL. Thank you in advance.
foreach($this->Event->find('all', array('conditions' => array('Event.attached_blog_id' => $this->id()))) as $attached_blog)
$this->Event->query('UPDATE armo8427_events' . ' SET attached_blog_id = NULL' . ' WHERE id = ' . $attached_blog['Event']['id']);
Don't use query unnecessarily
The code in the question would be better written as:
$events = $this->Event->find(
'all',
array(
'conditions' => array(
'Event.attached_blog_id' => $this->id()
)
)
);
foreach($events as $event) {
$this->Event->id = $event['Event']['id'];
$this->Event->saveField('attached_blog_id', null);
}
With the code-logic in the question there is no need to use query at all
But be efficient
The logic in the question can be expressed as a single sql query, instead of 1+n:
UPDATE
events
SET
attached_blog_id = NULL
WHERE
attached_blog_id = $id;
I.e. if there were 100 linked blogs, using the foreach loop will issue 101 queries, wheras this is the same in one query irrespective of the number rof affected rows.
The most appropriate way to do that in CakePHP is to use updateAll:
$id = $this->id(); // From the question
$this->Event->updateAll(
array('Baker.attached_blog_id' => null), // the update
array('Baker.attached_blog_id' => $id) // conditions to match
);
the method query should be reserved for sql calls which are not possible to achieve using the provided model methods.
Related
I'm trying to insert an array of data into a table in database but an error said Array to string conversion error
This is the post function in my controller, first i post an array of data. The values of the array will be the names, and numbers, they are not id. The id is only kodejdwl. This will be pass to my model
function index_post() {
$data = array(
'kodejdwl' => $this->post('kodejdwl'),
'tahun_akad' => $this->post('kode_tahun_akad'),
'semester' => $this->post('semester'),
'mk' => $this->post('mk'),
'ruangan' => $this->post('ruangan'),
'nama_dosen' => $this->post('nama_dosen'),
'namakelas' => $this->post('nama_kelas'),
'jam_mulai' => $this->post('jam_mulai'),
'jam_selesai' => $this->post('jam_selesai'),
);
}
After the data from above code is passed to the model. I created some new variables which are the id of each the name of the value in the array data. e.g if the value of data['mk'] is Website then the id will be 1 and that id will be stored in variable $kodemk and i do it to each value in the data. Then i created new_data which stores array of the id's which i previously made. Then i insert that array into one table in my database. I thought it would be fine but it said Array to string conversion error. What should i do so i could insert that array into the table in my database?
public function insert($data){
$this->db->select('thn_akad_id');
$tahunakad_id = $this->db->get_where('tik.thn_akad',array('tahun_akad'=>$data['tahun_akad'],'semester_semester_nm'=>$data['semester']))->result();
$this->db->flush_cache();
$this->db->select('kodemk');
$kode_mk = $this->db->get_where('tik.matakuliah',array('namamk'=>$data['mk']))->result();
$this->db->flush_cache();
$ruangan = $this->db->get_where('tik.ruangan', array('namaruang' => $data['ruangan']), 1)->result();
$this->db->flush_cache();
$this->db->select('nip');
$nip_dosen = $this->db->get_where('tik.staff',array('nama'=>$data['nama_dosen']))->result();
$this->db->flush_cache();
$this->db->select('kodeklas');
$kodeklas = $this->db->get_where('tik.kelas',array('namaklas'=>$data['namakelas']))->result();
$this->db->flush_cache();
$this->db->select('kode_jam');
$kode_mk = $this->db->get_where('tik.wkt_kuliah',array('jam_mulai'=>$data['jam_mulai'],'jam_selesai'=>$data['jam_selesai']))->result();
$this->db->flush_cache();
$new_data = array(
'kodejdwl' => $data['kodejdwl'],
'thn_akad_thn_akad_id' => $tahunakad_id,
'matakuliah_kodemk' => $kode_mk,
'ruangan_namaruang' => $ruangan,
'staff_nip' => $nip_dosen,
'kelas_kodeklas' => $kodeklas,
);
$insert = $this->db->insert('tik.jadwal_kul', $new_data);
return $this->db->affected_rows();
}
You probably want to use row() instead of result() because it'll contain only one result that you want. If you want to use result() and store multiple values then you'll have to use implode to concatenate them and store it as a string.
I've written a possible solution for your problem; Some things were missing, so I've mentioned them in the comments. See if this helps you.
public function insert($data){
$this->db->select('thn_akad_id');
$tahunakad_id = $this->db->get_where('tik.thn_akad',array('tahun_akad'=>$data['tahun_akad'],'semester_semester_nm'=>$data['semester']))->row(); // use row here
$this->db->flush_cache();
$this->db->select('kodemk');
$kode_mk = $this->db->get_where('tik.matakuliah',array('namamk'=>$data['mk']))->row();
$this->db->flush_cache();
// remove your_ruangan_column with your desired column name
$this->db->select('your_ruangan_column');
$ruangan = $this->db->get_where('tik.ruangan', array('namaruang' => $data['ruangan']), 1)->row();
$this->db->flush_cache();
$this->db->select('nip');
$nip_dosen = $this->db->get_where('tik.staff',array('nama'=>$data['nama_dosen']))->row();
$this->db->flush_cache();
$this->db->select('kodeklas');
$kodeklas = $this->db->get_where('tik.kelas',array('namaklas'=>$data['namakelas']))->row();
$this->db->flush_cache();
// Not sure where this ↓↓ is being used but you can use it the same way as others
$this->db->select('kode_jam');
// duplicate variable name here ↓↓ (fix this)
$kode_mk = $this->db->get_where('tik.wkt_kuliah',array('jam_mulai'=>$data['jam_mulai'],'jam_selesai'=>$data['jam_selesai']))->row();
$this->db->flush_cache();
$new_data = array(
'kodejdwl' => $data['kodejdwl'],
'thn_akad_thn_akad_id' => $tahunakad_id->thn_akad_id, // {$tahunakad_id} consists an object with the key {thn_akad_id}-- table_column_name
'matakuliah_kodemk' => $kode_mk->kodemk, // ...
'ruangan_namaruang' => $ruangan->your_ruangan_column, // ...
'staff_nip' => $nip_dosen->nip, // ...
'kelas_kodeklas' => $kodeklas->kodeklas // ...
);
$insert = $this->db->insert('tik.jadwal_kul', $new_data);
return $this->db->affected_rows();
}
Your are making a total of 7 separate trips to the database. Best practice recommends that you always minimize your trips to the database for best performance. The truth is that your task can be performed in a single trip to the database so long as you set up the correct INSERT query with SELECT subqueries.
I don't know what your non-English words are, so I will use generalized terms in my demo (I've tested this successfully in my own CI project). I am also going to reduce the total subqueries to 3 to reduce the redundance in my snippet.
$value1 = $this->db->select('columnA')->where('cond1', $val1)->get_compiled_select('childTableA');
$value2 = $this->db->select('columnB')->where('cond2', $val2)->get_compiled_select('childTableB');
$value3 = $this->db->select('columnC')->where('cond3', $val3)->get_compiled_select('childTableC');
return (int)$this->$db->query(
"INSERT INTO parentTable
(column1, column2, column1)
VALUES (
($value1),
($value2),
($value3)
)"
);
// to mirror your affected rows return... 1 will be returned on successful insert, or 0 on failure
Granted this isn't using the ActiveRecord technique to form the complete INSERT query, but this is because CI doesn't allow subqueries in the VALUES portion (say, if you were to use the set() method). I am guessing this is because different databases use differing syntax to form these kinds of INSERTs -- I don't know.
The bottom line is, so long as you are fetching a single column value from a single row on each of these sub-SELECTs, this single query will run faster and with far less code bloat than running N number of individual queries. Because all of the variables involved are injected into the sql string using get_compiled_select() the stability/security integrity should be the same.
I have a draggable div in which the position is being saved to database with user_id set to unique. How would I check if user_id exists.. and if it does, update the other 3 columns.. If it does not exist, insert new row. I have read to use "ON DUPLICATE KEY UPDATE", but having a hard time implementing it. Any thoughts?
As explained by #N.B. - Working solution
global $wpdb;
$_POST['user_id'];
$_POST['div_id'];
$_POST['x_pos'];
$_POST['y_pos'];
$user_id = $_POST['user_id'];
$div_id = $_POST['div_id'];
$x_pos = $_POST['x_pos'];
$y_pos = $_POST['y_pos'];
$wpdb->query($wpdb->prepare(" INSERT INTO coords
(user_id, div_id, x_pos, y_pos)
VALUES (%d, %s, %d, %d)
ON DUPLICATE KEY UPDATE
x_pos = VALUES(x_pos), y_pos = VALUES(y_pos)",
$user_id,
$div_id,
$x_pos,
$y_pos
));
As #N.B. pointed out in the comments, while the first method I submitted works, it is open to race conditions. I'd like to thank him for pointing that out. Here is that first solution with race conditions:
$user_exists = $wpdb->get_var(
$wpdb->prepare("SELECT user_id FROM coords WHERE user_id = %d", $user_id)
);
if($user_exists) {
// Do Update
}
else {
// Do Insert
}
These race conditions are astronomical, as an insert must finish executing in the time between the first query returning and the next insert query. But the race condition exists none-the-less, and therefore could happen at some point in time.
However your database is still safe. When it occurs it won't duplicate the data, but rather it will throw a wpdb error that a unique key already exists and the insert will silently fail (it won't terminate the script and it won't output the error unless error reporting is turned on).
WordPress database error: [Duplicate entry '3' for key 'PRIMARY']
Amazingly, the above technique is used in the Wordpress core and by countless plugin and theme authors, and I could only find two instances of 'ON DUPLICATE' being used correctly in the Wordpress core. So a large chunk of the internet runs with multiple instances of this race condition seemingly just fine, just to give you an idea of the astronomical chance we're talking about.
Regardless of the chance, to use it is bad practice. As N.B. commented, the database should worry about the data and not PHP.
The Real Solution
Wordpress, for whatever reason, does not have an 'INSERT ON DUPLICATE UPDATE' function, which means you have to either write up a query each time with $wpdb->query or build your own function to handle it. I went with writing a function because writing wpdb->query each time is a pain and brings the user one layer closer to accidental mysql injection. Also development speed.
/**
* Insert on Duplicate Key Update.
*
* Wordpress does not have an 'insert on duplicate key update' function, which
* forces user's to create their own or write standard queries. As writing
* queries is annoying and open to mysql injection via human error, this function
* automates this custom query in an indentical fashion to the core wpdb functions.
* Source: http://stackoverflow.com/a/31150317/4248167
*
* #global wpdb $wpdb
* #param string $table The table you wish to update.
* #param array $data The row you wish to update or insert, using a field => value associative array.
* #param array $where The unique keys that you want to update at or have inserted, also a field => value array.
* #param array $data_formats Wordpress formatting array for the data. Will default to %s for every field.
* #param array $where_formats Wordpress formatting array for the where. Will default to %s for every field.
* #return boolean True if successfully inserted or updated the row.
*/
function insertOrUpdate($table, $data, $where, $data_formats = array(), $where_formats = array())
{
if(!empty($data) && !empty($where)) {
global $wpdb;
// Data Formats - making sure they match up with the data.
$default_data_format = (isset($data_formats[0])) ? $data_formats[0] : '%s';
$data_formats = array_pad($data_formats, count($data), $default_data_format);
$data_formats = array_splice($data_formats, 0, count($data));
// Where Formats - making sure they match up with the where data.
$default_where_format = (isset($where_formats[0])) ? $where_formats[0] : '%s';
$where_formats = array_pad($where_formats, count($where), $default_where_format);
$where_formats = array_splice($where_formats, 0, count($where));
// Get Fields
$data_fields = array_keys($data);
$where_fields = array_keys($where);
// Create Query
$query =
"INSERT INTO $table" .
" (" . implode(', ', array_merge($data_fields, $where_fields)) . ")" .
" VALUES(" . implode(', ', array_merge($data_formats, $where_formats)) . ")" .
" ON DUPLICATE KEY UPDATE";
// Compile update fields and add to query
$field_strings = array();
foreach($data_fields as $index => $data_field) {
$field_strings[] = " $data_field = " . $data_formats[$index];
}
$query .= implode(', ', $field_strings);
// Put it all together - returns true on successful update or insert
// and false on failure or if the row already matches the data.
return !!$wpdb->query(
$wpdb->prepare(
$query,
array_merge(
array_merge(
array_values($data),
array_values($where)
),
array_values($data)
)
)
);
}
return false;
}
To use it, you simply enter parameters just like you would with a $wpdb->update function call.
insertOrUpdate(
'testing_table',
array('column_a' => 'hello', 'column_b' => 'world'),
array('id' => 3),
array('%s', '%s'),
array('%d')
);
In your case, it would be:
insertOrUpdate(
'coords',
array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos),
array('user_id' => $user_id),
array('%d', '%d', '%d'),
array('%d')
);
Or you can just use a default formatting:
insertOrUpdate(
'coords',
array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos),
array('user_id' => $user_id),
array('%d')
);
Or you can ignore the formatting which will default to formatting as strings:
insertOrUpdate(
'coords',
array('div_id' => $div_id, 'x_pos' => $x_pos, 'y_pos' => $y_pos),
array('user_id' => $user_id)
);
If you find any issues just let me know.
This is my function to insert new rows to a table, depending on the date. I want to avoid duplication of rows if one already exists. This function basically inserts November, 2014 as mwf_month, so mwf_student_id and mwf_month pairs are unique for the row. What modification should I do to avoid this kind of duplication?
public function month_wise_due($grade_due, $new_due, $id, $remaining) {
$now1 = time();
$now = date('F, Y', $now1);
$store = array(
'mwf_month' => $now,
'mwf_previous' => $remaining,
'mwf_due' => $grade_due,
'total_due' => $new_due,
'mwf_student_id' => $id,
'mwf_pay_day' => 'Not Yet Paid',
'mwf_payment' => 0,
'mwf_diff' => $new_due
);
$this->db->trans_start();
$this->db->insert('mwf', $store);
$this->db->trans_complete();
}
The right way is to update your database table by adding "unique key" on two fields "mwf_month+mwf_student_id". The SQL command to do it is:
ALTER TABLE `mwf` ADD UNIQUE `unique_month_student`(mwf_month,mwf_student_id);
Then, possible duplicity would end with SQL error you can catch. You can also suppress the exception with 'ignore' statement or use 'replace' method instead of 'insert'.
If you don't have needed privileges on the table, you would need to simply check whether the pair exist with separate sql call before inserting new record.
I have found a solution from some where else without altering the table, it seems good to me.
$now1 = time();
$now = date('F, Y', $now1);
$row_exists = "SELECT * FROM mwf WHERE mwf_month = '" . $now."' AND mwf_student_id = '" . $id."' LIMIT 1";
if ( ! $this->db->simple_query($row_exists)) {
$store = array(
'mwf_month' => $now,
'mwf_previous' => $remaining,
'mwf_due' => $grade_due,
'total_due' => $new_due,
'mwf_student_id' => $id,
'mwf_pay_day' => 'Not Yet Paid',
'mwf_payment' => 0,
'mwf_diff' => $new_due
);
$this->db->trans_start();
$this->db->insert('mwf', $store);
$this->db->trans_complete();
}
Ok, so i'm in a pickle trying to figure a way for how to do this :
I have a live search(using ajax) which allows the user to select a criteria from a dropdown list and then enter a value which will be matched to values inside the database. This is quite trivial stuff, and on direct fields of the main model, i don't have any issues.
I have a Donor Model/table which consists of attributes such as ID, Name, Surname etc, but more importantly it also has FK of other associated models such as blood_group_id, donor_type_id, which map back to the respective models (BloodGroup and DonorType).. These two are already set with the associations and I am beyond that part, as I am already retrieving Donor records with associated model data.
Here is the search method which will hopefully help you in understanding my problem better.
public function search() {
if($this->request->is('post')){
if(!empty($this->request->data)){
$criteria = $this->request->data['criteria'];
$query = $this->request->data['query'];
$conditions = array("Donor." .$criteria. " LIKE '". $query . "%'");
The above checks if its a post request and whether data was sent. The criteria and user input are used to construct the query..
This is where my problem arises.. (When the user select search By blood type, as a criteria from the drop down)the above expects the user to enter the id of the blood_group rather than A+ or A- for instance.. So if the input is 1(id of blood group A+), the results are returned as expected. But I want the user to be able to enter A+...
Here is the rest of the method :
$this->Paginator->settings = array(
'conditions' => $conditions,
'limit' => 2
);
$donors = $this->Paginator->paginate('Donor');
$this->set('donors', $donors);
$this->beforeRender();
$this->layout= 'ajax';
}
}
}
I have tried this approach, setting up the conditions using the Model's name such as
if($criteria == 'blood_group_id'){
$conditions = array("BloodGroup.id" . " LIKE '". $query . "%'");
}elseif($criteria == 'donor_type_id'){
$conditions = array("DonorType.id" . " LIKE '". $query . "%'");
}else{
$this->Paginator->settings = array(
'conditions' => $conditions,
'limit' => 2
);
}
But this returns all the records irrespective of the input.
I also tried changing the settings for the paginator with no luck
$settings = array(
'joins' => array(
'table' => 'blood_groups',
'alias' => 'BloodGroup',
'type' => 'LEFT',
'conditions' => array(
"BloodGroup.id" => "Donor.blood_group_id",
"AND" => $conditions
)
),
'limit'=> 2
);
Any help on how to accomplish what I explained above, would greatly be appreciated!
simply:
if($criteria == 'blood_group_id')
$conditions = array("BloodGroup.name LIKE" => $query.'%');
(assuming bood_types has a 'name' column)
Also let me suggest you to use the CakeDC search plugin (https://github.com/CakeDC/search).
I have a very complex setup on my tables and achieving this via any of the find() methods is not an option for me, since I would need to fix relationships between my tables and I don't have the time right now, so I'm looking for a simple fix here.
All I want to achieve is run a query like this:
SELECT MAX( id ) as max FROM MyTable WHERE another_field_id = $another_field_id
Then, I need to assign that single id to a variable for later use.
The way I have it now it returns something like [{{max: 16}}], I'm aware I may be able to do some PHP on this result set to get the single value I need, but I was hoping there was already a way to do this on CakePHP.
Assuming you have a model for your table and your are using CakePHP 2.x, do:
$result = $this->MyTable->field('id', array('1=1'), 'id DESC');
This will return a single value.
see Model::field()
This example is directly from the CakePHP documentation. it seems you can use the find method of a model to get count
$total = $this->Article->find('count');
$pending = $this->Article->find('count', array(
'conditions' => array('Article.status' => 'pending')
));
$authors = $this->Article->User->find('count');
$publishedAuthors = $this->Article->find('count', array(
'fields' => 'DISTINCT Article.user_id',
'conditions' => array('Article.status !=' => 'pending')
));