I want to create a custom model event in laravel 5.1.
For e.x. when an Articles category is updated i want to make an event and listen to it.
$article = Article::find($id);
$article->category_id = $request->input('category_id');
// fire an event here
You should use Eloquent Events (do not confuse with Laravel Events).
public function boot()
{
Article::updated(function ($user) {
// do some stuff here
});
}
You would want to look into Observers to make this more reusable and single-responsible, though a starting point would be something alike:
public function boot()
{
self::updated(function ($model) {
if (array_key_exists('category_id', $model->getDirty())) {
// Log things here
}
});
}
Laravel will populate a 'dirty' array which contains modified fields. You can detect when a certain field has changed using this.
You also have:
$model->getOriginal('field_name') // for this field value (originally)
$model->getOriginal() // for all original field values
You can use Attribute Events to fire an event when the category_id attribute changes:
class Article extends Model
{
protected $dispatchesEvents = [
'category_id:*' => ArticleCategoryChanged::class,
];
}
Related
I want to use Carbon::today()->addMonth() as default value of reserved_at column in jobs table
then I will use ite in the job in the way of
if(reserved_at == Carbon->today())
{
//execution
} else {
//don't
}
You can set reserved_at in model's creating event within the model boot() method, like that add following method in Job model:
public static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->reserved_at = Carbon::today()->addMonth();
});
}
It will append reserved_at field value in model when you storing data using eloquent methods.
Note: It'll not work with insert() query builder method.
I figured it out it's by using the function of the class InteractsWithQueue
->delay(Carbon::now()->addMinutes(5)) with the dispatch function
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 am developing a web admin panel using Laravel Nova.
I am having an issue since Nova is quite a new technology.
What I would like to do now is I would like to add a hidden field or extend or override the create method.
This is my scenario. Let's say I have a vacancy nova resource with the following field.
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Title')->sortable(),
Text::make('Salary')->sortable()
// I will have another field, called created_by
];
}
Very simple. What I like to do is I want to add a new field called created_by into the database. Then that field will be auto filled with the current logged user id ($request->user()->id).
How can I override or extend the create function of Nova? How can I achieve it?
I can use resource event, but how can I retrieve the logged in user in
the event?
What you're looking for is Resource Events.
From the docs:
All Nova operations use the typical save, delete, forceDelete, restore Eloquent methods you are familiar with. Therefore, it is easy to listen for model events triggered by Nova and react to them.
The easiest approach is to simply attach a model observer to a model:
If you don't feel like creating a new observable you could also create a boot method in your eloquent model as so:
public static function boot()
{
parent::boot();
static::creating(function ($vacancy) {
$vacancy->created_by = auth()->user()->id;
});
}
But please do note that these are a bit harder to track than observables, and you or a next developer in the future might be scratching their head, wondering how's the "created_at" property set.
In my opinion you should go for Observers. Observers will make you code more readable and trackable.
Here is how you can achieve the same with Laravel Observers.
AppServiceProvider.php
public function boot()
{
Nova::serving(function () {
Post::observe(PostObserver::class);
});
}
PostObserver.php
public function creating(Post $post)
{
$post->created_by = Auth::user()->id;
}
OR
You can simply hack a Nova field using withMeta.
Text::make('created_by')->withMeta([
'type' => 'hidden',
'value' => Auth::user()->id
])
You could also do that directly within your Nova resource. Every Nova resource has newModel() method which is called when resource loads fresh instance of your model from db. You can override it and put there your logic for setting any default values (you should always check if values already exist, and only set if they are null, which will only be the case when the model is being created for the first time, which is what you actually need):
public static function newModel()
{
$model = static::$model;
$instance = new $model;
if ($instance->created_by == null) {
$instance->created_by = auth()->user()->id;
}
return $instance;
}
a) Create an Observer class with following command:
php artisan make:observer -m "Post" PostObserver
b) Add following code in the PostObserver:
$post->created_by = Auth::user()->id;
c) Register PostObserver in AppServiceProvider.php
For detailed explanation: https://medium.com/vineeth-vijayan/how-to-add-a-new-field-in-laravel-nova-resource-87f79427d38c
Since Nova v3.0, there is a native Hidden field.
Usage:
Hidden::make('Created By', 'created_by')
->default(
function ($request) {
return $request->user()->id;
}),
Docs: https://nova.laravel.com/docs/3.0/resources/fields.html#hidden-field
I have a post model, a user model, and a point model.
Whenever a user posts a new post, they get a point added (new row in points table).
I have controllers setup for each of those models.
Should/how can I call the PointsController#store from the PostsController#store after adding the post?
Or is this the wrong way to go about it?
Full code on GitHub
Consider using a modal.
For instance, in your PointsController:
public function store() {
// your code
User::addPoint(); // this is responsible for adding the point
}
And in your PostsController
public function store() {
// your code
User::addPoint(); // call this again
}
This way, you have a model responsible for updating the post.
Option1: using models
In PostController
public function store(Request $request){
\Auth::user()->post()->create($request->all());
\Auth::user()->points()->create['point'=> 1]; // add your columns here
}
In Point model
protected $fillable = ['point']; //add here list of columns you want to use on \Auth::user()->points->create['point'=> 1];
Option2: use an observer
create an observer class like here. I would make an Observers folder in app/
https://laravel.com/docs/5.5/eloquent#observers
Call it PostObserver
in PostObserver in create method:
public function created(Post $post)
{
$post->user()->points()->create['point'=> 1]; // [column1=>value1, column2=>value2]
}
In Point model
protected $fillable = ['point']; //add here list of columns you want to use on \Auth::user()->points->create['point'=> 1];
In AppServiceProvider in method boot add
public function boot()
{
Post::observe(PostObserver::class);
}
Don't forget to add the correct namespaces and then add use Observers\PostObserver in AppServiceProvider
In Laravel, I would like to define a grandparent type eloquent relationship so that I can have the grandchild touch the grandparent when it is added or updated (for instance if it brings the grandparent a very nice birthday card). Say I have a hierarchy like Collection > post > comment.
--Comment--
protected $touches = array('post', 'collection');
public function post()
{
return $this->belongsTo('Post');
}
public function order()
{
// not working
// return $this->post->collection();
// not working
// return $this->belongsTo('Post')->belongsTo('Collection');
}
--Post--
public function collection()
{
return $this->belongsTo('Collection');
}
I want the update function in the Collection to run when a Comment is added. How can I set that up?
I'm using Laravel 4.1
I want the update function in the Collection to run when a Comment is
added.
I'm a bit confused about the update function but...
You can register an event handler for that, for example, add this in your Comment model:
class Comment extends Eloquent {
public static function boot()
{
parent::boot();
static::created(function($comment)
{
// $comment is the current Comment instance
});
}
}
So, whenever any Comment will be created the event handler (anonymous function) will run so you may do something in that function. Read more on Laravel Website.