I've using this to create a new user in the WordPress database...
// Add user to WP users table.
$user_table_name = $wpdb->prefix . "users";
$unique_string = substr(md5(rand(0, 1000000)), 0, 10);
$wpdb->insert( $user_table_name, array(
'user_login' => sanitize_text_field($_POST['email']),
'user_pass' => sanitize_text_field(MD5($unique_string)),
'user_email' => sanitize_text_field($_POST['email']),
'user_registered' => sanitize_text_field(date("Y-n-d G:i:s")),
'user_status' => $_POST['1'],
'display_name' => sanitize_text_field($_POST['first_name']) . " " . sanitize_text_field($_POST['last_name'])
) );
...which works fine, and let's pretend that the ID of that user turned out to be 1234 in the database table (thanks to auto increment).
So now I also need to add the corresponding user meta information into the usermeta table for that user, and this is where I get a little confused.
The code above is easy because it's just adding a row to a table. But the usermeta table is different because it will need - in this case - a bunch of rows with the corresponding user_ID of 1234 each respectively with:
nickname (I'll use the email address for this)
wp_capabilities (value to be a:1:{s:10:"subscriber";b:1;})
sales (a custom field I have - value will be set to the word "yes")
colour (another custom field I have - value will be set to the word "green")
I'm guessing the SQL statement is going to be similar to the one at the start of this post.
If anyone could show me, that would be awesome.
UPDATE:
So this is mostly done. This works:
// Add corresponding user metadata to WP users table.
$usermeta_table_name = $wpdb->prefix . "usermeta";
$last_id = $wpdb->insert_id;
$role = sanitize_text_field('a:1:{s:10:"subscriber";b:1;}');
$wpdb->query(
$wpdb->prepare(
"
INSERT INTO $usermeta_table_name (
`umeta_id`,
`user_id`,
`meta_key`,
`meta_value` )
VALUES (
NULL,
$last_id,
$usermeta_table_name . 'capabilities',
'$role' )
",
$last_id, $last_id
)
);
That will add one row to the usermeta table, but how can I add 2 more rows within the same statement?
Store the last inserted row's ID to a variable:
$last_id = $wpdb->insert_id;
now, use this $last_id variable for metadata insertion. The insert_id variable is provided by the wpdb class.
OK so I feel a little silly about this - though to be fair it's been a long time since I wrote any SQL :-)
The answer is simply to follow this method:
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);
Related
I'm trying to find an answer which is the best practice for following parts of code:
Let's say I have a single action for add and edit products
DELETE & INSERT
$product_id = 1;
$this->db->query("DELETE FROM `product_description` WHERE `product_id` = :product_id");
$this->db->bind(array(":product_id" => $product_id));
$this->db->execute();
$updateset = array(
":product_id" => $product_id,
":name" => $_POST["name"]
);
$this->db->query("INSERT INTO `product_description` SET `product_id` = :product_id, `name` = :name");
$this->db->bind($updateset);
$this->db->execute();
UPDATE or INSERT
$product_id = 1;
$updateset = array(
":name" => $_POST["name"]
":product_id" => $product_id
);
$this->db->query("UPDATE `product_description` SET `name` = :name WHERE `product_id` = :product_id");
$this->db->bind($params);
$this->db->execute();
if(!$this->db->row_count()) {
$this->db->query("INSERT INTO `product_description` SET `product_id` = :product_id, `name` = :name");
$this->db->bind($updateset);
$this->db->execute();
}
so which is better?
Neither option is good.
Problem 1:
They both are susceptible to a race condition. Another concurrent session might insert a row in the brief time between your two SQL statements.
Problem 2:
In the second solution, if the row-count of the UPDATE is zero, there may still be a matching row, because it won't count rows when the update doesn't change the value in the row.
Problem 3:
If you use the first option, and you use foreign keys, it won't work if there are any child rows referencing the row you delete. Either the delete will be blocked because of the child rows, or else you use ON DELETE CASCADE which means you will accidentally delete the child rows.
Better solution:
Define a UNIQUE constraint for product_id. Your usage suggests that this column would be a candidate key (i.e. there can only be one row per product_id).
Then use INSERT...ON DUPLICATE KEY UPDATE.
$updateset = array(
"product_id" => $product_id,
"name" => $_POST["name"]
);
$this->db->query("INSERT INTO `product_description`
SET `product_id` = :product_id, `name` = :name
ON DUPLICATE KEY UPDATE `name` = VALUES(`name`)");
$this->db->bind($updateset);
$this->db->execute();
Tip: Your array doesn't need to prefix the column names with :. That was required in early versions of PDO, but they changed it so this hasn't been a requirement for years.
The question is quite unclear but the second option looks much better.
The main reason is if your schema evolve and you end up with more columns than you already have, the first option will deleted existing data.
The second option on the other hand will make sure that any existing data will be kept in your database.
I am trying to update a 'ticketHistory' table after every update of my 'tickets' table. I have the following trigger set up to run whenever my 'tickets' table is updated (usually when a field is edited):
TRIGGER `ticket_edit` AFTER UPDATE ON `tickets` FOR EACH ROW INSERT INTO ticketHistory
(ticketId, action, originator, assigned, date)
VALUES
(NEW.ticketId, "Edited", NEW.userId, NEW.assigned, NEW.lastUpdated);
Whenever an update is done in my app, the field being edited is updated, as well as the 'lastUpdated' time. I think because two fields are being updated, it is running the trigger twice, resulting in two identical rows being inserted into my 'ticketHistory' table. I only want one row inserted.
Is there a way to limit it to just one row being inserted? Any help is greatly appreciated!
EDIT
All database inserts and updates are being done using CodeIgniter's ActiveRecord.
My model:
public function editTicket($ticket)
{
$q = $this->db->where('ticketId', $ticket['ticketId']);
$q = $this->db->update('tickets', $ticket);
$results = $this->db->affected_rows();
}
My Controller:
public function edit_ticket()
{
$ticketId = $this->uri->segment(3);
$description = str_replace("\n", "\r", $this->input->post('description'));
$ticket = array(
'ticketId' => $ticketId,
'headline' => $this->input->post('headline'),
'description' => $description,
'priority' => $this->input->post('priority'),
'category' => $this->input->post('category'),
'lastUpdated' => date("Y-m-d H:i:s", time())
);
$this->tickets->editTicket($ticket);
}
public function editTicket($ticket)
{
$q = $this->db->where('ticketId', $ticket['ticketId']);
echo "I am here again<br/>";
$q = $this->db->update('tickets', $ticket);
$results = $this->db->affected_rows();
}
I'm not sure why that's adding 2 different rows, but you could just make a primary key field and your DBMS wouldn't allow the duplicate.
Hope this helps!
So, as a solution, I modified my trigger as such:
TRIGGER `ticket_edit` AFTER UPDATE ON `tickets`
FOR EACH ROW
IF NEW.headline <> OLD.headline || NEW.description <> OLD.description || NEW.priority <> OLD.priority
THEN
INSERT INTO ticketHistory
(ticketId, action, originator, assigned, date)
VALUES
(NEW.ticketId, "Edited", NEW.userId, NEW.assigned, NEW.lastUpdated);
END IF
By adding the conditionals, I think it only activated the trigger once, resulting in only one row being inserted.
Hope this helps someone!
I am using jQuery draggable, when the draggable element moves I save those css values (left, top) into the database ($wpdb) using ajax... I have no problem when there is one draggable element it runs smooth... but adding onto the project I want to add multiple draggable elements and thats where I am having a problem... here is the code I am sending the data to the database...
global $wpdb;
//The data here comes from an ajax call
$_POST['x'];
$_POST['y'];
$_POST['id'];
$term_id = 100;
$name = $_POST['id'];
$slug = $_POST['x'];
$term_group = $_POST['y'];
if(isset($_POST['id'],$_POST['y'], $_POST['x'])){
print_r($_POST);
}
//Im adding into wp_terms a default table (for testing)...
$query = "INSERT INTO $wpdb->terms (term_id, name, slug, term_group) VALUES (%s, %s, %s, %s)";
//Here I insert the ajax data into the columns
$wpdb->query($wpdb->prepare($query, $term_id, $name, $slug, $term_group));
$wpdb->update("$wpdb->terms", array('slug' => $_POST['x'], 'term_group' => $_POST['y']), array('name' => $_POST['id']));
die();
This code summed up: It connects to the $wpdb then I take the data from the ajax call and store them in a variable.. I am targeting the wp_terms table... So on draggable element stop the ajax data is sent to my script and stored in the database, on update every time draggable stops I preserve that row and only update the x and y values located in slug && term_group that has the name of $_POST['id']... this updates it well, but since $term_id = 100 a static value, I cannot create a new row for an element with a new $_POST['id'].. does that make sense.. its much more simple than I think I made it out to be... I am trying to do what I am already doing but when there is a new $_POST['id'] I need to generate a $term_id = 101 dynamically it works exactally how I want if I manually give the row a term_id of 101...
Table looks like this...
Im trying to add a 101 and silly2.. then 102 silly3.. something like that, then when the last two values x & y change the 152 & 32 changes but term_id 101 stays and silly 2 stays...
The thing here is I dont want to create a new instance of silly, silly2, silly3... I want to overwrite the last two columns..
If you are running a standard Wordpress installation, the wp_terms table always has auto_increment as default for term_id, so you can just drop the static $term_id, the query now should be:
$query = "INSERT INTO $wpdb->terms (name, slug, term_group) VALUES (%s, %s, %s)";
$wpdb->query($wpdb->prepare($query, $name, $slug, $term_group));
Hope this helps.
EDIT:
Your question now is quite different than the first one. First you have to do a SELECT query to see if the row with that name exist or not, something like:
$query = "SELECT FROM $wpdb->terms WHERE name=%s"; // remember to use prepare() after this
If that row doesn't exist then you can do the INSERT like above, if that row exists then you should do an UPDATE query:
$query = "UPDATE $wpdb->terms SET slug=%s, term_group=%s WHERE name=%s";
There is also INSERT ... ON DUPLICATE KEY UPDATE solution to replace above solution but I'm not so experienced with that.
Try using auto increment in sql table definition. If you cant modify the table definition you can add one more table and link the data with it.
Edit to answer changed question.
You need to check does the value exist in the table before inserting new row.
If it exist do an update SQL query on that row. Otherwise insert new row.
I'm writing a plugin and trying to insert a new row into the wp_term_relationships table inside of a foreach loop. I know the variables have values because of a var_dump, but for some reason, I'm getting an error consistently. This shows up about 600 times on the show_errors() function:
WordPress database error: [Duplicate entry '0-0' for key 1] INSERT
INTO wp_term_relationships
(object_id,term_taxonomy_id,term_order) VALUES ('','','')
My Code:
foreach ($cb_t2c_cat_check as $values) {
global $wpdb;
$prefix = $wpdb->prefix;
$table = $prefix . 'term_relationships';
$object_id = $values->object_id;
$taxo_id = $values->term_taxonomy_id;
$num_object_id = (int)$object_id;
$num_taxo_id = (int)$taxo_id;
//var_dump($num_object_id); //This produces values, so why are they not getting inserted into the table?
//var_dump($num_taxo_id); //This produces values, so why are they not getting inserted into the table?
$wpdb->insert(
$table,
array(
'object_id' => $num_object_id,
'term_taxonomy_id' => $num_taxo_id,
'term_order' => 0
), ''
);
//$wpdb->show_errors();
//$wpdb->print_error();
}
As for why it does not work: do not set third parameter of $wpdb->insert to empty string. It formats every field accordingly..
What it does now is equivalent to:
$wpdb->insert($table, array(
'object_id' => sprintf('', $num_object_id),
'term_taxonomy_id' => sprintf('', $num_taxo_id),
'term_order' => sprintf('', 0)
));
If you really want to set third parameter you should do:
$wpdb->insert($table, array(
'object_id' => $num_object_id,
'term_taxonomy_id' => $num_taxo_id,
'term_order' => 0
), array('%d', '%d', '%d'));
As for error: wp_term_relationships table has a unique primary key on (object_id, term_taxonomy_id). This means that you cannot have two rows in that table which have both same object_id and term_taxonomy_id.
Though this has happened because by setting third parameter of insert to empty string, you are trying to insert rows with object_id=0 and term_taxonomy_id=0 over and over again.
The answer above was correct in that the database needs to have unique keys and cannot insert a row where the key-value pair already exists, and the format of each new value needs to be set. In addition, specific to Wordpress, there was a problem I wasn't addressing, specifically dealing with the term_taxonomy table and updating the count.
First it's important to note that the plugin was designed to update certain categories for posts in the term_relationships table. This was actually accomplished using the $wpdb-> insert method. However, my test for determining whether the plugin actually inserted new rows in the term_relationships table was not to look at the table directly, but to go to the Wordpress dashboard, select categories, and see if the number of posts with that category was more than before. This didn't work, because the plugin never updated the count in the term_taxonomy table. I only discovered this by clicking 'view' next to a category in the Wordpress dashboard and seeing that there were multiple posts with that category, even though the official Wordpress "count" said there were none.
I confirmed that the term_taxonomy table, the 'count' column, needed to be updated as well by going straight to the database and putting WHERE = 'term_taxonomy_id' in the statement. Sure enough, there were over 1700 results, even though Wordpress thought there were none.
Lesson: Confirm the $wpdb->insert method is working by using PHPMyAdmin, not necessarily relying on the Wordpress dashboard.
With a few modifications, the code now works great. Here's an example:
foreach ($cb_t2c_objects as $values) {
global $wpdb;
$prefix = $wpdb->prefix;
$table = $prefix . 'term_relationships';
$object_id = $values->object_id;
$taxo_id = $values->cat_taxo;
$num_object_id = (int)$object_id;
$num_taxo_id = (int)$taxo_id;
//Need to check to see if row exists for each, if not, then insert.
$cb_t2c_get_row = $wpdb->get_row("
SELECT *
FROM ".$prefix."term_relationships
WHERE object_id = ".$num_object_id." AND term_taxonomy_id = ".$num_taxo_id."
GROUP BY object_id, term_taxonomy_id
", OBJECT);
//var_dump($cb_t2c_get_row);
if ( is_null($cb_t2c_get_row) ) {
//Insert the new values.
$wpdb->insert(
$table,
array(
'object_id' => $num_object_id,
'term_taxonomy_id' => $num_taxo_id,
'term_order' => 0
),
array(
'%d',
'%d',
'%d'
)
);
}
//Set the variables for the count update.
$cb_t2c_term_taxonomy_table = $prefix . 'term_taxonomy';
$cb_t2c_update_data = $wpdb->get_var("
SELECT count(term_taxonomy_id) as 'new_count'
FROM ".$prefix."term_relationships
WHERE term_taxonomy_id = ".$num_taxo_id."
",0,0); //returning NULL
//var_dump($cb_t2c_update_data);
//Update the count in the term_taxonomy table.
$wpdb->query("
UPDATE ".$prefix."term_taxonomy
SET count = ".$cb_t2c_update_data."
WHERE term_taxonomy_id = ".$num_taxo_id."
");
I'm building an application where users can add, edit and delete users from the database.
I've done the 'View Users' ( Select statement returning joined tables ) and the 'Add Users' ( Returns a form for user to fill in and INSERTS into database after validation of course )
But I'm having trouble with the 'Edit Users'.
My Edit form is the SAME as the add form but the values for the text boxes are filled in with values from the database.
Upon submit, the logic checks for mismatches between the user input and the database values (So we know what we're actually updating in the database)
So I end up with an array of all the values that need to be changed, example:
$values_to_update = array (
"telephone" => "07788991010"
"email_address" => "my_new_email_address#host.com"
);
The values in this array are dynamic and I need to thing of a way to dynamically update each field.
Also, the fields may also come from different tables.
I DON'T want to do:
if ( isset ( $values_to_update[ "telephone" ] ) )
$database->update("UPDATE users SET telephone = '{$values_to_update[ "telephone" ]}' WHERE user_id = $user_id");
else if ( isset ( $values_to_update[ "email_address" ] ) )
$database->update("UPDATE authentication SET email_address = '{$values_to_update[ "email_address" ]}' WHERE user_id = $user_id");
else if ( /* ... */)
// etc etc etc
Does anybody have any ideas of a better way I could do this?
I was thinking maybe I could use one HUGE update statement uses the same select statment that fetches the data. but i dont know how this would work.
Is it a standard practise for applications to store an array of fields to tables for dynamic query generation?
A clean way I've found of doing this in PDO-style ORM mappers (which is also used in Zend Framework):
public function save($model) {
// Possible values to insert
$values = array(
":id" => $model->id,
":name" => $model->name,
//...
":contact_number" => $model->telephone
);
// If model id is set, we are updating a record, otherwise inserting
if (isset($model->id)) {
$sql = "UPDATE tbl SET name = :name, ..., contact_number = :contact_number WHERE id = :id LIMIT 1";
} else {
// Don't pass the ID when it isn't in the query
unset($values[":id"]);
$sql = "INSERT INTO tbl (name, ..., contact_number) VALUES (:name, ..., :contact_number)";
}
// Execute the query with our values (below is a PDO example)
$stmt = $this->db->prepare($sql);
$stmt->execute($values);
$stmt->closeCursor();
// Get the new ID if we inserted
if (!isset($model->id)) {
$model->id = $this->db->lastInsertId();
}
return $model;
}
Usually this amounts to writing a single save method for each model. You could extend this in UPDATE situations by keeping a copy of the originally-retrieved model, so that you only update the columns that have changed, however I don't see any huge overhead by updating all columns.