Difference between [attributes:protected] and [original:protected] - laravel

Please could anyone explain to me a difference between [attributes:protected] array and [original:protected] array in laravel when using print_r to an array?

When Model reads data from table, arrays 'original' and 'attribute' contains same data. When you change the attribute value (ex $user->name='John'), the change is reflected only on the 'attributes' array but 'original' remains same. (hence the name).
When update() on a model is called, method checks what has changed comparing two arrays and construct query only for changed fields. Thus, in the case of $users->name change Laravel will not create this code:
UPDATE users set name = 'John', password = 'pass', email = 'email' where id = 1
but this:
UPDATE users set name = 'John' where id = 1
This may not be the only way Eloquent uses 'original' array. I found clockwork helpful when you need to see what's going on under the hood of Eloquent.

Related

Laravel updateOrCreate ignoring some fields

I've been pulling my hair for hours now trying to find the problem with this lines of code:
ContentSetting::updateOrCreate(
['content_id' => $this->id, 'key' => 'max_width'],
['value' => $value]
);
It is supposed to check if there is a row in my content_settings table with a certain content id and the key "max_width" and if so update it and if not create it. It's used in a model mutator, hence the $this.
Anyway, no matter what values I try to put in, this ALWAYS results in this mysql query being executed (only with different time stamps):
insert into `content_settings` (`content_id`, `updated_at`, `created_at`) values (2, '2020-10-15 14:07:00', '2020-10-15 14:07:00')
...simply making new rows with empty key and value. Can anyone please spot the error? Or is this a bug somewhere? Something with "key" being a reserved word?
you should set the fields you want to use in mass assignment in the $fillable array in your model:
class ContentSetting extends Model
{
protected $fillable = ['content_id','key','value'];
}

Laravel updateOrCreate with where clause

How to implement updateOrCreate function with where clause.
For example. I would want to update a field only if certain column contains a specific value otherwise don't update.
Is it possible with updateOrCreate function?
updateOrCreate is an update with where-clause:
$user = User::updateOrCreate(
[
'id' => $userId,
'type' => $userType,
],
[
'name' => $userName,
'email' => $userEmail,
]
);
This query will update the user with $userName and $userEmail if a user exists with matching $userId and $userType. If no matching user is found, a new user is created with $userId, $userType, $userName and $userEmail.
So the first array is the "where-clause" that has to match for the update, second array is the values that should be updated if a match is found and a merge of both arrays are used when creating a new user if no match was found.
See docs here
updateOrCreate mainly used when you upadate one column value in your model.You don't know the condition either this row is exists or not.If the row is exists then it just upadte your column value otherwise it create that row.
$update_value = YourModel::updateOrCreate(
['your_checking_column' => 'your_value',],
['your_updated_coumn' => 'your_updated_value']
);
updateOrCreate doesn't provide this functionality as far as I know. Instead you can use regular where clause followed by update. From your question I see that you don't need to create at all.
Something::where('column', 'value')->first()->update(['otherColumn' => 'some value']);
If you're trying to set a value based on some criteria you can use a ternary:
'user_id' => ($request->user_id && is_numeric($request->user_id) ? $request->user_id : \Auth::user()->id)
This is the equivalent of saying, if user_id is provided and numeric, set the value to $request->user_id, otherwise take the user_id from the authenticated user, a simpler example:
'user_id' => ($request->user_id ? $request->user_id : null)
If a user_id is give in the request use that value, otherwise set value to null.

I get duplicate entries with the attach method in Laravel 5. Not sure what the problem is

So I'm trying to attach id's with some meta data to a pivot table in Laravel 5.
For some reason, I get the two inserts where there should be one, and the wrong ID's being inserted the second time round.
I'm not sure if there is something I might be missing here.
This is the code:
$match_values = array(
'dataId' => $result->id,
'dataMetaId' => $the_meta->id
);
$result->campaignDataMeta()->attach($match_values, [
'meta_value' => $value
]);
The database structure consists of a main campaignData table for email campaigns, a campaignDataMeta table (id, timestamps, name) for email meta data names, and a lookup table campaignDataMatches (id, campaignDataId, campaignDataMetaId, meta_value).
In campaignDataMatches I get the campaignDataId value sometimes being inserted into the campaignDataMeta column.
I've solved the problem.
Apparently had to add the relevant ID (in this case the dataMetaId) within the attach parameter.
Like this:
$result->dataMeta()->attach([$data_meta_id => [
'meta_value' => $value
]]);
Check the database columns primary maybe the dataId and metaId are both primary.

First Or Create

I know using:
User::firstOrCreate(array('name' => $input['name'], 'email' => $input['email'], 'password' => $input['password']));
Checks whether the user exists first, if not it creates it, but how does it check? Does it check on all the params provided or is there a way to specifiy a specific param, e.g. can I just check that the email address exists, and not the name - as two users may have the same name but their email address needs to be unique.
firstOrCreate() checks for all the arguments to be present before it finds a match. If not all arguments match, then a new instance of the model will be created.
If you only want to check on a specific field, then use firstOrCreate(['field_name' => 'value']) with only one item in the array. This will return the first item that matches, or create a new one if not matches are found.
The difference between firstOrCreate() and firstOrNew():
firstOrCreate() will automatically create a new entry in the database if there is not match found. Otherwise it will give you the matched item.
firstOrNew() will give you a new model instance to work with if not match was found, but will only be saved to the database when you explicitly do so (calling save() on the model). Otherwise it will give you the matched item.
Choosing between one or the other depends on what you want to do. If you want to modify the model instance before it is saved for the first time (e.g. setting a name or some mandatory field), you should use firstOrNew(). If you can just use the arguments to immediately create a new model instance in the database without modifying it, you can use firstOrCreate().
As of Laravel 5.3 it's possible to do this in one step with firstOrCreate using a second optional values parameter used only if a new record is created, and not for the initial search. It's explained in the documentation as follows:
The firstOrCreate method will attempt to locate a database record using the given column / value pairs. If the model cannot be found in the database, a record will be inserted with the attributes resulting from merging the first array argument with the optional second array argument.
Example
$user = User::firstOrCreate([
'email' => 'dummy#domain.example'
], [
'firstName' => 'Taylor',
'lastName' => 'Otwell'
]);
This returns the User for the specified email if found, otherwise creates and returns a new user with the combined array of email, firstName, and lastName.
This technique requires Mass Assignment to be set up, either using the fillable or guarded properties to dictate which fields may be passed into the create call.
For this example the following would work (as a property of the User class):
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['email', 'firstName', 'lastName'];
firstOrCreate() checks for all the arguments to be present before it finds a match.
If you only want to check on a specific field, then use firstOrCreate(['field_name' => 'value']) like:
$user = User::firstOrCreate([
'email' => 'abcd#gmail.com'
], [
'firstName' => 'abcd',
'lastName' => 'efgh',
'veristyName'=>'xyz',
]);
Then it checks only the email.
An update:
As of Laravel 5.3 doing this in a single step is possible; the firstOrCreate method now accepts an optional second array as an argument.
The first array argument is the array on which the fields/values are matched, and the second array is the additional fields to use in the creation of the model if no match is found via matching the fields/values in the first array:
See the Laravel API documentation
You can always check if in current instance the record is created with the help of
$user->wasRecentlyCreated
So basically you can
if($user->wasRecentlyCreated){
// do what you need to do here
}

Laravel: object or other structures (array, json..) to the view?

There are several ways you may pass data to a Laravel Blade view.
In this savvy discussion Laravel hidden attributes. e.g. Password - security Antonio Carlos Ribeiro states (and i agree) that:
"you are not supposed to send objects to a view. In the MVC pattern, views should receive data that are relative to them, processed data, not objects, because they don't have to know anything about your business logic."
I am learning Laravel and everywhere i look i often see examples like:
$users = User::all();
return View::make('users')->with('users', $users);
This one specially comes from the official documentation.
What method should be ideally used?
Do you transform your objects in array or other formats prior to send them to the view?
Do you selectively clean your data from all the unnecessary values prior of pass it to the template engine?
Apart being probably academically wrong, what are the potential risks for passing the object to the view?
If you use following approach
$users = User::all();
return View::make('users')->with('users', $users);
You will get a collection of User objects in your model and can use a loop to print out all the User objects and it's fine, what risk could be doing this, it's upon you, so you should know what should do but if you don't want to pass a collection object then it's also possible to pass only an array of arrays using:
$users = User::all()->toArray();
return View::make('users')->with('users', $users);
So, you'll get an array of arrays in the view where each child array will contain a perticular user's details. The array may look something like this:
array (size=2)
0 =>
array (size=5)
'id' => int 1
'username' => string 'heera' (length=5)
'email' => string 'heerasheikh#ymail.com' (length=21)
'created_at' => string '2014-01-20 06:10:53' (length=19)
'updated_at' => string '2014-01-23 10:23:50' (length=19)
1 =>
array (size=5)
'id' => int 2
'username' => string 'usman' (length=5)
'email' => string 'mdusyl#yahoo.com' (length=16)
'created_at' => string '2014-01-20 06:10:53' (length=19)
'updated_at' => string '2014-01-20 09:06:23' (length=19)
But, you can use the Laravel's traditional way and there is no risk at all. Don't follow something blindly, use your sense and ask yourself, what risk it may rise for you. You are only about to loop the collection, nothing else. Now, the choice is your's, if you pass the collection then you can use object notation, i.e. $user->username but if you pass an array then you have to use something like $user['username'], that's it.

Resources