Mass assignment issue - php

I have a form with a lot of fields and each of them has to be added in a table as a different row.
My table looks like this:
| Category | Device | Value |
|----------|:-------|-------------|
| 2 | 1 | some value |
| 3 | 1 | other value |
| 7 | 3 | etc |
The Category and Device are actually foreign keys from the Categories and Devices tables. Also they should be unique, meaning that there can't be Category: 2 and Device: 1 twice. If they exists already, the value should be updated.
The categories and value are retrieved from the form and it looks like this:
{"2":"some value","3":"other value","5":"etc","6":"something","8":"can be empty"}
The device also comes from the form but it will be the same.
Now I need to enter everything in my database and I'm looking for a simple solution.
But it will do about 100 queries (one for each input) and I'm sure there must be a better solution.
If one of the values that comes from the form is empty, it should be ignored.
Here's my currently working code, maybe you can understand better:
public function postSpecs(Request $request)
{
$specs = $request->except(['_token', 'deviceid']);
foreach($specs as $key=>$val)
{
if($val == '') continue;
if(Spec::where('category', $key)->where('device', $request->deviceid)->exists())
{
$spec = Spec::where('category', $key)->where('device', $request->deviceid)->first();
$spec->value = $val;
$spec->save();
}
else
{
$spec = new Spec;
$spec->category = $key;
$spec->device = $request->deviceid;
$spec->value = $val;
$spec->save();
}
}
}

use the insert method like:
$model->insert([
['email' => 'taylor#example.com', 'votes' => 0],
['email' => 'dayle#example.com', 'votes' => 0]
]);
See also: http://laravel.com/docs/5.1/queries#inserts
EDIT:
Updated to your code:
public function postSpecs(Request $request)
{
$specs = $request->except(['_token', 'deviceid']);
$data = array();
foreach($specs as $key=>$val)
{
if($val == '') continue;
if(Spec::where('category', $key)->where('device', $request->deviceid)->exists())
{
$spec = Spec::where('category', $key)->where('device', $request->deviceid)->first();
$spec->value = $val;
$spec->save();
}
else
{
$data[]['category'] = $key;
$data[]['device'] = $request->deviceid;
$data[]['value'] = $val;
}
}
Spec::insert($data);
}
While this is not perfect, it will save you a lot queries. Else you have to use raw query something like (untested!):
INSERT INTO spec (id,category,device,value) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE id=LAST_INSERTED_ID(id)

A little more information on the on duplicate key update method because I think it deserves to be a possible solution if this is something that's going to happen a lot...
First, you would need to create the unique key. This will force those columns to be unique. I'd highly suggest adding the unique key no matter how you handle the inserts. It may help preserve your sanity in the future.
In a migration, you would do...
$table->unique(['Category', 'Device']);
Or in plain sql...
alter table category_device add unique index unique_category_device (Category, Device);
Now to insert into the table, you would simply use the query...
insert into category_device (Category, Device, Value) values ($category_id, $device_id, $value)
on duplicate key udpate Value = VALUES(Value)
If it's not a duplicate entry, mysql will simply insert the new record. If it is a duplicate, mysql will update the value column with whatever you tried to insert as the new value. This is very performance friendly as you will not be required to check for the existence of duplicates before trying to do the insert as it will simpy act like an update statement in the event there is a duplicate. The drawback here is laravel does not support on duplicate key update so you would need to actually write the SQL. I would suggest you still use data bindings though which can make it somewhat easier.
$sql = "insert into category_device (`Category`, `Device`, `Value`) values (:category_id, :device_id, :value)
on duplicate key udpate Value = VALUES(Value)";
$success = \DB::insert($sql, [
'category_id' => $category_id,
'device_id' => $device_id,
'value' => $value
]);
Also please note, if you have over 100 entries, you could loop through them and keep adding to the query and it should work just the same...
insert into category_device (`Category`, `Device`, `Value`)
values
(1, 1, 'some value'), (1, 2, 'some other value'), (1, 3, 'third value')...
on duplicate key udpate Value = VALUES(Value)";
If you are using this method, I would probably not worry about the data binding and just insert the values right into the query. Just make sure they are properly escaped beforehand.
If you had a hundred items to insert, which would be 200 queries (one for checking the existence of the record, and another for inserting/updating), this would turn that 200 queries into 1 query which obviously would be a huge performance gain.

Related

How to insert multiple select option in PHP MySQL when multiple='multiple' is enabled

I have a FORM with method POST. In the form I have two select drop-down with multiple='multiple' enabled in both so that user can make multiple selections.
Now I am using MySQL database to store data.
I am using foreach loop to iterate all the selections made.
Here is the image Select drop-down image
My code
$cid = $row['cid'];
$work_location = $_POST['work_location'];
$interest = $_POST['interest'];
foreach ($work_location as $value) {
$work = $value;
}
foreach ($interest as $value) {
$int = $value;
}
$insert_oth_data = "INSERT INTO resume_spec_data
(cid, work_location, interest)
VALUES('$cid', '$work', '$int')";
mysqli_query($con, $insert_oth_data);
echo var_export($work_location);
echo var_export($interest);
echo var_export($work);
echo var_export($int);
echo var_export( $insert_oth_data );
Here work_location and interest are the fields that will carry the multiple select values. I don't want to use implode function because it will make data extraction difficult as table grows. I want to store the values in saperate rows of database.
The problem I am facing is that data is being inserted, but the problem is when I select multiple options only one data is being inserted not all.
The result I get after inserting the data is:
array ( 0 => 'Delhi', 1 => 'NCR', )array ( 0 => 'Software development', 1 => 'Business analyst', 2 => 'Web development', )'NCR''Web development''INSERT INTO resume_spec_data(cid, work_location, interest) VALUES(\'1\', \'NCR\', \'Web development\')'
As you can compare from the image I have made multiple selections. But single data is being taken here
For reference
$work_location contains array ( 0 => 'Delhi', 1 => 'NCR', )
$interest contains
array ( 0 => 'Software development',
1 => 'Business analyst',
2 => 'Web development',
)
$work contains NCR
$int contains Web development only
$work and $int should also contains same as $work_location and $interest respectively.
One thing that I found here is that I have applied Insert query outside the foreach loop maybe that is why only one value is being taken. Since I am using multiple select drop-down here with multiple='multiple' enabled, I don't know how to do this to achieve the result I want.
Any help would be appreciated.
Your INSERT command happens once, and it occurs after your loops have all finished. So in the loops you're pointlessly re-assigning new values to the same variable, and then discarding them again without using them. Obviously if you wait until after a loop has finished, then you can't use the values which are present when the loop is running. You'll only ever see the last one which was assigned in the last run of the loop.
And you've explained that you're struggling to fix this because you don't know how to include the insert query inside the loops, because there are 2 different foreach loops.
So really I think the root cause here is that your database structure is flawed. It seems you permit different numbers of values in your form (e.g. 2 entries for work locations, then 3 entries for domains of interest, per your screenshot), and there's no particular relationship between them (e.g. does "Delhi" belong with "software development, or "business analyst", or the other one? There's no logic to explain that).
And yet, you're trying to store them in a table which inherently relates them together by storing them next to each other in rows. This will result in poor data quality because a) there's no way to decide which belongs with which, and b) if you have different numbers of entries in each field then you'll end up with gaps.
You need separate tables to store each list - one table for locations, and one table for interests, both with the Cid to link them to the master record.
This will then make it easy to use the data in you loops e.g.
foreach ($work_location as $value) {
$work = $value;
//...write query code to insert into work locations table
}
foreach ($interest as $value) {
$int = $value;
//...write query code to insert into interests table
}

Array to string conversion error in Code Igniter when inserting array of data into table in database

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.

How to update multiple rows in sql from array data using laravel eloquent

I was trying to update multiple records in my database using laravel eloquent but is getting errors when trying to update using an array.
I am not really sure how to correctly get the data from the array to my update function.
The array I am passing looks like this.
My Database table looks like
id | checklistid | categoryid | isCheck | created_at | updated_at
My Controller looks like this.
public function updateCategoryListData(Request $request){
$checklistdata = $request->get('checklist');
$id = $request->get('checklistid');
$dataset = [] ;
foreach($checklistdata as $key =>$value){
$dataset[] = ['checklistid'=>$id,'categoryid' => $key,'isCheck'=>$value];
}
categorylistcontent::where([['checklistid',$id], ['categoryid', $dataset=>['categoryid'] ]])
->update($dataset['isCheck']);
}
Would you be able to advise how I can use the array to get the 'checklistid' and 'categoryid' to be used as the where clause of the update statement and then the 'isCheck' to be set in the update.
You don't need dataset array, rather do the following:
foreach($checklistdata as $key =>$value){
categorylistcontent::where('checklistid',$id)->where('categoryid',$key)
->update(['isCheck'=>$value]);
}
You can't do that with just one query, but you could do that with two queries. An example:
$check = categorylistcontent::query();
$notCheck = categorylistcontent::query();
foreach ($request->checklist as $item) {
$query = $item['isCheck'] === 1 ? 'check' : 'notCheck';
$$query->orWhere(function($q) use($item) {
$q->where('checklistid', $item['checklistid'])->where('categoryid', $item['categoryid']);
}
}
$check->update(['check' => 1]);
$notCheck->update(['check' => 1]);
I haven't tested this exact code, but I think it will be helpful for you to get the idea.
You need to update multiple rows by putting it in foreach loop
eg:
foreach($checklistdata as $key =>$value){
$dataset[] = ['checklistid'=>$id,'categoryid' => $key,'isCheck'=>$value];
categorylistcontent::where([['checklistid',$id], ['categoryid', $dataset=>['categoryid'] ]])
->update($dataset['isCheck']);
}

How to mass insert or update in a single query (not a for loop of queries) using Laravel 4.2

$cardQueryList = [];
foreach($cards as $cardName => $quantity) {
$cardQueryList[] = [
'username' => $user->username,
'card_uid' => $card->uid,
'have_quantity' => $quantity
];
}
Collection::insert($cardQueryList);
The above code creates new rows even if the row exists. How can I make it so if the row exists, it updates. And if it doesn't it creates the row? An Eloquent or Fluent answer would be optimal but I'm open to raw if there's no other way.
I would like to do a mass update/insert with a single query. Not a for loop of queries for every record. Ideally I'd like to hit the database once for obvious reasons.
Also I've already checked the following link:
Insert a new record if not exist and update if exist, laravel eloquent
The above works for a single record update/insert. Which if I ran with a for loop would be very slow. I'm looking for an answer that allows a mass insert/update in a single query.
Note: I'm using, both 'username' and 'card_uid' as my key. So basically when I find a row with said username and card_uid, I'd like to update the corresponding row. Otherwise create a new row.
Typically the sort of sql you would be using would be something along the lines of the following:-
insert into `TABLE` ( `FIELD1`,`FIELD2`, `FIELD3` ) values ( 'VALUE1','VALUE2','VALUE3' )
on duplicate key
update
`FIELD1`='VALUE1',
`FIELD2`='VALUE2',
`FIELD1`='VALUE3';
How you would use this with laravel I couldn't tell you! Oops - forgot the field names in the update part
<?php
$valuesArray = [];
foreach( $cards as $cardName => $quantity )
{
$valuesArray[] = "('".$user->username."','".$card->uid."','".$quantity."')";
}
$db->query
(
"INSERT INTO `TABLE` ( `username`,`card_uid`, `have_quantity` ) VALUES ".implode( ',', $valuesArray )."
ON DUPLICATE KEY UPDATE `have_quantity` = VALUES(`have_quantity`);"
);
Make sure you have a primary key on username and card_uid. Also don't forget to escape the values and to only run the query if $valuesArray is not empty.
If you don't want duplicates have your DB schema prevent the entry of duplicate entries. Catch the error in this case and just continue on.
You may try this:
Collection::firstOrNew([
'username' => $user->username,
'card_uid' => $card->uid,
'have_quantity' => $cards['quantity']
])
->save();
To make it working, add $fillable property with mass assignable field names in your Collection model, for example:
protected $fillable = ['username', 'card_uid', 'have_quantity'];

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);

Categories