欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

laravel save update one to one relationship 保存或更新一对一的关联 有大用

Ask Question

User has one (or zero) Company, and a Company belongs to one and only one User. I try to save a company for a user but it adds a new entry in database every time I re-trigger the save method. It's a one to one relation, so I though save method on User.

So Company has one method user():

public function user() {return $this->belongsTo(User::class, 'user_id');}

And User has one method company():

public function company() {return $this->hasOne(Company::class, 'user_id');}

I'm trying to save (so create or update) a user's company like this (in a Controller):

$company = new Company();$company->name = 'Test';User::findOrFail(1)->company()->save($company);

First time I run this code it creates the entry in database (OK), but second time it adds a new entry for the same user (Not OK). I thought it will only update the database entry.

Is it a glitch (or something I don't understand in one to one relation) in Laravel or am I doing something wrong? (I think and hope it's the second purpose)

shareimprove this questionasked Sep 10 '15 at 9:32rap-2-h5,84133082
 
   Check this answer, it might help you: stackoverflow.com/a/20764237/5122070 – Mihkel Allorg Sep 10 '15 at 9:39   Thanks, but I'm not sure it really helps for this particular problem. My problem is more specific. It's about saving (creating or updating) directly in relation – rap-2-h Sep 10 '15 at 9:46

3 Answers 正确答案

Creating and updating need to treat differently. So check the existence of company attribute first.

$user = User::with('company')->findOrFail(1);
if ($user->company === null)
{
    $company = new Company(['name' => 'Test']);
    $user->company()->save($company);
}
else{
    $user->company->update(['name' => 'Test']);
}

Note that hasOne() does not guarantee that you will have one-to-one relationship, it just telling Eloquent how to create query. It works even you have multiple Company refer to same User, in such case when you call $user->company you will get first Company in the result data set from database.

shareimprove this answeranswered Sep 10 '15 at 9:57PaePae48238
 
   Ok thanks. "Note that hasOne() does not guarantee that you will have one-to-one relationship, it just telling Eloquent how to create query" -> Very helpful, It's clear enough now. – rap-2-h Sep 10 '15 at 10:10
$user = User::findOrFail(1);$company = $user->company ?: new Company;$company->name = 'Test';$user->company()->save($company);
shareimprove this answeranswered Sep 10 '15 at 10:14MstfAsan2,7571927
 
   Like this way too! Quick and clean! +1 :) – rap-2-h Sep 10 '15 at 10:19

You are doing wrong here

$company = new Company();$company->name = 'Test';User::findOrFail(1)->company()->save($company);

You aren't telling which row to update

So your code has to be modified like this

$companyId is your company to update

$company = new Company();$company->name = 'Test';User::findOrFail(1)->company()->find($companyId)->save($company);
shareimprove this answeranswered Sep 10 '15 at 9:38ARIF MAHMUD RANA2,92821534
 
   But I don't know the company ID at this point, I just want to update the company values for one user (without having to care of the possible previous one). Maby it's impossible, but it seems weird that save() does not care of the relation type (here, it's harcoded on one to one) – rap-2-h Sep 10 '15 at 9:44   @rap-2-h you have a form where you are passing company info. If the user has company info then there will be an id if not then you left id to empty and when you post the form you get the id. Now if there is id value you find and update or you insert. – ARIF MAHMUD RANA Sep 10 '15 at 9:52   Ok thanks. I know it's a solution to use hidden field in a form, but my question is should we do such things? Laravel's knows that it can be only one company for one user, so why does it add a new company when I save it. It's seems weird (as if it does not respect what is declared), so I think I'm doing the wrong way. – rap-2-hSep 10 '15 at 10:08

来自  https://stackoverflow.com/questions/32498142/saving-one-to-one-relation-in-laravel




 

I have 2 tables that are in one to one relationship:

tours :

id|title|content

featured_image:

id|tour_id|name|path

My models FeaturedImage.php:

class FeaturedImage extends Model{protected $fillables = ['name','path','tour_id',];public function tour()
 {
  return $this->belongsTo('App\Tour');
 }}

Tours.php

class Tour extends Model{protected $fillables = ['title','content',];public function featuredImage()
 {
  return $this->hasOne('App\FeaturedImage');
 }}

I'm trying to update the featured_image table when new image for tour is uploaded:

  1. update path column in featured_image table with new file's path

  2. Delete the old image

Below is my method for updating featured_image's path column:

// update featured image       if ($request->hasFile('featured_image')) {$featured_image= new FeaturedImage;// add the new photo$image = $request->file('featured_image');$filename = $image->getClientOriginalName();$location = 'images/featured_image/'.$filename; //dd($location);Image::make($image)->resize(800, 400)->save($location);$oldFilename= $tour->featuredImage->path;// update the database$featured_image->path = $location;// Delete the old photoFile::delete(public_path($oldFilename));}

The above code sucessfully deletes the old image and uploads the new, but it fails to update the path column. I ran dd($location);, it gives the path of the new image but doesn;t save in the db column.

shareimprove this questionasked Dec 16 '16 at 17:13Zachary Dale19611
 
   $featured_image->save() after you assign the new location to the path property. $featured_image->path = $location; $featured_image->save(); – Wild Beard Dec 16 '16 at 17:21   I dit that and got error SQLSTATE[HY000]: General error: 1364 Field 'tour_id' doesn't have a default value – Zachary Dale Dec 16 '16 at 17:25

1 Answer 正确答案

  

You should save relation like this:

$featuredImage = $tour->featuredImage;
$featuredImage->path = $location;
$tour->featuredImage()->save($featuredImage);

https://laravel.com/docs/5.3/eloquent-relationships#the-save-method

shareimprove this answeranswered Dec 16 '16 at 17:23Alexey Mezenin50.1k52344
 
1 Thanks it really worked. – Zachary Dale Dec 16 '16 at 17:28

来自  https://stackoverflow.com/questions/41189301/update-in-one-to-one-relation-table-laravel

 

How to save Eloquent Model with relations in one go?


defiant posted 3 years ago

When saving a model with relations we do something like this;

$ship = new Ship;

$ship->name = 'Enterprise';
$ship->registry = 'ncc-1701D';

$ship->save();

Then for the relation;
$captain = new Captain;

$captain->name = 'jean Luc Picard';

$ship->captain()->save($captain);

Now the problem is when something goes wrong in the related model ($captain in this example) we have a database entry without a related table.

So the question is there a way to save both in one go?

Any ideas?

Ilyes512 replied 3 years ago

Transactions may be what you need:
See the Laravel documentation about transactions here: Database Transactions
Here is an example on SO: Laravel Eloquent ORM Transactions

evandertino replied 3 years ago

@Ilyes512 this has nothing to do with transactions

You have to find the ship first to which you want to save the captain in

This is to ensure that when you save the captain, the records of the ship found are also saved with it. e.g) ship_id in the captains table

So doing something like this, should work

$ship->find(1)->captain()->save(new Captain(array('name' => 'jean Luc Picard')));
sjdaws replied 3 years ago

I have a package that does this, but it was more designed for AngularJS objects. It basically takes and array and saves it.

http://github.com/lakedawson/vocal

defiant replied 3 years ago

@evandertino

What you say kinda makes sense. But suppose you have to create the ship at the same time with captain. In your case you save a ship first then you save the captain with a ship model.

However if ship is saved without problem but something goes wrong while saving captain. We will have a ship without a captain. I want to prevent this.

evandertino replied 3 years ago

Oh yeah, now i get it...for that case now @Ilyes512 is right, you need transactions. So that when something goes wrong saving the captain everything is automatically rolled back.

But again, it cannot be possible at any given time for both records to be saved at the same time, the ship has to be saved first before the captain. It makes sense because, even in real life, a ship has to exist before the captain pops into it.

So something like this, is what i am thinking. I have not tested it but i think it should work

$ship = new Ship;
$ship->name = 'Enterprise';
$ship->registry = 'ncc-1701D';

$captain = new Captain;
$captain->name = 'jean Luc Picard';

DB::transaction(function() use ($ship, $captain) {
   $ship = $ship->save(); //Ship Exists First
   Ship::find($ship->id)->captain()->save($captain);//Captain is saved to existing ship
});

This way you will be guaranteed in both ways while saving, for instance in the above code:-

  • If something goes wrong when saving the ship, No ship will be saved and therefore no captain will be saved

  • If something goes wrong when saving the captain, The ship will be saved and No captain will be saved

To get more control when using these transactions, try{}catch{} blocks should do like this

DB::beginTransaction(); //Start transaction!

try{
   //saving logic here
   $ship = $ship->save();
   Ship::find($ship->id)->captain()->save($captain);
}
catch(\Exception $e)
{
  //failed logic here
   DB::rollback();
   throw $e;
}

DB::commit();
kirkbushell replied 3 years ago

The original problem unfortunately isn't really solvable. Even if you can save all at once, if the first save fails, it fails. You CAN, however - use transactions to get around this, as was originally provided. Transaction statements will let you know if something is wrong with any of the queries, before you execute them. Just wrap it in a transaction block:

DB::transaction(function() {
    // do stuff
});

If any statement fails, it'll rollback any queries. So what you can do, is basically step through the queries you need to do and resolve it.

That all said and done, I'm currently working on a pull request that will allow for batch saves for related models.


来自  https://laravel.io/forum/06-11-2014-how-to-save-eloquent-model-with-relations-in-one-go


普通分类: