I need to update my person object every time i retrieve it from the database and then save it again .. But how to do it?
My object, Person, with a bool-attribute seen_by_organization which is 0 when its created.
First time the Person is retrieve from the database i want to set the seen_by_organization to 1.
Ive tried put into the constructor, but it seems not to work
public function __construct(array $attributes = array())
{
parent::__construct($attributes);
if($this->seen_by_organization == 0)
{
$this->seen_by_organization = 1;
$this->save();
}
}
I know a method to bind code to "on save", but not "before get" or "after get".
protected static function boot()
{
parent::boot();
//This is on saving
static::saving(function($model)
{
});
//Is there something like this ?
static::getting(function($model)
{
}
}
I hope that you can help me
Thanks !
There is no built-in event for this. Also created is not the one you need - it is fired when a new model is inserted to the storage.
You need something like this:
// This is called after fetching data from db
// override it in order to fire the event you need
public function setRawAttributes(array $attributes, $sync = false)
{
parent::setRawAttributes($attributes, $sync);
$this->fireModelEvent('loaded', false);
}
// add handler for new event
public static function loaded($callback)
{
static::registerModelEvent('loaded', $callback);
}
// do the job
public static function boot()
{
parent::boot();
static::loaded(function ($user) {
if($user->exists && $user->seen_by_organization == 0)
{
$user->seen_by_organization = 1;
$user->save();
}
});
}
Mind that it will be called for each model that you retrieve using get() or first() etc. so there might be multiple inserts.
I got it !
The name of the event is created
protected static function boot()
{
parent::boot();
static::created(function($model)
{
if($model->seen_by_organization == 0)
{
$model->seen_by_organization = 1;
$model->save();
}
});
}
Related
I have a code on My Model:
protected static function boot() {
parent::boot();
if($this->status == 1) {
static::updated(function () {
//do something
}
}
}
I need check if status of current record == 1 then do static::updated().
Now I get error: $this cannot be in static function.
How I can check status in protected static function boot?
Like Daan mentioned in the comments this is not possible, you could however solve this in another way:
protected static function boot() {
parent::boot();
static::updated(function ($model) {
if ($model->status == 1) {
//do something
}
});
}
1) Usage of $this
You should replace $this with self:: to refer to the variable ($status) contained within the static class:
protected static function boot() {
parent::boot();
if(self::$_status == 1) {
static::updated(function () {
//do something
}); //also close off your method call correctly.
}
}
2) Check if status of current record = 1
I need check if status of current record == 1 then do static::updated().
Where is the value of $status set/saved/accessible? The code you show appears to show that $status is set in the class itself, but as a static class this will be, er, static.
You may need to stop the class/method being static or to supply the data to the function like so:
protected static function boot($status = null) {
parent::boot();
if($status == 1) {
static::updated(function () {
//do something
}); // close off your method properly.
}
}
I have a function in a controller that when executed, check for existence of a record in a table and returns that record. This function is used as validation of a model inside the same controller. For example:
public function somethingValidate() {
$someVariable = SomeTable::find()->where(['some_id' => $someVariable])->one();
if ($model) {
return $model;
} else {
return false;
Here is the validation part of controller:
public function actionSave() {
$model = new TestModel() {
if ($this->somethingValidate()) {
try {
--- REST OF THE CODE ---
How can i now, pass $someVariable variable into a TestModel and manipulate the data either on save or beforeSave.
Any assistance is greatly appreciated.
For example, an Investment amount should be more or equal with the Proposal's min_investment value. Below is the beforeValidate() function in Investment model.
public function beforeValidate()
{
parent::beforeValidate();
$proposal = Proposal::findOne($this->proposal_id);
if ($this->amount < $proposal->min_investment)
{
$this->addError('amount', Yii::t('app', 'Minimum amount should be ' . $proposal->min_investment));
return FALSE;
}
return TRUE;
}
Is that what you're trying to achieve?
I'm using an observer on models in Laravel 4 for the purposes of keeping historical records of changes made by each user. The code I'm currently using is as follows:
class BaseObserver {
public function __construct(){}
public function saving(Eloquent $model){}
public function saved(Eloquent $model){}
public function updating(Eloquent $model){}
public function updated(Eloquent $model)
{
$this->storeAuditData($model, 'update');
}
public function creating(Eloquent $model){}
public function created(Eloquent $model)
{
$this->storeAuditData($model, 'create');
}
public function deleting(Eloquent $model){}
public function deleted(Eloquent $model)
{
$this->storeAuditData($model, 'delete');
}
public function restoring(Eloquent $model){}
public function restored(Eloquent $model)
{
$this->storeAuditData($model, 'restore');
}
public function storeAuditData(Eloquent $model, $action)
{
$snapshot = array();
foreach ($model->fillable as $fieldName) {
$snapshot[$fieldName] = $model->$fieldName;
}
$auditData = new AuditData;
$auditData->model = get_class($model);
$auditData->rowId = $model->id;
$auditData->action = $action;
$auditData->user = Auth::user()->username;
$auditData->moment = date('Y-m-d H:i:s');
$auditData->snapshot = json_encode($snapshot);
$auditData->save();
}
}
This works fine, except when a restore() is performed, both the restored and updated methods get run, so I end up with two rows in the AuditData database table, when I only want one (the "restore").
Is there any way I can tell within the updated method whether the update is a restore or not, and only store the audit data if it is a stand-alone update and not a restore?
You could check if only the deleted_at column has been modified (is dirty)
public function updated(Eloquent $model)
{
if($model->isDirty($model->getDeletedAtColumn()) && count($model->getDirty()) == 1){
// only soft deleting column changed
}
else {
$this->storeAuditData($model, 'update');
}
}
While restoring, and using Laravel auto timestamps, the column updated_at will be modified along with deleted_at. To differentiate between 'restored' and 'updated' observer event, within updated method I use:
public function updated(Eloquent $model)
{
if ($model->isDirty($model->getDeletedAtColumn())
&& $model->{$model->getDeletedAtColumn()} === null
&& $model->getOriginal($model->getDeletedAtColumn()) !== null) {
// model was restored
}
}
I have a model with this code:
<?php
use Illuminate\Database\Eloquent\SoftDeletingTrait;
class Intervention extends Eloquent {
use SoftDeletingTrait;
protected $fillable = array('start_date','stove_id','description','operation_mode','store_id','user_id','intervention_status_id','code');
public function operations()
{
return $this->hasMany('InterventionOperation');
}
public function store()
{
return $this->belongsTo('Store');
}
public function stove()
{
return $this->belongsTo('Stove');
}
public function user()
{
return $this->belongsTo('User');
}
public function statues()
{
return $this->hasMany('InterventionStatus');
}
then the boot
public static function boot()
{
parent::boot();
static::creating(function($intervention)
{
exit("creating");
});
static::created(function($intervention){
exit("created");
});
static::updating(function($intervention)
{
exit("updating");
});
}
the controller:
$intervention = new \Intervention(\Input::all());
$status = \Status::find(\Input::get('status')['id']);
$interventionStatus = new \InterventionStatus();
$interventionStatus->change_status_date = new \DateTime();
$interventionStatus->status()->associate($status);
$interventionStatus->description = "";
$user = \Auth::user();
$store = $user->store;
$intervention->store()->associate($store);
$intervention->user()->associate($user);
$intervention->request_date = new \DateTime();
$intervention->save();
...
but when save model, creating callback is not call.
I have try put exit("test") after parent::boot(); and exit is triggered.
If I put event's code in app/start/global.php it work.
I have try use the code in another model and work.
I do not know why it does not work.
Resolved:
I recreated the database and now everything works. Probably, in the various attempts to save, some relationship was skipped.
Thank you all for the help!
I think this has something to with the namespaces and registering the correct class in the event. Let's hack the source code a bit :)
In: /vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php
Add:
public function getAllEvents()
{
return array_keys($this->listeners);
}
And call/dump Event::getAllEvents();
Try this for both cases (boot in the model and in the global.php) and compare.
I'm new to Laravel and ORM's in general. How could i hook into Eloquent to fire code before and after a save of any model? I know i can do the following for specific models but i'm looking at figuring out how to do this for every model.
class Page extends Eloquent {
public function save()
{
// before save code
parent::save();
// after save code
}
}
Using laravel models own life cycle events may solve this easy
/**
* model life cycle event listeners
*/
public static function boot(){
parent::boot();
static::creating(function ($instance){
//
});
static::created(function ($instance){
//
});
}
There's even a better way of accomplishing this! Create an observer for, lets say a model called House:
class HouseObserver {
public function saving(House $house) {
// Code before save
}
public function saved(House $house) {
// Code after save
}
}
Now register the observer with the House model by adding the line House::observe(new HouseObserver) somewhere. The line can be added in the boot method of the model:
class House extends Eloquent {
// Lots of model code
public static function boot() {
parent::boot();
self::observe(new HouseObserver);
}
}
More info can be found here.
You can create a BaseModel class that extends eloquent and then have all your models extend BaseModel. Here's an example:
abstract class Elegant extends Eloquent{
/* Save ****************************/
public function preNew() {}
public function postNew() {}
public function preSave() { return true; }
public function postSave() {}
public function save($validate=true, $preSave=null, $postSave=null)
{
$newRecord = !$this->exists;
if ($validate)
if (!$this->valid()) return false;
if($newRecord)
$this->preNew();
$before = is_null($preSave) ? $this->preSave() : $preSave($this);
// check before & valid, then pass to parent
$success = ($before) ? parent::save() : false;
if ($success)
is_null($postSave) ? $this->postSave() : $postSave($this);
if($newRecord)
$this->postNew();
return $success;
}
public function onForceSave(){}
public function forceSave($validate=true, $rules=array(), $messages=array(), $onForceSave=null)
{
if ($validate)
$this->valid($rules, $messages);
$before = is_null($onForceSave) ? $this->onForceSave() : $onForceSave($this); // execute onForceSave
return $before ? parent::save() : false; // save regardless of the result of validation
}
/** Soft Delete ****************************/
public function preSoftDelete() { return true; }
public function postSoftDelete() { }
public function softDelete($val = true, $preSoftDelete=null, $postSoftDelete=null)
{
if ($this->exists)
{
$before = is_null($preSoftDelete) ? $this->preSoftDelete() : $preSoftDelete($this);
$success = null;
if($before) {
$this->set_attribute(static::$softDelete, $val);
$success = $this->save(false);
}
else
$success = false;
if ($success)
{
is_null($postSoftDelete) ? $this->postSoftDelete() : $postSoftDelete($this);
}
return $success;
}
}
/** Hard Delete ****************************/
public function preDelete() { return true;}
public function postDelete(){}
public function delete( $preDelete=null, $postDelete=null)
{
if ($this->exists)
{
$before = is_null($preDelete) ? $this->preDelete() : $preDelete($this);
$success = ($before) ? parent::delete() : false;
if ($success)
{
is_null($postDelete) ? $this->postDelete() : $postDelete($this);
}
return $success;
}
}
}