Versioning with Laravel / Eloquent - laravel

I have a products table that has an serial id, a sku, an is_current flag, and then various attributes about the item. It also has a unique index on sku where is_current = true to prevent having more than one current record.
What I want to happen is, when you change the model and call save(), a new record is created instead and the existing record is only changed to flip the is_current flag. So what I think I want is a way to copy the model with replicate(), discard the changes in the old model and just update to flip the current flag false, and then insert the new model. Something like that, although I might not have thought that though 100%. Can this be done in as a part of an "updating" event? Is a trigger a better choice to prevent accidents if some direct table updates occur?

You can do this 2 ways, One is hooking into the save event on the model. The other is creating a new method on the model, I would probably go the new method route. like $product->updateCurrent(); which would then do all of the logic.
public function updateCurrent($details)
{
$newProduct = $this->replicate()->fill($details);
$this->update([
'is_current' => false,
]);
return $newProduct;
}
Otherwise the saving event
product model
protected $dispatchesEvents = [
'saving' => \App\Events\ProductSaving::class,
];
ProductSaving event
<?php
namespace App\Events;
use App\Product;
use Illuminate\Queue\SerializesModels;
class ProductSaving
{
use SerializesModels;
public $product;
/**
* Create a new event instance.
*
* #param \App\Product $product
*/
public function __construct(Product $product)
{
$this->product = $product;
}
}
Now you need a listener
ProductSaving
<?php
namespace App\Listeners;
use App\Events\ProductSaving as ProductSavingEvent;
class ProductSaving
{
/**
* Handle the event.
*
* #param \App\Events\ProductSavingEvent $event
* #return mixed
*/
public function handle(ProductSavingEvent $event)
{
$product = $event->product->replicate();
$old_product = Product::find($event->product->id);
$old_product->update([
'is_current' => false,
]);
return false; // Prevent model from saving.
}
}

Related

Laravel 8 - Insert in related table in model

Whenever I create a "user", I have to create a line in different tables (like account).
I know that in the controller I can create the user and account like this:
$user = User::create($user_inputs);
$account = $user->account()->create($account_inputs);
$OtherTables...
Is there a way to do this in the model? Always when someone creates a user from another controller, will the lines be automatically inserted in the other tables. Or is it always necessary to indicate it in the controller every time?
You can use Laravel observer
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
/**
* Handle the user "created" event.
*
* #param \App\User $user
* #return void
*/
public function creating(User $user)
{
$user->account()->create([
// your data
]);
}
}
You can use model events for this. https://laravel.com/docs/9.x/eloquent#events-using-closures
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The "booted" method of the model.
*
* #return void
*/
protected static function booted()
{
// This code will be called every time a new user is inserted into the system
static::created(function ($user) {
$user->account()->create([ 'name' => $user->name ])
});
}
}
There are few more events you can use within booted method, the name tells clearly what they do.
creating
created
updating
updated
saving
saved
deleting
deleted

Retrieve created model ID in laravel observer

i've registered an observer on my Client model.
Why is $client->id null? Shouldn't the freshly created model ID be available to read?
What can i do to retrieve the last created Client ID?
ID is set as fillable in model Client, so i don't understand what the problem might be. Thanks.
<?php
namespace App\Observers;
use App\Models\Client;
use App\Models\SampleModel;
class ClientObserver
{
/**
* Handle the Client "created" event.
*
* #param \App\Models\Client $client
* #return void
*/
public function created(Client $client)
{
$model_to_create = new SampleModel();
$model_to_create->id_client = $client->id;
//other stuff to save in model
$model->save();
}
You should have created the Observer without reference to your model, if the laravel does not know which model is, the $client always gonna return null.

Eloquent how to do something when delete or update operate on a special model

Most of my db table contain create_user_id and update_user_id
How can l update this two field automatic when l use save(), update(), insert(), createOrUpdate() and etc method.
For example, l execute this script:
$model = Model::find(1);
$model->model_f = 'update';
$model->save();
then this record's model_f updated, and update_user_id updated, too.
l know eloquent can manage update_time automatic and l have use it already. But l want to do something else when update or insert or delete
PS: l have a constant named USERID to remember current user's id
You could make use of Observers.
You can hook to the following events on your Model:
retrieved
creating
created
updating
updated
saving
saved
deleting
deleted
restoring
restored
Let me give you an example where we are trying to hook into the events emitted by the App/User model. You can change this to match your particular Model later on.
To create an observer, run the following command:
php artisan make:observer UserObserver --model=User
Then you can hook to specific events in your observer.
<?php
namespace App\Observers;
use App\User;
class UserObserver
{
/**
* Handle the User "saved" event.
*
* #param \App\User $user
* #return void
*/
public function saved(User $user)
{
//
}
/**
* Handle the User "created" event.
*
* #param \App\User $user
* #return void
*/
public function created(User $user)
{
//
}
/**
* Handle the User "updated" event.
*
* #param \App\User $user
* #return void
*/
public function updated(User $user)
{
//
}
}
Since, in your particular case, you want to hook into these 3 events, you can define the events above and perform additional operations to your model when those events are called.
Don't forget to register this observer in your AppServiceProvider.
<?php
namespace App\Providers;
use App\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
User::observe(UserObserver::class);
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
//
}
}
There is pretty simple way to automatically update the create_user_id and update_user_id
Step1:
Open you app folder and create the new file named as UserStampsTrait.php
Step:2
and paste the following code
<?php
namespace App;
use Illuminate\Support\Facades\Auth;
trait UserStampsTrait
{
public static function boot()
{
parent::boot();
// first we tell the model what to do on a creating event
static::creating(function($modelName='')
{
$createdByColumnName = 'create_user_id ';
$modelName->$createdByColumnName = Auth::id();
});
// // then we tell the model what to do on an updating event
static::updating(function($modelName='')
{
$updatedByColumnName = 'update_user_id';
$modelName->$updatedByColumnName = Auth::id();
});
}
}
Thats it
Step:3
Open you model which needs to updated the corresponding models automatically
for Example it may be Post
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\UserStampsTrait;
class Post extends Model
{
use UserStampsTrait;
}
Thats it

Laravel Model accessing a value of an instance of its self

I've got a model and the model its self could be linked to multiple other databases but only one at a time.
Instead of having a eloquent method for all the possible databases; it could have one that will use a variable from the self instance to choose the database and return just that.
It will save alot of work, as returning each one and testing to see if there are any results is cumbersome.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Feature extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'companies';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [
'db_name',
'enabled',
];
/**
* Uses the its own database name to determine which input to return.
*/
public function inputs() {
// if this->hidden->db_name == 'input type 1'
// return $this->HasMany(InputType1::class);
.... and so on
} // end function inputs
}
This is definitely a strange behaviour but I think you can achieve what you are looking for like so :
//in your model
public function inputs()
{
switch ($this->attributes['db_name']) {
case : 'input type 1':
return $this->hasMany(InputType1::class);
case : //some other database name
return //another relation
}
}
Expanding on shempognon answer, what I actually got to work was
switch($this->db_name) {
case 'Input_Timesheet':
return $this->hasMany(Input_type1::class);
}

Eloquent ORM all() not returning anything

Shoot me down if I this is a silly question, but I am really struggling to get this all() function working for me. It is returning empty list for me. Any help will be highly appreciated. I have got 2 rows in the newsletters table
Model looks like this -
<?php
use Illuminate\Auth\UserTrait;
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableTrait;
use Illuminate\Auth\Reminders\RemindableInterface;
class Newsletters extends Eloquent {
//use UserTrait, RemindableTrait;
use SoftDeletingTrait; // <-- Use This Insteaf Of protected $softDelete = true;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'newsletters';
/**
* The attributes excluded from the model's JSON form.
*
* #var array */
protected $guarded = array('newsletterId');
protected $fillable = array('name', 'subject','from_email','from_name');
public static $rules = array(
'name' => 'required|min:5',
'subject' => 'required|min:5',
'from_email' => 'required|email',
'from_name' => 'required'
);
}
My call in the controller is like this -
<?php
class newslettersController extends \BaseController {
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
//$newsletters = Newsletters::paginate(3);
$newsletters = Newsletters::all();
echo $newsletters;exit();
return View::make('newsletters.index', compact('newsletters'));
}
Any value - even 0000-00-00 00:00:00 - in the deleted_at column tells Laravel that the item has been deleted. Change your default value for that column to NULL or new items will be flagged as deleted on creation.
The $table->softDeletes() Schema function does this automatically if you use it in a migration.
As soon as you use the SoftDeletingTrait a global scope will be applied to every query with your model so all records where deleted_at is not NULL will be ignored.
Illuminate\Database\Eloquent\SoftDeletingScope:
public function apply(Builder $builder)
{
$model = $builder->getModel();
$builder->whereNull($model->getQualifiedDeletedAtColumn()); // <<-- this
$this->extend($builder);
}
Change the default of your deleted_at column to NULL and update the existing records to be NULL as well.
If you are sure newsletters is the correct table name as #Ray said.
Try this:
$newsLetters = DB::table('newsletters')->get();

Resources