Laravel: Create or update related model? - php

Please be gentle with me - I'm a Laravel noob.
So currently, I loop through a load of users deciding whether I need to update a related model (UserLocation).
I've got as far as creating a UserLocation if it needs creating, and after a bit of fumbling, I've come up with the following;
$coords = $json->features[0]->geometry->coordinates;
$location = new UserLocation(['lat'=>$coords[1],'lng'=>$coords[0]]);
$user->location()->save($location);
My issue is that one the second time around, the Location may want updating and a row will already exist for that user.
Is this handled automatically, or do I need to do something different?
The code reads like it's creating a new row, so wouldn't handle the case of needing to update it?
Update - solution:
Thanks to Matthew, I've come up with the following solution;
$location = UserLocation::firstOrNew(['user_id'=>$user->id]);
$location->user_id = $user->id;
$location->lat = $coords[1];
$location->lng = $coords[0];
$location->save();

You should reference the Laravel API Docs. I don't think they mention these methods in the "regular docs" though so I understand why you may have not seen it.
You can use the models firstOrNew or firstOrCreate methods.
firstOrNew: Get the first record matching the attributes or instantiate
it.
firstOrCreate: Get the first record matching the attributes or create it.
For Example:
$model = SomeModel::firstOrNew(['model_id' => 4]);
In the above example, if a model with a model_id of 4 isn't found then it creates a new instance of SomeModel. Which you can then manipulate and later ->save(). If it is found, it is returned.
You can also use firstOrCreate, which instead of creating a new Model instance would insert the new model into the table immediately.
So in your instance:
$location = UserLocation::firstOrNew(['lat'=>$coords[1],'lng'=>$coords[0]]);
$location will either contain the existing model from the DB or a new instance with the attributes lat and lng set to $coords[1] and $coords[0] respectively, which you can then save or set more attribute values if needed.
Another example:
$location = UserLocation::firstOrCreate(['lat'=>$coords[1],'lng'=>$coords[0]]);
$location will either contain the existing model from the DB or a new model with the attributes set again, except this time the model will have already been written to the table if not found.

Related

Laravel: Is always read the newly written data after calling save() method of ORM?

Code:
$item1 = Item::find(1);
$item1->foo = 1;
$item1->save();
$another_item1 = Item::find(1);
dd($another_item1->foo);//Is this value always 1?
My question:
Is always read the newly written data after calling save() method of ORM? In my example, Is $another_item1->foo always 1?
If the answer to question 1 is not, how could I ensure I read the newly written data from the database?
Is always read the newly written data after calling save() method of ORM?
No, there is no SELECT ran after a INSERT or UPDATE statement in this case.
In my example, Is $another_item1->foo always 1?
Based on your own comment, Yes.
If the answer to question 1 is not, how could I ensure I read the newly written data from the database?
$model->save();
// Reload the current model instance with fresh attributes from the database.
$model->refresh();
// OR
// Reload a fresh model instance from the database.
$fresh = $model->fresh();
I think you may be confused about the find() function. find() is used to fetch one or many models by its / their primary key(s). The return value will either be a single model, a collection or null if the record is not found.
If you are looking to lookup multiple rows you need to run Item::get();
Uses
$Item = Item::find(1); // returns model or null
$Items = Item::find(array(1, 2, 3)); // returns selected Items in collection
$Items = Item::get(); // Returns all in collection
https://laravel.com/docs/5.5/eloquent

Access the id after saving ActiveRecord model

i have an issue with access to the id of the ActiveRecord model in Yii2 framework. when i save the model i just created, i cannot acquire the id field of new object.
$house = new House;
$house->save();
$hid = $house->id;
$hid value is empty string ''.
the problem is that i am creating new model, so that i can pass the new id to thread process that is handling file moving while i create db rows. thread starts and after json slicing and array populating, the first insert fail on sql condition (where) statement.
i have researched many answers and they point to several flaws:
assignment of pk - i don't assign the new model id field (db handles the pk autoincrement), i receive the $_post body content through json (json has many fields that are not for bulk assignment into main model, so i deal with slicing the json data before $attibutes insert).
pk in model rules - i don't have id field in model rules array.
fault in the ActiveRecord class - i don't want to hack the base classes of the framework.
later in code i planned to link the models through relations, but i suppose that failed because of this error, so i also use $hid value to populate the foreign key fields in related models.
help. please.
Could be a validation problem try in this way
$house = new House;
if ($house->validate()) {
$house->save();
$hid = $house->id;
} else {
$errors = $house->errors;
var_dump($errors)
}
If you see the result of var_dump the your validation fails (eg: some required fields .. ) and you need change the proper validation rules in your House Model ..
Otherwise you cant try with
$house->save(false); //this way the validation is not executed
(use save(false) only for debugging porpose)
thank you scaisEdge and Alex. i have forgot to check the not null columns of the db. yii2 gii module generated the model according to the db schema and i missed the rules fields of the model. i didn't need validation since i was just generating empty row (just pk).
this is the code that passes:
$house = new House;
$house->name = 'name'; [field set as required in model rules array]
$house->description = 'description'; [field set as required in model rules array]
$house->save();
$hid = $house->id;
convention and configuration, pretty neat.

PHP/Laravel - Assign new model to a fetched one

Let's say I make a call to my User and return the following:
$user = User::with('permissions')->find(1);
I expect to get a user with $user->permissions being the permission of the user.
Next, I create and assign some new permissions to this user by:
// Say Input::all() contains an array of new permissions I want to add
// I am using Underscore PHP here
$newPermissions = Arrays::each(Input::all(), function($permission) {
$new = new Permission($permission);
$user->associate($new);
return $new;
});
Now I want to update the $user and return them back:
// This does NOT work (it returns the original $user->permissions)
return $user->permissions = $newPermissions;
// But this DOES work
unset($user->permissions);
return $user->permissions = $newPermissions;
Is this a PHP thing or Laravel thing? And what can I do? (btw, even if I say $user->permissions ='anything, text, string, or object doesnt work!' nothing happens).
It looks like permissions is a relationship, not a simple value that you can assign values to. I'd recommend reading the documentation on relationships, eager loading, and on inserting related models.
That last one, in particular, is what you're trying to do. Instead of assigning a value to $user->permissions, you need to update the relationship with either the save, associate, or attach methods, depending on the type of relationship.

add a row to a model

I want to fill an empty model and save the model in a blob field for later use. My issue is i can not find how to add anouther row to the empty Model.
this works:
$test = LineItem::model();
$test->item_id = '2';
This does not work
$test->1->item_id = '3';
or
$test->item_id[1] = '3';
i have tried looking in the Yii documentation but i was unable to find an answer.
Thanks
Clarification
Im trying to create a false table using the model of a real table. I'm working on an invoicing system and i don't want to right the line items or invoice body information to the DB until it is "closed". Instead i want to fill the corresponding models that will then be serialized and stored in a BLOB field. Once the invoice is finished the data will be written to the table.
You should use
$test = new LineItem;
instead of
$test = LineItem::model();
for INSERT queries. And after setting properties
$test->save();
And so in every iteration.

Copy a model to another database in symfony 1.4

Using Symfony 1.4 and doctrine I'd like to save a retrieved model to a different database connection:
retrieve model from master-database
change database connection to slave-database
save the model to the slave-database
I have the 2 connections defined in databases.yml.
here in pseudo-code:
$model = [retrieved from master-database];
$slaveConnection = Doctrine_Manager::getInstance()
->getConnection('slave-connection');
$model->save($slaveConnection);
If I create a new model, $model=new model(); the "code" above successfully saves the model to the slave-connection.
What is going wrong?
According to the Symfony log, Symfony recognizes the model as existing and issues an update (instead of an insert).
UPDATE model SET updated_at = '2011-10-21 17:37:32' WHERE id = '1';
Although Symfony is using the correct database connection ('slave-connection'), the update fails because the model isn't present in the slave-database, yet.
And the insert into the slave-database should use all values of the model, not only the changed ones, too.
Anyone can point me to the right direction to save an existing model to a different database?
edit with my solution.
Thanks samura!
Just some additions:
After performing deep copy Symfony saved a new id. But I wanted to really clone the model object to the slave db and so, I had to modify the id.
That caused unique constraint exceptions, so I had to delete first. So this is it:
$id = $model->getId();
$slaveConnection->execute("delete from modeltable where id=".$id);
$model_copy = $model->copy(true); # deep copy
$model_copy->setId($id);
$model_copy->save($slaveConnection);
hope this helps if someone else stumbles.
You could use the public function copy($deep = false) method of the Doctrine_Record class.
$model = [retrieved from master-database];
$slaveConnection = Doctrine_Manager::getInstance()
->getConnection('slave-connection');
$model_copy = $model->copy(true); # deep copy
$model_copy->save($slaveConnection);

Categories