Laravel insert or update multiple rows - laravel

Im new in laravel, and im trying to update my navigation tree.
So i want to update my whole tree in one query without foreach.
array(
array('id'=>1, 'name'=>'some navigation point', 'parent'='0'),
array('id'=>2, 'name'=>'some navigation point', 'parent'='1'),
array('id'=>3, 'name'=>'some navigation point', 'parent'='1')
);
I just want to ask - is there posibility in laravel to insert(if new in array) or update my current rows in database?
I want to update all, because i have fields _lft, _right, parent_id in my tree and im using some dragable js plugin to set my navigation structure - and now i want to save it.
I tried to use
Navigation::updateOrCreate(array(array('id' => '3'), array('id'=>'4')), array(array('name' => 'test11'), array('name' => 'test22')));
But it works just for single row, not multiple like i tried to do.
Maybe there is another way to do it?

It's now available in Laravel >= 8.x
The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table. The method's third and final argument is an array of columns that should be updated if a matching record already exists in the database:
Flight::upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);

I wonder why this kind of feature is not yet available in Laravel core (till today). Check out this gist The result of the query string would look like this: here
I am putting the code here just in case the link breaks in the future, I am not the author:
/**
* Mass (bulk) insert or update on duplicate for Laravel 4/5
*
* insertOrUpdate([
* ['id'=>1,'value'=>10],
* ['id'=>2,'value'=>60]
* ]);
*
*
* #param array $rows
*/
function insertOrUpdate(array $rows){
$table = \DB::getTablePrefix().with(new self)->getTable();
$first = reset($rows);
$columns = implode( ',',
array_map( function( $value ) { return "$value"; } , array_keys($first) )
);
$values = implode( ',', array_map( function( $row ) {
return '('.implode( ',',
array_map( function( $value ) { return '"'.str_replace('"', '""', $value).'"'; } , $row )
).')';
} , $rows )
);
$updates = implode( ',',
array_map( function( $value ) { return "$value = VALUES($value)"; } , array_keys($first) )
);
$sql = "INSERT INTO {$table}({$columns}) VALUES {$values} ON DUPLICATE KEY UPDATE {$updates}";
return \DB::statement( $sql );
}
So you can safely have your arrays inserted or updated as:
insertOrUpdate(
array(
array('id'=>1, 'name'=>'some navigation point', 'parent'='0'),
array('id'=>2, 'name'=>'some navigation point', 'parent'='1'),
array('id'=>3, 'name'=>'some navigation point', 'parent'='1')
)
);
Just in case any trouble with the first line in the function you can simply add a table name as a second argument, then comment out the line i.e:
function insertOrUpdate(array $rows, $table){
.....
}
insertOrUpdate(myarrays,'MyTableName');
NB: Be careful though to sanitise your input! and remember the timestamp fields are not touched. you can do that by adding manually to each arrays in the main array.

I've created an UPSERT package for all databases: https://github.com/staudenmeir/laravel-upsert
DB::table('navigation')->upsert(
[
['id' => 1, 'name' => 'some navigation point', 'parent' => '0'],
['id' => 2, 'name' => 'some navigation point', 'parent' => '1'],
['id' => 3, 'name' => 'some navigation point', 'parent' => '1'],
],
'id'
);

Eloquent Style
public function meta(){ // in parent models.
return $this->hasMany('App\Models\DB_CHILD', 'fk_id','local_fk_id');
}
.
.
.
$parent= PARENT_DB::findOrFail($id);
$metaData= [];
foreach ($meta['meta'] as $metaKey => $metaValue) {
if ($parent->meta()->where([['meta_key', '=',$metaKey]] )->exists()) {
$parent->meta()->where([['meta_key', '=',$metaKey]])->update(['meta_value' => $metaValue]);
}else{
$metaData[] = [
'FK_ID'=>$fkId,
'meta_key'=>$metaKey,
'meta_value'=> $metaValue
];
}
}
$Member->meta()->insert($metaData);

No, you can't do this. You can insert() multiple rows at once and you can update() multiple rows using same where() condition, but if you want to use updateOrCreate(), you'll need to use foreach() loop.

I didn't find a way to bulk insert or update in one query. But I have managed with only 3 queries. I have one table name shipping_costs. Here I want to update the shipping cost against the shipping area. I have only 5 columns in this table id, area_id, cost, created_at, updated_at.
// first get ids from table
$exist_ids = DB::table('shipping_costs')->pluck('area_id')->toArray();
// get requested ids
$requested_ids = $request->get('area_ids');
// get updatable ids
$updatable_ids = array_values(array_intersect($exist_ids, $requested_ids));
// get insertable ids
$insertable_ids = array_values(array_diff($requested_ids, $exist_ids));
// prepare data for insert
$data = collect();
foreach ($insertable_ids as $id) {
$data->push([
'area_id' => $id,
'cost' => $request->get('cost'),
'created_at' => now(),
'updated_at' => now()
]);
}
DB::table('shipping_costs')->insert($data->toArray());
// prepare for update
DB::table('shipping_costs')
->whereIn('area_id', $updatable_ids)
->update([
'cost' => $request->get('cost'),
'updated_at' => now()
]);

in your controller
use DB;
public function arrDta(){
$up_or_create_data=array(
array('id'=>2, 'name'=>'test11'),
array('id'=>4, 'name'=>'test22')
);
var_dump($up_or_create_data);
echo "fjsdhg";
foreach ($up_or_create_data as $key => $value) {
echo "key ".$key;
echo "<br>";
echo " id: ".$up_or_create_data[$key]["id"];
echo "<br>";
echo " Name: ".$up_or_create_data[$key]["name"];
if (Navigation::where('id', '=',$up_or_create_data[$key]["id"])->exists()) {
DB::table('your_table_ name')->where('id',$up_or_create_data[$key]["id"])->update(['name' => $up_or_create_data[$key]["name"]]);
}else{
DB::insert('insert into your_table_name (id, name) values (?, ?)', [$up_or_create_data[$key]["id"], $up_or_create_data[$key]["name"]]);
}
}

Related

Laravel backpack select2_from_ajax setting my value as null after the correct value has been saved

Im having a weird problem.
Im using laravel backpack for an admin panel. There i use select2_from_ajax to list a values according to another field in create operation. It is showing up correctly as expected & i can select one too.
But after selection when i click save & back it gives me an error
That means my column doesn't allow to update to null right.
So when i go back & check the column it has saved the correct value.
But when default value of my column was null this error will not showup & db value would be changed to null.
This is my select2_from_ajax part.
$this->crud->addField([ // Select
'label' => "Link Type",
'type' => 'select_from_array',
'name' => 'link_type', // the db column for the foreign key
'options' => [1 => 'Product',0 => 'Collection'],
'allows_null' => false,
]);
$this->crud->addField([ // Select
'label' => "Link To", // Table column heading
'type' => "select2_from_ajax",
'name' => "link_to",
'entity' => 'link',
'attribute' => "name",
'data_source' => url('admin/itemtype'),
'placeholder' => "Select a item",
'minimum_input_length' => 0,
'include_all_form_fields' => true,
'dependencies' => ['link_type'],
]);
So why is it trying to set null value after the correct value?
Any help would be appreciated. Thanks.
My admin/itemtype function:
$search_term = $request->input('q');
$form = collect($request->input('form'))->pluck('value', 'name');
if ($search_term) {
if ($form['link_type'] == 0) {
$items = Collection::where('name', 'LIKE', '%' . $search_term . '%')->paginate(10);
} else {
$items = Product::where('title', 'LIKE', '%' . $search_term . '%')->paginate(10);
}
} else {
if ($form['link_type'] == 0) {
$items = Collection::paginate(10);
} else {
$items = Product::paginate(10);
}
}
return $items;

Use Array Data in CodeIgniter Model as SQL?

In my controller, I am passing data to the model using the following code:
$data = array(
'gid' => $this->input->post('gid'),
'name' => $this->input->post('name'),
'pic' => $this->input->post('pic'),
'link' => $this->input->post('link')
);
var_dump($data);
$this->Login_model->insert_entry($data);
In my model, what I want to do is use the gid value as part of an SQL statement, like so:
$get_gid = $this->db->query('SELECT * FROM users WHERE gid = $gid');
Obviously this doesn't work, so I'm just wondering how I get the gid from $data and use it in my SQL statement?
Tested using
$get_gid = $this->db->where('gid', $data['gid'])->get('users');
print_r($get_gid);
However output is:
CI_DB_mysql_result Object ( [conn_id] => Resource id #30 [result_id]
=> Resource id #33 [result_array] => Array ( ) [result_object] => Array ( ) [custom_result_object] => Array ( ) [current_row] => 0
[num_rows] => 0 [row_data] => )
Did you try gid = $data['gid']
I assume that yours model method looks like this:
insert_entry($data)
{
here active record or query...
}
If yes try to display query to see if $data['gid'] is visible there
You can try it by
$this->db->get_compiled_select();
Or after query runs
$this->db->last_query();
Try this way:
$data = array(
'gid' => $this->input->post('gid'),
'name' => $this->input->post('name'),
'pic' => $this->input->post('pic'),
'link' => $this->input->post('link')
);
$gid = $data['gid'];
$this->db->where('gid',$gid);
$this->db->from('users');
$query = $this->db->get();
if($query)
{
//you can return query or you can do other operations here like insert the array data
//$this->db->insert('yourtable',$data);
return $query;
}
else
{
return FALSE;
}
You can try:
$get_gid = $this->db->query('SELECT * FROM users WHERE gid = '.$data['gid'].');
You just Forget after query $query->result().

how to build fluent query by array of criterias in Laravel

If I store in DB criterias what I want to use to build and filter queries - how to build query with Laravel Fluent Query Builder? Maybe you can give advice for refactoring this array for adding OR/AND to make complex filter by such conditions? Thanks!
For example if I read from database these criteria and made array:
$array = array(
'0' => array(
'table' => 'users',
'column' => 'name',
'criteria' => 'LIKE'
'value' => 'John'
),
'1' => array(
'table' => 'groups',
'column' => 'name',
'criteria' => 'NOT LIKE'
'value' => 'Administrator'
),
...
)
If you are really set on doing it your way, make a switch statement to set the flag for the index of the array
switch ($query)
{
case 0:
$this->get_data($x);
break;
case 1:
$this->get_data($x);
break;
case 2:
$this->get_data($x);
break;
default:
Return FALSE;
}
public function get_data($x){
$value = DB::table($array[$x]['table'])
->where($array[$x]['name'], $array[$x]['criteria'], $array[$x]['value'])
->get();
Return $value;
}
However, I would not advise this at all. Instead of storing the query filters in an array I feel you should just make them methods in your model. This will help with readability, modularity, and having reusable code.
public function get_user_by_name($name){
$user = DB::table('users')->where('name', 'LIKE', '%'.$name.'%')->get();
return $user;
}
public function get_group_by_name($name, $criteria = 'LIKE'){
$groups = DB::table('groups')->where('name', $criteria, '%'.$name.'%')->get();
return $groups;
}
http://laravel.com/docs/database/fluent

Codeigniter Update Multiple Records with different values

I'm trying to update a certain field with different values for different records.
If I were to use MySql syntax, I think it should have been:
UPDATE products
SET price = CASE id
WHEN '566423' THEN 49.99
WHEN '5681552' THEN 69.99
END
WHERE code IN ('566423','5681552');
But I prefer to use Active Record if it's possible.
My input is a tab delimited text which I convert into an array of the id and the desired value for each record:
$data = array(
array(
'id' => '566423' ,
'price' => 49.99
),
array(
'id' => '5681552' ,
'price' => 69.99
)
);
I thought this is the proper structure for update_batch, but it fails. Here's what I've tried:
function updateMultiple()
{
if($this->db->update_batch('products', $data, 'id'))
{
echo "updated";
}
else
{
echo "failed )-:";
}
}
And I get failed all the time. What am I missing?

Codeigniter - Sending a query result_arry() to a View for form_dropdown

I am using form_dropdown() and have a a problem below:
The form code is:
echo form_dropdown($level,$level_options,'1');
It works when I use
$level_options = array(
'1' => 'Grade 6',
'2' => 'Grade 7'
);
but not when I send a $data['levels'] from controller to view
For reference, the model database retrieve code is:
public function getAllLevelNames() {
$query = $this->db->query("SELECT level_description from levels ORDER BY level_description");
return $query->result_array();
}
The Problem
The problem is I get a dropdown pick list with:
0
Grade 6
1
Grade 7
The indexes are greyed out.
How do I get rid of the indexes?
Thanks in advance!
P.S.
I seem to have the form working now with a data['levels'] sent to the view. Now, the following code in my view seems to return "null" to my controller. Any ideas why please?
$level = array(
'name' => 'level',
'id' => 'level',
'value' => '1',
'maxlength' => '50',
'size' => '50',
'style' => 'width:50%',
);
$level_options = $levels;
echo "<p>Level: ";
echo form_dropdown($level,$level_options,'1');
Thanks!
You'll need to loop through your results_array and create a new array which is formatted correctly.
$query = $this->db->query("SELECT level_description from levels ORDER BY level_description");
$for_dropdown = array();
foreach ($query->result_array() as $row) {
$for_dropdown[$row->level_description] = $row->level_description;
}
return $for_dropdown;
Also I'm not sure how your levels table is structured, but usually you'll have an ID of some sort, which will be the primary key. If you do have that, you can include it in your query and have something like this instead:
$query = $this->db->query("SELECT id, level_description from levels ORDER BY level_description");
... // other code
$for_dropdown[$row->id] = $row->level_description;

Resources