Codeigniter 4 update into database with save() - codeigniter

I tried to update into my database by using save(). As my primary key is an auto increment id, it creates a new row when I tried to use save().
I tried to use save($game_id, $newData) for testing and I got error message.
Below is my code :
Table add_game
id | game_id | user_id | ign | acc_id
1 | 3 | 1 | ignA | accA
2 | 3 | 1 | ignB | accB
3 | 3 | 1 | ignC | accC
Model
protected $table = 'add_game';
protected $allowedFields = [
'user_id',
'game_id',
'ign',
'acc_id'
];
Controller
$newData = [
'user_id' => session()->get('user_id'),
'game_id' => $game_id,
'ign' => $this->request->getVar('ign'),
'acc_id' => $this->request->getVar('acc_id')
];
$model->save($newData); // currently it keeps creating new record
$model->save($newData); // return me error message
How do I update my existing records? I want to update them base on my user_id as well as game_id. Thanks in advance guys!

you need to have a primary key defined to make save() work for updating. In your example this primary key should/must be id:
save()
This is a wrapper around the insert() and update() methods that handle
inserting or updating the record automatically, based on whether it
finds an array key matching the $primaryKey value:
as you don't mention the id, obviously a new record is inserted and not updated.
in order to update, you need to supply that unique $primaryKey:
// Performs an update on id=3, since the primary key, 'id', is found.
$newData = [
'id' => 3,
'user_id' => session()->get('user_id'),
'game_id' => $game_id,
'ign' => $this->request->getVar('ign'),
'acc_id' => $this->request->getVar('acc_id')
];
$model->save($newData);
no need to repeat last line ($model->save($newData);), that's always calling for trouble/error
from the CI 4.x docs: Saving Data
an important read to understand the underlying SQL query you find here: INSERT ON DUPLICATE KEY UPDATE and https://mariadb.com/kb/en/insert-on-duplicate-key-update/ among many others

in codeigniter 4 save() method can do insert and update depend on data you provide
in your table if u want to update to add_game table u just add the id of data you want to update
$newData = [
'id' => 1 //add data id here
'user_id' => session()->get('user_id'),
'game_id' => $game_id,
'ign' => $this->request->getVar('ign'),
'acc_id' => $this->request->getVar('acc_id')
];
$model->save($newData);

I solve this problem for me too..
If u have custome 'id' name, u must define it with:
$primaryKey ="custom_id"
$useAutoIncrement = false
and you must include your custom_id in AllowedField :
$allowedFelds = [
"custom_id", // you must include it
"foo",
"bar"
]

Related

Laravel how to return virtual generated column on model create

I have the following table:
| id | number_one | number_two | number |
| 1 | 10 | 20 | 10020 |
Where number is a virtual created combination of number_one and number_two. Now when accessing a row the Laravel model also does return the number as expected.
Number::find(1); // returns [ id => 1, number_one => 10, number_two => 20, number => 10020]
However when using
Number::create(['number_one' => 10, 'number_two' => 21]);
It only returns
[number_one => 10, number_two => 21, id => 2]
So my question is how can I make it return the following on create:
[ number_one => 10, number_two => 21, number => 10021, id => 2]
And yes I could use a additional query or a getNumberAttribute method but would like to avoid this
Since create is causing an INSERT statement, there isn't a SELECT happening to get the fields from the table. You will notice that if you have a table that has default values and you don't pass those fields you also won't have them in your model.
To get these fields you would need to do a SELECT. You can call refresh on the Model instance to have its attributes and relationships refreshed from the database or you can also call fresh to get a new instance from the database (basically a find). [both methods will return a Model instance]
$model = Model::create([...])->refresh();
// or
$model = Model::create([...])->fresh();
Another option is to set the number attribute inside the created Eloquent event.
For example, if your table is created like this:
CREATE TABLE `numbers` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`number_one` BIGINT(20) NOT NULL,
`number_two` BIGINT(20) NOT NULL,
`number` BIGINT(20) AS (1000 * number_one + number_two) VIRTUAL,
PRIMARY KEY (`id`)
)
The virtual column is generated with the SQL expression (1000 * number_one + number_two).
You can do this in your created event.
class Number extends Model
{
protected static function booted()
{
self::created(function (self $model) {
$model->number = DB::scalar('SELECT 1000 * ? + ?', [$model->number_one, $model->number_two]);
});
}
}
I've used a really basic example, but you could be using very complex sql to generate the number column and this answer would still work. It's not depending on finding the right way to translate SQL to PHP. You just need to be mindful of the bindings.
Granted, it's a bit more boilerplate than using fresh() or refresh(), but it should achieve the same thing, without having to rehydrate an entire model.

remove row from pivot table in laravel

I have tables (projects, roles, scopes, shifts, users) and a table (project_role_scope_shift_user) as pivot.
I want to delete specific row like:
[project_id = 1 , scope_id = 2 , ... ]
However, detach method doesn't work correctly.
$user->roles()->detach(role) !not working => removes all rows of that role
$user->roles()->detach(role, ['project_id' => $projectId, 'scope_id' => $scopeId, 'shift_id' => $shiftId]); => this does not work too, removes all rows of that role
Try this :
$user->roles()->wherePivot(['project_id' => $projectId, 'scope_id' => $scopeId, 'shift_id' => $shiftId])->detach();

updateOrCreate() update null data if existed in database and only inserted new record

id_branch is PK, id_item is PK&FK
$id = B;
$id_selected = A;
$from_category= Category::where('id_branch', $id_selected)->get();
foreach ($from_items as $from_item) {
$test = Category::updateOrCreate(['id_branch' => $id,'id_item'=>$from_item->id_item], ['remarks' => $from_item->remarks]);
}
For example, user will select which branch category record need to be copied to the current branch category. After that will update or insert the record from the branch category selected to current one. Copy branch category A record to branch category B. If exist then update else insert. I able to insert the record but when update the value will be null. Anything wrong with my code?
updateOrCreate method take 2 parameters the first one is the conditions and the second one is the data to update or create.
In your case if the ID not exists in the branch, it will create a new row with the given data but you didn't pass the id_branch and id_item in the second parameter, I guess to fix this issue you should write the code like the following
$test = Category::updateOrCreate(
[
'id_branch' => $id,
'id_item'=>$from_item->id_item
],
[
'id_branch' => $id,
'id_item'=>$from_item->id_item,
'remarks' => $from_item->remarks
]
);

pivot table with multiple columns in Eloquent

Here's my scenario:
I have a Event model and a Stage model, a event can have multiple stages and a stage could be assigned to multiple events. So Many-to-many. The thing is, a stage has a sort_order, and that sort_order could be different in each event. That's why I added the sort_order into the pivot table instead in, for example, the stage table.
table: events_stages
| event_id | stage_id | sort_order |
------------------------------------
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 1 | 5 | 3 |
The thing is when I'm going to relate the Stage with the events its in,
I'm doing something like in the StageController:
sending a post with events: [1,2,3] and sort_order: [1,1,2]
$relatedEvents = array();
foreach ($request->events as $key => $event)
{
$relatedEvents[] = array(
'event_id' => $relatedEventId,
'sort_order' => $request->sort_order[$key]
);
}
$stage->events()->sync(
$relatedEvents
);
but rely simply in the order of the post, doesn't seem like a really good idea.
Does anyone have a nicer solution?
Thanks!
Sometimes is better to create another model (and use it as a pivot) rather than use pivot table itself. You have more control. I'm not sure what exactly you want to achieve.

Insert array on multiple rows

I have a multiple select:
Form::select('color', array('1' => 'Red', '2' => 'Blue', '3' => 'Green', ... ), null, array('multiple'));
How can I insert these values into a table on separate rows, like this:
id | user_id | color
----------------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 1 | 4
5 | 2 | 1
6 | 2 | 3
In the above example, user with an id of 1 selected 4 different values in the select and each was inserted on a separate row.
I have this working in this way:
foreach (Input::get('tags') as $key => $value)
{
$user_color = new UserColor;
$user_color->user_id = $user->id;
$user_color->color = $key;
$user_color->save();
}
Is there a better way of doing this? It seems odd using a foreach loop when it feels like Laravel should have some sort of built-in method of inserting multiple values on multiple rows.
As Laravel doc provided,
You may also use the sync method to attach related models. The sync
method accepts an array of IDs to place on the pivot table. After this
operation is complete, only the IDs in the array will be on the
intermediate table for the model:
In this case,
$colors = Input::get('tags');
$user->colors()->sync($colors);
Please make sure to set relation in your User model :
public function colors()
{
return $this->belongsToMany('Color');
}
You can also use attach method when you parameter is not array. To more clear, Here is difference between attach and sync.

Resources