Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.2] firstOrCreate will not create new db rows when a model has a mutator #14656

Merged
merged 9 commits into from
Aug 12, 2016
Merged

Conversation

miscbits
Copy link
Contributor

@miscbits miscbits commented Aug 6, 2016

fixes issue #14649

When using the firstOrCreate function, the attributes passed in will be used to search for the value in the database even if the model has mutators which will change the values that should be in attributes. This change fills a model and then runs the attributesToArray() method to get the true value of the attributes needed to see if this model is the first in the database.

@@ -233,6 +233,8 @@ public function findOrNew($id, $columns = ['*'])
*/
public function firstOrNew(array $attributes)
{
$attributes = $this->model->newInstance()->fill($attributes)->attributesToArray();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't fill() only works if we un-guard the model?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes if someone tries to use variables that aren't fillable, but if that's the case it should not create a new model anyway because those attributes are guarded

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not create a new model anyway because those attributes are guarded

Mass assignment exception doesn't occur when you call save(), it occur within fill() https://github.com/laravel/framework/blob/5.2/src/Illuminate/Database/Eloquent/Model.php#L439-L457

You might want to look at forceFill().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the newest commit. Instead of forceFill() I just moved the instance line to the top of the method
$instance = $this->model->newInstance($attributes);
This was already in the method beforehand so it should not change the behavior of the method and still fix the bug that was reported.

@GrahamCampbell GrahamCampbell changed the title firstOrCreate will not create new db rows when a model has a mutator [5.2] firstOrCreate will not create new db rows when a model has a mutator Aug 6, 2016
@antonkomarev
Copy link
Contributor

antonkomarev commented Aug 7, 2016

Accordingly firstOrCreate method. Why it's not just utilizing firstOrNew method to make an instance and just save it?

public function firstOrCreate(array $attributes)
{
    $instance = $this->firstOrNew($attributes);
    $instance->save();
    return $instance;
}

I understand that it's not related to this PR, but just noticed code duplication.

@fernandobandeira
Copy link
Contributor

👍

This is actually changing the behavior of the function, should it really go to 5.2?

@themsaid
Copy link
Member

themsaid commented Aug 7, 2016

How would this change handle the case of mutators with dynamic data?

public function setNameAttribute($value)
{
    $this->attributes['name'] = $this->title.'. '.$value;
}
$mutatedAttributes = $this->model->newInstance($attributes)->attributesToArray();

attributesToArray() applies the mutator having title = '' if it wasn't provided already which leads to unexpected results.

@taylorotwell
Copy link
Member

I think in general this makes sense as an idea but I don't agree with using attributesToArray here. I think you only need to do ->newInstance($attributes)->getAttributes()...

Running attributesToArray will return the get accessors on all the attributes, which may not be the expected behavior either. You only want to run the set mutators and then get the raw value of the attributes at that point, which getAttributes will give you.

@miscbits
Copy link
Contributor Author

miscbits commented Aug 8, 2016

@taylorotwell I was talking about the possibility of that in the bug report cause I was thinking about it today. I haven't had time to look/test that possibility. I'll send up a new commit momentarily to this PR.

@miscbits
Copy link
Contributor Author

miscbits commented Aug 8, 2016

@fernandobandeira good idea and will do.

@miscbits
Copy link
Contributor Author

miscbits commented Aug 8, 2016

@fernandobandeira nevermind. you're just sending in an ID for the find function, so why would this fix need to be implemented? (unless someone put a mutator on the id of course)

@miscbits
Copy link
Contributor Author

miscbits commented Aug 8, 2016

@themsaid I think the results would be expected really. It might not be if you forget you wrote that mutator but if a dev is aware of the mutators in their model class it should be apparent why results like that may come about. It's still better than the behavior that allows you to create more than one instance of the same object in your database.

@taylorotwell taylorotwell merged commit df2761f into laravel:5.2 Aug 12, 2016
tillkruss pushed a commit to tillkruss/framework that referenced this pull request Aug 30, 2016
…tator (laravel#14656)

* firstOrCreate will not create new db rows when a model has a mutator

* removed changes to firstOrCreate in Relationships

* fixed firstOrCreate mutating attribute twice

* replicated behavior of Builder before intial change. No mass assignment error thrown anymore

* fixed typo

* changed Builder edit to pass test. firstOrNew not creating null id when soft deleted user is called from instance

* white space issue

* addedAttributesToArray to declaration of mutatated Attributes

* use getAttributes instead of getAttributesArray
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants