Get ID of multiple inserts in Codeigniter - mysql

I'm in the process of learning CI for myself and this came up. If I run multiple INSERTs, whether as transaction or not, is it possible to get the insert ID of each (in the proper order) instead of running $this->db->insert_id() one at a time for each INSERT?
For example
-- Get ID of each
INSERT INTO table VALUES (insert1), (insert2), (insert3)
-- What if they're called separately
INSERT INTO table VALUES (insert1)
INSERT INTO table VALUES (insert2)
INSERT INTO table VALUES (insert3)
Is it still possible to get the ID as an array from all this?

I think you really must insert it between each inserts as far as i know, just store it in an array.
var $insert_id = array();
public function insert()
{
INSERT INTO table VALUES (insert1)
$this->insert_id =$this->db->insert_id();
INSERT INTO table VALUES (insert2)
$this->insert_id =$this->db->insert_id();
INSERT INTO table VALUES (insert3)
$this->insert_id =$this->db->insert_id();
}
everytime a database record is inserted you can still use insert_id(); after each insert then store it to an array. then you coudl make a function to return it.
public function get_inserted_keys()
{
return $insert_id;
}

It is simple
Controller
$ids = array();
$data[0] = array(blah,blah);
$data[1] = array(blah,blah);
$data[2] = array(blah,blah);
for($i=0;$i<3;$i++)
{
$ids[] = $this->my_model->insert($data[$i]);
}
echo '<pre>';
print_r($ids);
Model
public function insert($data)
{
$this->db->insert('table_name',$data);
return $this->db->insert_id()
}

Related

How to catch fails in pdo batch insert

I have this code to insert data from a CSV file to the database. Since, the CSV might have thousands of records, I am trying to implement a batch insert as follows.
$this->_connection->beginTransaction();
$sql = "INSERT INTO dbtable (col1,col2) VALUES (:value1,:value2)";
$stmt = $this->_connection->prepare($sql);
foreach ($requestArray['csv'] as $data) {
$stmt->bindParam(':value1', $data['csvCol1']);
$stmt->bindParam(':value2', $data['csvCol2']);
$stmt->execute();
}
$this->_connection->commit();
The variable $requestArray['csv'], holds all the record of the CSV post request. This code seems to be working as it should. Though, I am trying to improve it, because I want to let the user know how many records failed to insert on the database and if it is possible to show which records failed. Assume a duplicate key, or invalid data or generally any error that might come from this procedure.
PDOStatement::execute() will return false on failure:
$this->_connection->beginTransaction();
$sql = "INSERT INTO dbtable (col1,col2) VALUES (:value1,:value2)";
$stmt = $this->_connection->prepare($sql);
foreach ($requestArray['csv'] as $data) {
$stmt->bindParam(':value1', $data['csvCol1']);
$stmt->bindParam(':value2', $data['csvCol2']);
if (!$stmt->execute()) {
// error processing goes here.
}
}
$this->_connection->commit();

Delete from two mysql tables at once using join codeigniter

I am trying to delete from two mysql tables at once using this function.
public function deleteBusiness($id)
{
$this->db->from("business");
$this->db->join("opening_hours", "business");
$this->db->where("business", $id);
return $this->db->delete(array("business","opening_hours"));
}
It appears to be working correctly, as the correct data is being deleted from both tables, but when I call it with the following...
if($this->businesses_model->deleteBusiness($id)){
$this->session->set_flashdata('flash_message', 'Business Deleted');
} else {
$this->session->set_flashdata('flash_message', 'There was a problem deleting that business');
}
the failed error message is being displayed.
Replace Above code with Following Code let me know if does not work
public function deleteBusiness($id)
{
$this->db->from("business");
$this->db->join("opening_hours", "business");
$this->db->where("business", $id);
$this->db->delete(array("business","opening_hours"));
return $this->db->affected_rows();
}
if($this->businesses_model->deleteBusiness($id) > 0){
$this->session->set_flashdata('flash_message', 'Business Deleted');
} else {
$this->session->set_flashdata('flash_message', 'There was a problem deleting that business');
}
let me know if it does not work
It looks like you are trying to Delete a row from business table and all the rows in the opening_hours related to that business.
Unfortunately your code will not work as CodeIgniter ignores joins when doing Active Record deletes.
Their Documentation does says that you can pass an array to delete method and it will delete from multiple rows
$tables = array('table1', 'table2', 'table3');
$this->db->where('id', '5');
$this->db->delete($tables);
However, please see that it will delete rows from 3 tables where id = 5. Same way in your case it will delete from business and opening_hours where id is say 2. Obviously this is not what you want.
Only option is to use either 2 queries like below or use a Raw SQL query.
$this->db->from("business");
$this->db->where("id", $id);
$this->db->delete('business');
$this->db->from("opening_hours");
$this->db->where("business_id", $id);
$this->db->delete('opening_hours');

Is there a way to insert a row with no data

Using CakePHP 3.3, I have a table that only contains a primary key. This is used as a sequence. In CakePHP, I insert a row with no data and it generates a new row.
Cake 2.7
class Identity extends AppModel {
function nextVal() {
$data = [];
$this->create();
$this->save($data);
return $this->id;
}
}
I am attempting to duplicate this behavior in CakePHP 3.3 and it isn't doing what I expect.
CakePHP 3.3
class IdentityTable extends Table
{
public function generate() {
$identity = $this->newEntity();
if ( $this->save($identity) ) {
// The $ccn entity contains the id now
\Cake\Log\Log::debug(__METHOD__. ' success');
return $identity->id;
}
\Cake\Log\Log::debug(__METHOD__. ' failed');
return false;
}
//
public function initialize(array $config)
{
parent::initialize($config);
$this->table('identity');
$this->displayField('identityId');
$this->primaryKey('identityId');
}
}
MySQL is perfectly happy with this:
INSERT INTO `identity` () VALUES()
I am thinking that the CakePHP 3.x ORM sees that I am not inserting anything and it bailing on the save.
Is there a way in CakePHP 3 to insert a row with no data?
You have to tell cake that something is cahenged in the entity and mark it as 'dirty'. i.e.
$identity ->dirty('id', true);
so the resulting query will be
INSERT INTO sic_suppliers (id) VALUES (NULL)
that I guess is ok for you
PS
don't know if this is the best way to achieve this but it seems to work.
Edit: following #ndm suggestion you can also insert the record directly (see the cookbook)
$this->query()->insert(['id'])
->values([null])
->execute();

How to update a pivot table using Eloquent in laravel 5

I am new to laravel. I am working on a laravel 5 app and I am stuck here. I have 2 models as such:
class Message extends Eloquent{
public function user()
{
return $this->belongsTo('App\User', 'from');
}
public function users()
{
return $this->belongsToMany('App\User')->withPivot('status');
}
}
class User extends Eloquent {
public function messages()
{
return $this->hasMany('App\Message', 'from');
}
public function receive_messages() {
return $this->belongsToMany('App\Message')->withPivot('status');
}
}
There exist a many-to-many relationship between Message and User giving me a pivot table as such:
Table Name: message_user
Colums:
message_id
user_id
status
I have an SQL query as such:
update message_user
set status = 1
where user_id = 4 and message_id in (select id from messages where message_id = 123)
How can I translate this query to the laravel equivalent?
The code below solved my problem:
$messages = Message::where('message_id', $id)->get();
foreach($messages as $message)
$message->users()->updateExistingPivot($user, array('status' => 1), false);
You may use one of these two functions, sync() attach() and the difference in a nutshell is that Sync will get array as its first argument and sync it with pivot table (remove and add the passed keys in your array) which means if you got 3,2,1 as valued within your junction table, and passed sync with values of, 3,4,2, sync automatically will remove value 1 and add the value 4 for you. where Attach will take single ID value
The GIST: if you want to add extra values to your junction table, pass it as the second argument to sync() like so:
$message = Messages::find(123);
$user = User::find(4);
// using attach() for single message
$user->message()->attach($message->id, [
'status' => 1
]);
$message2 = Messages::find(456); // for testing
// using sync() for multiple messages
$user->message()->sync([
$message->id => [
'status' => 1
],
$message2->id => [
'status' => 1
],
]);
Here is a small example of how to update the pivot table column
$query = Classes::query();
$query = $query->with('trainees')
->where('user_id', Auth::id())
->find($input['classId']);
foreach ($query->trainees as $trainee) {
$trainee->pivot->status = 1 //your column;
$trainee->pivot->save();
}
Note: make sure your relation data must in an array
Hope its help you :)
happy coding
Laravel 5.8
First, allow your pivot columns to be searchable by chaining the withPivot method to your belongsToMany
Copied from my own code to save time
// I have 3 columns in my Pivot table which I use in a many-to-many and one-to-many-through scenarios
$task = $user->goalobjectives()->where(['goal_objective_id'=>$goal_objective_id,'goal_obj_add_id'=>$goal_obj_add_id])->first(); //get the first record
$task->pivot->goal_objective_id = $new; //change your col to a new value
$task->pivot->save(); //save
The caveat is that your pivot table needs to have a primary 'id' key.
If you don't want that then you can try the following:
$tasks=$user->posts()->where(['posts_id'=>$posts_id,'expires'=>true])->get()->pluck('id'); // get a collection of your pivot table data tied to this user
$key=join(",",array_keys($tasks->toArray(),$valueYouWantToRemove));
$tasks->splice($key,1,$newValueYouWantToInsert);
$c = array_fill(0,$tasks->count(),['expires'=>true]); //make an array containing your pivot data
$newArray=$tasks->combine($c) //combine the 2 arrays as keys and values
$user->posts()->sync($newArray); //your pivot table now contains only the values you want
4th July Update Update to above snippet.
//Ideally, you should do a check see if this user is new
//and if he already has data saved in the junction table
//or are we working with a brand new user
$count = $user->goalobjectives->where('pivot.goal_obj_add_id',$request->record)->count();
//if true, we retrieve all the ids in the junction table
//where the additional pivot column matches that which we want to update
if($count) {
$ids = $user->goalobjectives->where('pivot.goal_obj_add_id',$request->record)->pluck('id');
//convert to array
$exists = $ids->toArray();
//if user exists and both saved and input data are exactly the same
//there is no need
//to update and we redirect user back
if(array_sum($inputArray) == array_sum($exists)) {
//redirect user back
}
//else we update junction table with a private function
//called 'attachToUser'
$res = $this->attachToUser($user, $inputArray, $ids, $request->record);
}//end if
elseif(!$count) {
//we are working with a new user
//we build an array. The third pivot column must have equal rows as
//user input array
$fill = array_fill(0,count($inputArray),['goal_obj_add_id'=>$request->record]);
//combine third pivot column with user input
$new = array_combine($inputArray,$fill);
//junction table updated with 'user_id','goal_objective_id','goal_obj_add_id'
$res = $user->goalobjectives()->attach($new);
//redirect user if success
}
//our private function which takes care of updating the pivot table
private function attachToUser(User $user, $userData, $storedData, $record) {
//find the saved data which must not be deleted using intersect method
$intersect = $storedData->intersect($userData);
if($intersect->count()) {
//we reject any data from the user input that already exists in the database
$extra = collect($userData)->reject(function($value,$key)use($intersect){
return in_array($value,$intersect->toArray());
});
//merge the old and new data
$merge = $intersect->merge($extra);
//same as above we build a new input array
$recArray = array_fill(0,$merge->count(),['goal_obj_add_id'=>$record]);
//same as above, combine them and form a new array
$new = $merge->combine($recArray);
//our new array now contains old data that was originally saved
//so we must remove old data linked to this user
// and the pivot record to prevent duplicates
$storedArray = $storedData->toArray();
$user->goalobjectives()->wherePivot('goal_obj_add_id',$record)->detach($storedArray);
//this will save the new array without detaching
//other data previously saved by this user
$res = $user->goalobjectives()->wherePivot('goal_obj_add_id',$record)->syncWithoutDetaching($new);
}//end if
//we are not working with a new user
//but input array is totally different from saved data
//meaning its new data
elseif(!$intersect->count()) {
$recArray = array_fill(0,count($userData),['goal_obj_add_id'=>$record]);
$new = $storedData->combine($recArray);
$res = $user->goalobjectives()->wherePivot('goal_obj_add_id',$record)->syncWithoutDetaching($new);
}
//none of the above we return false
return !!$res;
}//end attachToUser function
This will work for pivot table which doesn't have a primary auto increment id. without a auto increment id, user cannot update,insert,delete any row in the pivot table by accessing it directly.
For Updating your pivot table you can use updateExistingPivot method.

How do I remove a row from mysql immediately after calling it?

Basically, I have a mysql database where every time I get a row, I want to delete it from the database (after reading its information).
I know I could do something like
$result = mysql_query(" SELECT <some row> FROM table ");
$row = mysql_fetch_array($result);
$id = $row["id"];
mysqli_query(" DELETE FROM table WHERE id=$id");
but now it seems I have two queries going on. Is there a command to tell mysql that I want the row deleted as soon as it gives me the information? I imagine that'd save time and resources.
In my head, it looks like
$result = mysql_query(" SELECT <some row> FROM table THEN DELETE ");
EDIT additional information: I wish to use the SELECTed information after deleting the row. To put it simply, I only want one instance of the information to exist at any give time; it would be as if I were only "moving" a physical copy of the information, so that when it is put on a device/what have you, it is no longer in the table since there is only one copy.
Sorry if my understanding of mysql is rough -- I'm pretty new to it :P
I don't know why you need it but you can use a stored procedure for that:
DELIMITER $$
CREATE PROCEDURE select_and_delete(IN aid INT)
BEGIN
SELECT * FROM table1 WHERE id = aid;
DELETE FROM table1 WHERE id = aid;
END$$
DELIMITER ;
Here is SQLFiddle demo.
mysql_* extension is deprecated, therefore use prepared statements and mysqli or PDO.
Your php code using PDO might look like
$id = 1;
try {
$db = new PDO('mysql:host=localhost;dbname=test;charset=UTF8', 'user', 'userpwd');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$query = $db->prepare("CALL select_and_delete(?)");
$query->execute(array($id));
$result = $query->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo "Exception: " .$e->getMessage();
$result = false;
}
$query = null;
$db = null;
//do whatever you need to do with your resultset
var_dump($result);
Following a rather simplified table structure (with the only column id) presented in SQL Fiddle example if you call it with $id=1 you'll you'll get in $result:
array(1) {
[0]=>
array(1) {
["id"]=>
int(1)
}
}
You'll need to add a timestamp field (with default as CURRENT_TIMESTAMP) to be able to tell when the row was added.
Then you can run the following MySQL query.
DELETE FROM `table` WHERE `timestamp` > DATE_SUB(NOW(), INTERVAL 0 SECOND);
You will need to run this as a cron job though. AFAIK it can't be done in MySQL alone.