Extend model get/build method in Laravel - php

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

Get $this in method protected boot model Laravel

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.
}
}

How to pass variable from controller to model after function execution Yii2

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?

Model observer in Laravel 4, restore() triggering both restored() and updated()

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
}
}

Laravel Model Events don't fire

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.

laravel hook into Eloquent pre and post save for every model

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;
}
}
}

Categories