Codeigniter/Mysql: Column count doesn't match value count with insert_batch()? - mysql

Alright, so i have a huge list (like 500+) of entries in an array that i need to insert into a MySQL database.
I have a loop that populates an array, like this:
$sms_to_insert[] = array(
'text' => $text,
'contact_id' => $contact_id,
'pending' => $status,
'date' => $date,
'user_id' => $this->userId,
'sent' => "1"
);
And then i send it to the database using the built insert_batch() function:
public function add_sms_for_user($id, $sms) {
//$this->db->delete('sms', array("user_id" => $id)); Irrelevant
$this->db->insert_batch('sms', $sms); // <- This!
}
The error message i get is as follows:
Column count doesn't match value count at row 1.
Now, that doesn't make sense at all. The columns are the same as the keys in the array, and the values are the keys value. So, why is it not working?
Any ideas?

user_id turned out to be null in some situations, that's what caused the error.
EDIT: If you replace insert_batch() with a loop that runs insert() on the array keys you will get more clear error messages.

Related

Can't figure out a query to upsert collection into a tablr

I want to insert or update rows into my models table. But can't figure out the query.
SmStudentAttendance This is my model.
$students is my collection.
I have put the collection fields in arrays.
foreach ($students as $student) {
array_push($temp_id, $student->id);
array_push($temp_lastname, $student->last_name);
array_push($temp_academic_id, $student->academic_id);
array_push($temp_attendance, 'P');
array_push($temp_attendancedate, $date);
array_push($temp_schoolid, '1');
array_push($temp_updatedby, '1');
array_push($temp_createdby, '1');
}
Now I want to insert them if a row for the student_id and attendance_date is not present in the table else update if it already exists.
This is the query:
SmStudentAttendance::upsert('attendance_type', $temp_attendance, 'attendance_date', $temp_attendancedate, 'student_id', $temp_id, 'created_by', $temp_createdby, 'updated_by', $temp_updatedby, 'school_id', $temp_schoolid, 'academic_id', $temp_academic_id);
Error I am geting:
Argument 1 passed to Illuminate\Database\Eloquent\Builder::upsert() must be of the type array, string given, called in D:\xampp\htdocs\sms\vendor\laravel\framework\src\Illuminate\Support\Traits\ForwardsCalls.php on line 23
You're creating your arrays for columns rather than rows, this will cause problems, consider this code instead:
$studentRows = [];
foreach ($students as $student) {
$studentRows[] = [
'id' => $student->id,
'last_name' => $student->last_name,
'academic_id' => $student->academic_id,
'attendance_type' => 'P',
'attendance_date' => $date,
// .... rest of the fields
]
}
SmStudentAttendance::upsert($studentRows, [ 'id', 'last_name', 'academic_id' ], [ 'attendance_type', 'attendance_date' ]);
In general the idea is you pass it an array of rows you want to upsert, then an array of fields to match and an array of fields to update. Then Laravel will make queries find all rows that match the fields specified and update those and then insert the rows that did not match the given fields.
The error message, "Argument 1 passed to Illuminate\Database\Eloquent\Builder::upsert() must be of the type array, string given", suggests that the first parameter needs to be an array rather than the string you are setting.
Take a look at the documentation for this at https://laravel.com/docs/8.x/eloquent#upserts for an example. The method accepts two arrays. The first contains all of the data to be updated, the second the fields which uniquely identify the record. You will need to update your method call to match this syntax.

Multiple Fields with a GroupBy Statement in Laravel

Already received a great answer at this post
Laravel Query using GroupBy with distinct traits
But how can I modify it to include more than just one field. The example uses pluck which can only grab one field.
I have tried to do something like this to add multiple fields to the view as such...
$hats = $hatData->groupBy('style')
->map(function ($item){
return ['colors' => $item->color, 'price' => $item->price,'itemNumber'=>$item->itemNumber];
});
In my initial query for "hatData" I can see the fields are all there but yet I get an error saying that 'colors', (etc.) is not available on this collection instance. I can see the collection looks different than what is obtained from pluck, so it looks like when I need more fields and cant use pluck I have to format the map differently but cant see how. Can anyone explain how I can request multiple fields as well as output them on the view rather than just one field as in the original question? Thanks!
When you use groupBy() of Laravel Illuminate\Support\Collection it gives you a deeper nested arrays/objects, so that you need to do more than one map on the result in order to unveil the real models (or arrays).
I will demo this with an example of a nested collection:
$collect = collect([
collect([
'name' => 'abc',
'age' => 1
]),collect([
'name' => 'cde',
'age' => 5
]),collect([
'name' => 'abcde',
'age' => 2
]),collect([
'name' => 'cde',
'age' => 7
]),
]);
$group = $collect->groupBy('name')->values();
$result = $group->map(function($items, $key){
// here we have uncovered the first level of the group
// $key is the group names which is the key to each group
return $items->map(function ($item){
//This second level opens EACH group (or array) in my case:
return $item['age'];
});
});
The summary is that, you need another loop map(), each() over the main grouped collection.

laravel 5 update multiple columns in one line?

It's possible update multiple columns in one line with laravel 5.4?
I try to this
DB::table('Home_Content')->where('id',1)->update(
[$_POST['name'] => $_POST['content']],
[$_POST['title'] => $_POST['titleMsg']]
);
only 1st part is work([$_POST['name'] => $_POST['content']])
server not return any error, but only 1st part is success.
Update method syntax in Laravel is like so :
DB::table('users')
->where('id', 1)
->update(['options->enabled' => true]);
Which means the values we want to update are inside an array, what I've seen is that you separated what you want to update into two arrays
DB::table('Home_Content')->where('id',1)->update(
[$_POST['name'] => $_POST['content']],
[$_POST['title'] => $_POST['titleMsg']]
);
so now you are out of the array
Your code should be like this
DB::table('Home_Content')->where('id',1)->update([
$_POST['name'] => $_POST['content'],
$_POST['title'] => $_POST['titleMsg']
]);
Meaning each pair has a key and a value and separated with comma
If you want to update record in multiple row with id as Array() use following query.
DB::table('users')
->whereIn('id', [1,2,3])
->update(['status' => 1]);

Laravel updateOrCreate with auto-incremental database

My purpose is to update if the value exists, else inserts a new row in the database table after submitting the form.
The problem is, the function here adds new columns in db table instead of updating them.
Here's my function :
MyModel::updateOrCreate(array(
'myField' => 'myValue',
))
->where('myAutoIncrementalField', '=', '5')
->where('myPrimaryKey', '=', '8');
My database table is like that :
1. myPrimaryKey (not auto incremental and is fillable on model.)
2. myAutoIncrementalField (auto incremental and cannot be fillable on model.)
Thank you in advance.
This is how you use this method:
Model::updateOrCreate(
['primary_key' => 8],
['field' => 'value', 'another_field' => 'another value']
);
As 1st param pass an array of fields that are unique, or in your case, the primary key. Non-unique fields don't make sense here obviously just like passing anything along with the PK.
2nd param is an array of values that should be updated/created too, but being ignored in the unique/pk search.
You cannot use where functions with this method. You have to include the where clauses in the array.
MyModel::updateOrCreate(array(
'myField' => 'myValue',
'myAutoIncrementalField' => '5',
'myPrimaryKey' => '8'
));

How to generate a MySQL IS NOT NULL condition in CakePHP?

I'm trying to get a subset of results as a virtualField for use in my view. I may even be way off on how I'm approaching this, but here's what I've done so far:
I started with this question here: CakePHP virtualField find all not null which lead to this little beauty.
Now I have an issue where the find statement passing (Array) into the MySQL.
My code looks like:
class Transaction extends AppModel {
public function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
$this->virtualFields['Accounts'] = $this->find("all", array("conditions" => array("account !=" => null)));
}
And I'm seeing:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Array' in 'field list'
SQL Query: SELECT `Transaction`.`id`, `Transaction`.`name`,
`Transaction`.`person_id`, `Transaction`.`account`, (Array)
AS `Transaction__Accounts` FROM `my_database`.`transactions`
AS `Transaction` WHERE `Transaction`.`person_id` = (2)
I've also tried $this->Transaction->find and "Transaction.account !=", to no avail. I've found some other issues with the (Array) but none that help my situation. Any pointers in the right direction would be great.
Problem: your query results are an array, and you're telling SQL to assign a field name to each query result containing that array - virtual fields are only made to contain single level variables like strings.
Solution: use a join structure onto itself with those conditions which will return a nested result set along with each of your results. Use CakePHP's model relationships to do this:
<?php
class Transaction extends AppModel {
var $hasMany = array(
'Accounts' => array(
'className' => 'Transaction',
'foreignKey' => false,
'conditions' => array('Accounts.account IS NOT NULL')
)
);
}
?>
Example output:
Array(
'Transaction' => array( // transaction data),
'Accounts' => array( // associated transaction data with account set to null
)
Now, as you can probably gather from that result, if you return 1000 rows from Transaction, you'll get all results from Accounts nested into each Transaction result. This is far from ideal. From here, you can either make the join conditions more specific to target relevant Accounts records, or this is not the right approach for you.
Other approaches could be:
Accounts model, uses Transaction database table, implicit find conditions are that account is null
Manual query to retrieve these results in the afterFind() method of your Transaction model, which will retrieve these results once, and you'll then return array_merge($accounts, $transactions)