I'm asking this question in order to find the best practice to do it.
DB::table('owners')
->where('property_id',$id)
->update(array('person_id'=>$owner));
The problem is that in the table owners might not have a row to update. In that occasion i need to make an INSERT INTO instead of UPDATE.
My problem is that i have to run 2 queries each time, one for checking if the row already exists, and one more to update or insert into. Is it right to run 2 queries each time? Is there a better way to achieve that? I need to keep the queering processes fast for the user.
UPDATE: The table owners is a middle table of a many to many relationship. Unfortunately i cannot use ON DUPLICATE KEY.
well you could try to use firstOrCreate method of Laravel to check if user exists. After that retrieve the user object and pass it to an update function else if the user is not found firstOrCreate method will take care of you as it will create a new user with the data you will provide and will auto increment last user + 1 id.
There is also the option to use firstOrNew which will check if an instance exists based on the array values you passed and if no match is found it will auto create a new instance of the model you are handling for further manipulation.
Here is example with firstOrNew
Example Controller file.
public function getFirstUserOrNew($email)
{
$user = User::firstOrNew(['email' => $email]);
if($user)
{
$this->UpdateUser($user);
}
else
{
$this->CreateUser($user);
}
}
public function UpdateUser(User $user)
{
//Do update stuff
}
public function CreateUser(User $user)
{
//Do create stuff
}
P.S - I'm from Greece, if you want to discuss anything in native language send me a PM :)
EDIT:
Thanks to #Pyton contribution It seems you can also use an updateOrCreate method as it is explained here.
If you want to Update or Insert row You can use updateOrCreate
$owner = Owner::updateOrCreate(['property_id' => $id], ['person_id'=>$owner]);
Related
I've a simple post form. Records are saved to database with updateOrCreate function.
I've following records to be stored,
user_id
product_id
review
rating
After submitting form, user_id and product_id are used to check if record exist or not. If exist, record will be updated otherwise new record created with the function.
public function updateOrCreate($input)
{
return $this->model->updateOrCreate(['product_id'=> $input['product_id'],'user_id'=>$input['user_id']],['rating'=>$input['rating'],'review'=>$input['review']]);
}
Is there any built in method to find new record created or existing record updated ?
Any kind of suggestion is appreciated.
There is a public property on Eloquent models called $wasRecentlyCreated. It is set when an insert is performed. And it will only be true after an insert for the same request; not for consecutive requests. If an update has been performed, it will be false.
Be aware that the property is also only true for the exact same model instance. If you reload the model with Product::find($id), it will be false. What happens if you call refresh() on a model, is unfortunately unclear to me.
You need to determine if the model or given attribute(s) have been modified. And wasChanged() method will help to identify if the model is updated or not.
$model = $this->model->updateOrCreate(['product_id'=> $input['product_id'],'user_id'=>$input['user_id']],['rating'=>$input['rating'],'review'=>$input['review']]);
// It will return true if the record have been modified
$wasChanged = $model->wasChanged();
if ( $wasChanged ) {
// updated
}
else {
// created
}
Hope this help you.
I assume that this should all be in one query in order to prevent duplicate data in the database. Is this correct?
How do I simplify this code into one Eloquent query?
$user = User::where( 'id', '=', $otherID )->first();
if( $user != null )
{
if( $user->requestReceived() )
accept_friend( $otherID );
else if( !$user->requestSent() )
{
$friend = new Friend;
$friend->user_1= $myID;
$friend->user_2 = $otherID;
$friend->accepted = 0;
$friend->save();
}
}
I assume that this should all be in one query in order to prevent
duplicate data in the database. Is this correct?
It's not correct. You prevent duplication by placing unique constraints on database level.
There's literally nothing you can do in php or any other language for that matter, that will prevent duplicates, if you don't have unique keys on your table(s). That's a simple fact, and if anyone tells you anything different - that person is blatantly wrong. I can explain why, but the explanation would be a lengthy one so I'll skip it.
Your code should be quite simple - just insert the data. Since it's not exactly clear how uniqueness is handled (it appears to be user_2, accepted, but there's an edge case), without a bit more data form you - it's not possible to suggest a complete solution.
You can always disregard what I wrote and try to go with suggested solutions, but they will fail miserably and you'll end up with duplicates.
I would say if there is a relationship between User and Friend you can simply employ Laravel's model relationship, such as:
$status = User::find($id)->friends()->updateOrCreate(['user_id' => $id], $attributes_to_update));
Thats what I would do to ensure that the new data is updated or a new one is created.
PS: I have used updateOrCreate() on Laravel 5.2.* only. And also it would be nice to actually do some check on user existence before updating else some errors might be thrown for null.
UPDATE
I'm not sure what to do. Could you explain a bit more what I should do? What about $attributes_to_update ?
Okay. Depending on what fields in the friends table marks the two friends, now using your example user_1 and user_2. By the example I gave, the $attributes_to_update would be (assuming otherID is the new friend's id):
$attributes_to_update = ['user_2' => otherID, 'accepted' => 0 ];
If your relationship between User and Friend is set properly, then the user_1 would already included in the insertion.
Furthermore,on this updateOrCreate function:
updateOrCreate($attributes_to_check, $attributes_to_update);
$attributes_to_check would mean those fields you want to check if they already exists before you create/update new one so if I want to ensure, the check is made when accepted is 0 then I can pass both say `['user_1' => 1, 'accepted' => 0]
Hope this is clearer now.
I'm assuming "friends" here represents a many-to-many relation between users. Apparently friend requests from one user (myID) to another (otherId).
You can represent that with Eloquent as:
class User extends Model
{
//...
public function friends()
{
return $this->belongsToMany(User::class, 'friends', 'myId', 'otherId')->withPivot('accepted');
}
}
That is, no need for Friend model.
Then, I think this is equivalent to what you want to accomplish (if not, please update with clarification):
$me = User::find($myId);
$me->friends()->syncWithoutDetaching([$otherId => ['accepted' => 0]]);
(accepted 0 or 1, according to your business logic).
This sync method prevents duplicate inserts, and updates or creates any row for the given pair of "myId - otherId". You can set any number of additional fields in the pivot table with this method.
However, I agree with #Mjh about setting unique constraints at database level as well.
For this kind of issue, First of all, you have to enjoy the code and database if you are working in laravel. For this first you create realtionship between both table friend and user in database as well as in Models . Also you have to use unique in database .
$data= array('accepted' => 0);
User::find($otherID)->friends()->updateOrCreate(['user_id', $otherID], $data));
This is query you can work with this . Also you can pass multiple condition here. Thanks
You can use firstOrCreate/ firstOrNew methods (https://laravel.com/docs/5.3/eloquent)
Example (from docs) :
// Retrieve the flight by the attributes, or create it if it doesn't exist...
$flight = App\Flight::firstOrCreate(['name' => 'Flight 10']);
// Retrieve the flight by the attributes, or instantiate a new instance...
$flight = App\Flight::firstOrNew(['name' => 'Flight 10']);
use `firstOrCreate' it will do same as you did manually.
Definition of FirstOrCreate copied from the Laravel Manual.
The firstOrCreate method will attempt to locate a database record using the given column / value pairs. If the model can not be found in the database, a record will be inserted with the given attributes.
So according to that you should try :
$user = User::where( 'id', '=', $otherID )->first();
$friend=Friend::firstOrCreate(['user_id' => $myId], ['user_2' => $otherId]);
It will check with both IDs if not exists then create record in friends table.
I have a situation to update the row based on id and return the complete row. I can do it in two queries but i want to do it in one query only. I have write this...
$result= DB::table($this->table)
->whereRaw($where['rawQuery'], $where['bindParams'] ? $where['bindParams'] : array())
->increment($updateField);
if($result){
return DB::table($updateTable)
->select('id')
->where('campaign_id',$where['bindParams'])
->where('date',date("Y-m-d"))
->get();
}else{
throw Exception("Error in fetching data");
}
I copied this from what you commented in your question:
No i want to return the id of the updated row or complete row if possible
If you want to return the ID of the just updated 'row' you're talking about. You can use Eloquent to accomplish this.
You can use a route like this:
Route::put('demo/{id}', Controller#update);
Then in your update function in your controller you can get the ID.
Find the model with $model = Model::find(id);
do things with the data you get.
Like $model->name = Input::get('name');
Then use $model->save();
After saving you can do $model->id;
Now you get back the ID about the row you just updated.
Refer back to this question:
Laravel, get last insert id using Eloquent
But any way it'll always be at least 2 queries (a SELECT and an UPDATE in MySQL, however you do it)
You can check Laravel Eloquent if you want a "cleaner" way to to this.
I'm working in a Webapp and I have a problems to work with a intermediate table, these are my tables in mysql:
User:
Integer:id
String:name
String:email
String:phone
Exercise:
Integer:id
String:name
String:description
User_Exercise:
Integer:id
Integer:id_user
Integer:id_exercise
Integer:record
So, what I want to do is that when I create an exercise, it be created one row for each user with the exercise-id that I have created it before. Later the user could change his record in this exercise.
I have thought to create a model to handle the user_exercise's table but I don't know if there is some way to do this better or not.
So, There are some way to do this without create a new model?
PD: Sorry for my terrible english
You don't need a seperate model for User_Exercise
You can use $this->belongsToMany from base Model i.e., User
Note :
For insert process you can get the parent id by
$insertUser = User::create($userData);
then
$insertUser->id for taking the last insert id
And then to retrieve with respect to User_Exercise you shall use $this->belongsToMany from your User Model
Example
Have this in your User Model
public function getUser() {
return $this->belongsToMany('App\User', 'excercise_name', 'user_id', 'excercise_id')->select(array('exercise.id', 'excercise.name'));
}
And Get the data you need from any Controller like this
$userData = User::find($userId)->getUser;
I have a many to many relationship set up and working, to add an item to the cart I use:
$cart->items()->attach($item);
Which adds an item to the pivot table (as it should), but if the user clicks on the link again to add an item they have already added it creates a duplicate entry in the pivot table.
Is there a built in way to add a record to a pivot table only if one does not already exist?
If not, how can I check the pivot table to find if a matching record already exists?
You can also use the $model->sync(array $ids, $detaching = true) method and disable detaching (the second param).
$cart->items()->sync([$item->id], false);
Update:
Since Laravel 5.3 or 5.2.44, you can also call syncWithoutDetaching:
$cart->items()->syncWithoutDetaching([$item->id]);
Which does exactly the same, but more readable :)
You can check the presence of an existing record by writing a very simple condition like this one :
if (! $cart->items->contains($newItem->id)) {
$cart->items()->save($newItem);
}
Or/and you can add unicity condition in your database, it would throw an exception during an attempt of saving a doublet.
You should also take a look at the more straightforward answer from Barryvdh.
#alexandre Butynsky method works very well but use two sql queries.
One to check if cart contains the item and one to save.
To use only one query use this:
try {
$cart->items()->save($newItem);
}
catch(\Exception $e) {}
As good as all this answers are because I had tried them all, one thing is still left unanswer or not taken care of: the issue of updating a previously checked value (unchecked the checked box[es]). I do have something similar to the above question expect i want to check and uncheck features of products in my product-feature table (the pivot table). I am a newbie and I have realised none of the above did that. The are both good when adding new features but not when i want to remove existing features (i.e. uncheck it)
I will appreciate any enlightenment in to this.
$features = $request->get('features');
if (isset($features) && Count($features)>0){
foreach ($features as $feature_id){
$feature = Feature::whereId($feature_id)->first();
$product->updateFeatures($feature);
}
}
//product.php (extract)
public function updateFeatures($feature) {
return $this->features()->sync($feature, false);
}
or
public function updateFeatures($feature) {
if (! $this->features->contains($features))
return $this->features()->attach($feature);
}
//where my attach() is:
public function addFeatures($feature) {
return $this->features()->attach($feature);
}
Sorry guys, not sure is I should delete the question because having figure out the answer myself, it sounds a bit stupid, well the answer to the above is as simple as working #Barryvdh sync() as follows; having read more and more about:
$features = $request->get('features');
if (isset($features) && Count($features)>0){
$product->features()->sync($features);
}
There are some great answers posted already. I wanted to throw this one out here as well though.
The answers from #AlexandreButynski and #Barryvdh are more readable than my suggestion, what this answer adds is some efficiency.
It retrieves only the entries for the current combination (actually only the id) and it than attaches it if it does not exist. The sync method (even without detaching) retrieves all currently attached ids. For smaller sets with little iterations this will hardly be a difference, ... you get my point.
Anyway, it is definitely not as readable, but it does the trick.
if (is_null($book->authors()->find($author->getKey(), [$author->getQualifiedKeyName()])))
$book->authors()->attach($author);
I just had this problem and was able to solve it.
For future readers: instead of using atach() you can use
syncWithoutDetaching()
This will make sure you do not get any duplicates!
There are more alternatives to attach()
That might be usefull in the laravel documentation
Note that this is not for Laravel 4
$branch->permissions()->syncWithoutDetaching([1,2,3]);