Insert parent and child at once via Eloquent in Laravel 5.4 - php

I am trying to insert a two related objects via Eloquent and I get an Integrity constraint violation error on one of the tables. Here is what I am trying:
The representation of the relationship:
class DataParticipant extends Model
{
public function data_config()
{
return $this->hasOne('App\Models\DataConfig');
}
}
class DataConfig extends Model
{
public function data_participant()
{
return $this->belongsTo('App\Models\DataParticipant');
}
}
Then, I am trying the following:
$dataParticipant = new DataParticipant();
$dataParticipant->ip = // ip
$dataParticipant->code = // code
$dataParticipant->study_name = // study
// Prepare the config child.
$dataConfig = new DataConfig();
$dataConfig->config = // config via json_encode
// Store the child associated to the parent.
$dataParticipant->data_config()->save($dataConfig);
The error is on data_participant_id column, which belongs to the DataConfig model. My guess is that there is no id on which the two can be linked. I want to ask you:
How can I accomplish this without having to first save the DataParticipant then retrieve it from the database and then store the DataConfig on the resulted id?

First save the first model
$dataParticipant->save ();
Then save the second model
$dataParticipant->data_config()->save($dataConfig);
This won't retrieve the data from DB at all, and $dataParticipant id will be automatically set with the last insert id

Related

Laravel insert relationship in newly created model

I have a relationship set up between two models. The two models are set up like:
app/Character.php
public function characteristics() {
return $this->hasMany('App\Characteristics');
}
app/Characteristics.php
public function character() {
return $this->belongsTo('App\Character');
}
then in a controller I have a method to create a new character with a predetermined set of characteristics as follows:
app/Http/Controllers/CharacterController.php
public function newCharacter(Request $request) {
$character = new Character();
$characteristics = getCharacteristics($character->id);
// Save basic character stuff
$character->characteristics()->saveMany($characteristics);
$character->save();
}
The highlighted line is throwing an error because saveMany is not a function of Builder so how can I get the created character without having to do a find that would have to hit the database again?
I think you need to save the character model first, because if you're building a hasMany/belongsTo relationship your characteristics table must have a column for character_id, and when you do $character->characteristics()->saveMany($characteristics); Laravel will try to insert the ID from the parent model. And as per the code shared by you, you've just instantiated the model, by this point of time it doesn't have an ID associated with it. So you need to save the character model first and assuming the getCharacteristics() method is returning an array/collection of Characteristics Models, Following should work:
public function newCharacter(Request $request) {
$character = new Character();
$character->save();
$characteristics = getCharacteristics();
// Save basic character stuff
$character->characteristics()->saveMany($characteristics);
}
And to further clarify this for you, from the characteristics method in your Character model an instance of HasMany not a Builder is being returned, thus saveMany works here.

Saving nested model laravel in one go

I have create two tables
AREAS
-id
-areaName
SOCIETIES
-id
-area_id
-societyName
My relationship is defined like this
class Area extends Model
{
//
public function society()
{
return $this->hasMany('App\Society');
}
}
class Society extends Model
{
public function area()
{
return $this->belongsTo('App\Area');
}
}
Now I have created objects for both area and society
$area = new \App\Area();
$area->areaName = $input['areaName'];
$society = new \App\Society();
$society->societyName = $input['societyName'];
$society->area()->associate($area);
After this when I call "$society->save()" its not saving any record and giving error area_id cannot be null. I want to save society and its relation area in one go.
When I use "$area->save()" before associate its saving the record when calling "$society->save()". All I want to do is skip "$area->save()" and save both society and area in one go.
It is giving you the error because you don't have a record in the database with an id for it. You need to save the area record first and then associate it with the relation. I don't think it will work other wise, take a look at eloquent docs on laravel's website:
https://laravel.com/docs/5.1/eloquent-relationships

Save multiple records in multiple tables after save model in Yii

I have the followings tables:
tbl_project(id, description)
tbl_operation(id, project_id, name)
tbl_itemType(id, operation_id, name)
tbl_item(id, itemType_id, name, unit, price)
I wanna when i create a new project, it adds some operations in tbl_operation and then adds some itemTypes to tbl_itemType and then adds some items in tbl_item. How can i do it in afterSave() behavior of project's model?
I read the following link, but i don't know is it possible to do by this?
esaverelatedbehavior
just create a function in your ProjectModel
public function afterSave()
{
$operation_model = new Operation();
$operation_model->setAttributes($YOUR_DATA);
$operation_model->save(); // create your operation
// same goes for every other data you want to save
return parent::afterSave(); // keep the chain
}
You could make use of the relations. This approach will only work if the respective relation contains only the models to be saved. In your controller have
$project->operations = array(/*your operations*/);
In turn each operation model could also have the related itemTypes
$operation->itemTypes = array(/*itemTypes for this operation*/)
And lastly each itemType could have the related items.
And in your afterSave for operations have
public function afterSave() {
foreach ($this->operation as $op) {
$op->project_id = $model->id;
$op->save();
}
return parent::afterSave();
}
For the afterSave for the Operation and ItemType classes should in turn save the related ItemTypes and Items respectively.
Better use afterSave() function , i think it will work for you

Retrieving inserted IDs from saveAll() in CakePHP

Using saveAll() to save multiple records in CakePHP, I am able to save them successfully in a table. But the problem arises while retrieving the IDs of those saved rows. LastInsertID() returns only a single last ID here. How can I get all the last inserted IDs which I have inserted using saveAll()?
afterSave function is called after each individual save in a saveAll execution, so you could do:
In your AppModel
class AppModel extends Model {
var $inserted_ids = array();
function afterSave($created) {
if($created) {
$this->inserted_ids[] = $this->getInsertID();
}
return true;
}
}
You can place this code into any model and it should work fine. Then to return the IDs after the saveAll in your controller, you would do so like this:
if($this->Post->saveAll($posts)) {
$post_ids=$this->Post->inserted_ids; //contains insert_ids
}
Hope it helps

Custom logic for updating a row

I have this table class:
class Songs extends Zend_Db_Table_Abstract
{
protected $_name = 'songs';
protected $_primary = 'song_id';
protected $_rowClass = 'Song';
}
And a class that extends the class above with some custom logic.
class Song extends Zend_Db_Table_Row_Abstract
{
protected function _insert()
{
print_r($this);
// $this does exist
}
protected function _update()
{
print_r($this);
//$this does not existing when updating a row, why not?
}
}
My problem is that when I'm inserting a new row I can use $this in my custom logic.
$row->save(); // $this exists in _insert()
But it doesn't exist when I'm trying to update a row.
$myRow->update($data, $where); // $this does not exists in _update()
Why does $this not exist when I want to do some custom logic before updating a row?
To update a row, you don't use:
$myRow->update($data, $where);
You use:
$myRow->save();
But trying to use update() on a row object should throw an exception.
So I'm guessing you're actually calling the update() function on the table object, and not the row object.
$songs = new Songs();
//...
$songs->update($data, $where);
At that point the row object is never even used, the query is simply generated from the $data array and the $where clause.
If you want to use the custom _update() method you would need to do something like:
$songs = new Songs();
$song = $songs->find($id)
//change some data
$song->save();
Of course is also perfectly valid to add custom logic at the table level, and should be noted while calling an update or insert from the table object does not use the row object, calling save() on the row object proxies the table object.
For example, from the Zend_Db_Table_Row _doInsert() function:
$this->_insert();
//...
$primaryKey = $this->_getTable()->insert($data);
So if you have custom logic that you want to use every time you update a row (whether you update from the table object or the row object), it should be put into the table object.
From the Zend_Db_Table_Row docs:
If you need to do custom logic in a specific table, and the custom logic must occur for every operation on that table, it may make more sense to implement your custom code in the insert(), update() and delete() methods of your Table class. However, sometimes it may be necessary to do custom logic in the Row class.

Categories