I have a model and I want to take an old the value of that model's field after save, I was using getOldAttribute() method and it always returns the latest value?
Code sample
class User extends ActiveRecord
{
public function fields()
{
return [
'id',
'name',
'email' //old value: example#example.com
];
}
}
class UserService extends BaseService
{
/**
* Current user have email is `test#test.com`
*/
public function update(User $user)
{
//before save
$oldEmailBeforSave = $user->getOldAttribute('email'); //work fine and value is `example#example.com`
//handle update user
$this->saveEntity($user)
//after save
$oldEmailAfterSave = $user->getOldAttribute('email'); //not work and value is `test#test.com`
}
}
According to the docs you can use the second parameter of the afterSave( $insert, $changedAttributes) which holds the old values of the attributes that were changed and were saved.
Your code should look like below if you want to get the email fields old value if it was changed
public function afterSave( $insert, $changedAttributes ){
parent::afterSave();
if(!$insert){
$oldEmail = $changedAttributes['email'];
//do something here with the old email value
}
}
When you save new attribute, it is no longer "old". getOldAttribute() represents state from database and allows you to compare whether and how attribute was changed between find() and save(). After calling save(), old attributes are refreshed, so getOldAttribute() will return the same as getAttribute().
If you want to access old state of object, you need to clone it and use these methods on clone - $cloned will represent state of object before calling save():
public function update(User $user)
{
//before save
$oldEmailBeforSave = $user->getOldAttribute('email'); // `example#example.com`
$cloned = clone $user;
//handle update user
$this->saveEntity($user)
//after save
$oldEmailAfterSave = $user->getOldAttribute('email'); // `test#test.com`
$oldEmailAfterSave = $cloned->getOldAttribute('email'); // `example#example.com`
}
Related
I have a user model, and I want to add (an attribute to the user model) the user's email that it was before it was updated.
before#email.com
new#email.com
Within the user model, I have this function, I can get the before email, I was wondering I can assign some fake attribute, so I can access it like: $user->beforeEmail
protected static function booted()
{
static::saved(function ($user) {
$user->beforeEmail = $user->original['email'];
});
}
$user->beforeEmail // before#email.com
The code above is not working but provided it to help illustrate what I am trying to accomplish.
You could check if the email address has changed just before storing the new email to the db. This can be accomplished by using the saving event instead of the saved event.
protected static function booted()
{
static::saving(function ($user) {
if($user->isDirty('email')){
$user->beforeEmail = $user->email
}
});
}
Note: Your code example will not save the changes automatically since the saved event is ran after executing the query. It's possible that your code works just by adding $user->save()
Are you trying to get this value in the model or in a different class? As what you have works with a few adjustments already.
protected static function boot(){
parent::boot();
static::saved(function($user){
$user->originalEmail = $user->original['email'];
}
}
You can access originalEmail if you update the model in a controller or other class, like so:
$user = User::find(1);
$user->update([
'email' => 'email#email.com'
]);
// $user, $user->originalEmail, $user->some_test_accessor all return the correct values
I've also tested with an accessor, just to verify and it still works as though the value is available in the model. I'm not sure where you're attempting to access this value, though.
public function getSomeTestAccessorAttribute(){
return $this->originalEmail;
}
Does any of this help?
I have a laravel application in which the setting_types and user's settings are saved into different models.
User.php:
/*
* Getting the user's notification setting.
*/
public function notificationSetting()
{
return $this->hasMany('App\NotificationSetting');
}
/*
* Controller function to get the user's settings
*/
public function getSetting(Request $request)
{
$userSetting = $user->notificationSetting;
// check new settings are inserted for user or not.
if (someCondition) {
// add new settings for user.
$user->notificationSetting()->save(new NotificationSetting(['user_id' => $user_id, "notification_type_id" => 121]));
print_r($user->notificationSetting); // still rec. Old values.
}
return $user->notificationSetting;
}
As you can see that I insert the relation object but I didn't receive on the same time. and if I hit again (this time my someCondition become false) so it will return the update records.
Since the save() method returns a boolean, you could write it like this:
$user->notificationSetting()
->save(
$notificationSetting = new NotificationSetting([
'user_id' => $user_id,
'notification_type_id' => 121
])
);
return $notificationSetting;
You might also be able to use the create() method instead, that will return the instance of the model, but only if the attributes are fillable of course.
If you want to retrieve all the related records of a model at any time, you can use the load() method like this:
$user->load('notificationSetting');
It is also important the use the plural form for a hasMany relation in order to distinguish it from a hasOne or a belongsTo relation:
public function notificationSettings()
{
return $this->hasMany('App\NotificationSetting');
}
This is my User model:
/**
* The settings that belong to the user.
*/
public function settings()
{
return $this->hasMany(Setting_user::class);
}
/**
* Get user's avatar.
*/
public function avatar()
{
$avatar = $this->settings()->where('id',1);
if(count($this->settings()->where('id',1)) == 0 )
{
return "default-avatar.jpg";
}
return $this->settings()->where('id',1);
}
In my view I am accessing the value like that:
Auth::user()->avatar
When the user has an avatar everything is fine. But when is empty the method avatar() returns a string and I get the following error:
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation (View: C:\xampp\htdocs\laravel\laravel-paper-dashboard\resources\views\dashboard\user-profile.blade.php)
You may want to use an Eloquent Accessor instead.
public function getAvatarAttribute() {
$avatar = $this->settings()->where('id',1)->first(); // Changed this to return the first record
if(! $avatar)
{
return "default-avatar.jpg";
}
// You will need to change this to the correct name of the field in the Setting_user model.
return $avatar->the_correct_key;
}
This will allow you to then call Auth::user()->avatar in your templates.
Otherwise Eloquent thinks you're trying to get a relationship.
I am using Laravel 4.2.
I have two models: User and Video, both of these models are having one-to-many relationship i.e. User -> HasMany-> Video.
Recently, I got a requirement to display the list of users along with sum of file-size of total videos uploaded by each user and allow users to be order by the sum of file size ascending or descending.
I've made following changes in User model:
class User extends Eloquent {
protected $hidden = array('videosSum');
protected $appends = array('videos_size');
public function videosSum() {
return $this->hasOne('Video')
->selectRaw('sum(file_size) as sum, user_id')
->groupBy('user_id');
}
public function getVideosSizeAttribute()
{
// if relation is not loaded already, let's do it first
if ( ! array_key_exists('videos_size', $this->relations)){
$this->load('videosSum');
}
$related = $this->getRelation('videosSum');
return $this->attributes['videos_size'] = isset($related->sum) ? (int) $related->sum : 0;
}
}
And using like:
User::where('id', '!=', Auth::user()->id);
I am getting the desired result.
But the problem is, I don't want the videos_size attribute everywhere, where the User model gets called. I want to set it dynamically.
I tried User::$appends = ['videos_size'] but it gives protected property cannot be set outsize of class error.
I also tried to make a method in User model which set the $appends if called, but it is also not working.
Can anybody help me how to enable the appends property dynamically?
Laravel doesn't support this off the bat.
my friend and I wrote this extention:
Dynamically hide certain columns when returning an Eloquent object as JSON?
basically you have to override your models.php toArray() method as appended attributes get calculated when you ask for the model in json or array form.
you can add to the trait that's in that link and use it or just put these methods in your respective model class.
public static function getStaticAppends() {
return self::$_appends;
}
public static function setStaticAppends(array $value) {
self::$_appends = $value;
return self::$_appends;
}
public static function getDefaultAppends() {
return with(new static)->getAppends();
}
public function getAppends(){
return $this->appends;
}
public function toArray() {
if (self::getStaticAppends()) {
$this->appends = self::getStaticAppends();
}
return parent::toArray();
}
I'm trying to submit updates to items in the Items model. However, Ardent never actually performs the updates when the update method receives them but doesn't throw an error:
public function update($id)
{
$item = Item::findOrFail($id);
if ($item->save()) {
return Redirect::back()->with('message', "Item #$id updated!");
} else {
return Redirect::back()->withInput()->withErrors($item->errors());
}
}
What is wrong with my controller logic here?
It looks like you need to set those variables
class Item extends \LaravelBook\Ardent\Ardent {
// hydrates on new entries' validation
public $autoHydrateEntityFromInput = true;
// hydrates whenever validation is called
public $forceEntityHydrationFromInput = true;
}
properly to get the behavior you expect.
Source: https://github.com/laravelbook/ardent#automatically-hydrate-ardent-entities