Laravel Eloquent Events Implementation - laravel

I am updating my model instance or inserting a new one like this:
$model = Model::updateOrCreate([id' => $request['id']],
$model_to_update_array);
I want to execute some code only when existing model instance ('tourist') was updated (and NOT when a new one was created or nothing changes).
I've read https://laravel.com/docs/5.4/eloquent#events about Eloquent events and it seems to me that I need to use updated or updating event. As I understand these events are 'built-in' in Laravel, so I don't have to use a lot of stuff from here: https://laravel.com/docs/5.4/events
I haven't found a tutorial showing how to implement Eloquent events. Since I am new to events conception at all, it's hard for me to understand how to use them. Can anyone drop a link to a good tutorial about Eloquent events (not events in general, but Eloqeunt events in particular) or maybe it can be shortly explained here?
Thank you in advance!

The easiest way to add Eloquent event for a particular model is to overwrite its boot() method:
protected static function boot()
{
parent::boot();
static::updating(function ($model) {
});
}
When you put this in your model the anonymous function will run every time when the model is being updated. Please note that there is a difference between calling static::updating() and static::updated() depending on when you want to execute your code.

#TheFallen gave a great answer to this problem in another thread on StackOverflow, please read if you are interested in thoroughly explained solution:
Laravel Eloquent Events - implement to save model if Updated

Related

Laravel use query in creating event

what is best and clean way to insert a query value on creating event model like this (Laravel 5.7)
public static function boot() {
parent::boot();
self::creating(function ($model) {
$model->person_job = App\Job::where('person_name',$request->name);
});
}
i don't wanna put this routine query in my controller , so what do professional developers here?
You can use request() function(helper) anywhere in your project if you have no $request variable.
In MVC architecture logic must locate in Controller mostly, Here in Laravel, events mostly are used to do some specific logic(I think that logic mostly does not rely on a user's given data as you do here) on every model based on the event, for example, if you want to generate a unique_id for each Model on created || creating event, or doing caching or something like that.
See here for more about events.

Laravel: How to use function of relation

I'm facing a situation where a I have to call a function from the controller of a relation instance. For better explanation I will write an example below
I have a Article controller in which I have a preview() function.
A User can have multiple Article.
Let's say that the preview() function parse a text and replace special pieces of text by the user's name.
So my function will looks like this
//In ArticleController
public function preview(Article $article , User $user){
return str_replace("username", $user->name , $article->text);
}
But for a specific situation I want to display a preview of the article when I list all the users
So in UserController
public function index(){
foreach( User::all() as $user){
echo $user->articles[0]->preview( ... );
}
}
Obviously this piece of code will not work.
But I'm more looking of the way to proceed when I face this kind demand.
Should I create a Repository? Use this preview() function somewhere else? Or Is it just a bad practice to do that? What's the best approach or way of thinking when we face this?
Or maybe I'm just missing something important in Laravel's ORM. :/
I assume Article is a model. So you have to add hasMany relation to User (user has many articles). Inside article you have to add preview function. In this case you will be able to find $user->article (or user->articles) and run ->preview function. This is the easiest solution I guess.
You can also add custom attribute like getPreviewAttribute and append it to article model. This way you would have $user->article->preview.

Laravel Scout how refresh index with ElasticSearch?

I'm working on a Laravel project and I'm using https://laravel.com/docs/5.3/scout with ElasticSearch on a model Offer.
I already have some offers in my database, so I just run the command
`php artisan scout:import "App\Models\Offer"`
for generate the index for use my datas with ElasticSearch.
After that it's ok, I can search in my offers with, for example :
`$offers = Offer::search($request->keywords)->get();`
Now I have a function for create other offers in my database, and I dont know how can I refresh the index for use these new datas ?
In the documentation https://laravel.com/docs/5.3/scout#adding-records, I can read
all you need to do is save a model instance and it will automatically be added to your search index
I tried this and no, when I save() a new Offer, I can't find it in my index.
I tried to re run the command php artisan scout:import "App\Models\Offer" after add anew OFfer, but it's the same, I can't find it in my index.
Did I miss something ? Any ideas ?
Sorry for a late response on this but I ran into this issue myself when I attempted to use Scout. Everything went fine until I realized that the project's data would scale at a rate that went far beyond what scout could handle. In this case, however you can use the push() method instead of save(). I'm not sure why this isn't documented at all and it's rather frustrating but there's an answer at least.
So use:
->push()
instead of:
->save()
to update your indexes.
If that does not work for your specific version there is another way you can do it but it is "slightly" redundant. It involves a mix of using the queue system with the Artisan system and a command. In this you:
Create a queue/job php artisan make:job JobNameHere (As of Laravel 5.2 - 5.4)
Add use Artisan; to the top of that newly created Job Class so you can pull in the Artisan's functionality
In the handle of that Job Class add
class JobNameHere implements ShouldQueue {
...
...
public function handle() {
Artisan::call('scout:import', ['name' => "App\YourModelNameHere"]);
}
}
Add a dispatch call to that job class right after your DB push() process is called.
Example:
class YourController extends Controller {
public function yourUpdateMethod(Request $request) {
//Some code you wrote
//Some more code you wrote
$update_obj->push( $array_to_update_obj);
dispatch(new JobNameHere());
}
}
Test your index by searching on it
If all went well then you should no longer get any errors. Please leave a comment and let me know how it went... provided you're still having this issue.
I would also like to mention that Laravel Scout does not support ElasticSearch anymore as of August of 2016 (I believe). No one really knows why the support was removed but there are a few tutorials out there to help you get Laravel and Elastic search working together again.
I will also note, based on my research, that if your project is small then you should be fine to use Scout and not need to use ElasticSearch. If your project is going to get huge, then you're probably better off finding a package that supports and well documents how handle Laravel's relationships between models. Elastic search is capable of accomplishing this but there are tons of docs that make figuring it out difficult. Here are some semi-decent tutorials to help get you going down the right path.
https://tirdeamihai.com/blog/laravel-and-elasticsearch
https://laravel-news.com/laravel-and-elasticsearch
Plastic is a package that I would currently recommend as it's being actively worked on. Elasticquent hasn't been touched or updated since last year in June.
https://github.com/sleimanx2/plastic#1---create-a-new-index

How to use model events with query builder in laravel

I'm using model events such as static::saving, static::saved, etc in my models' static function boot method, and that works great when users save new posts, but when I do something like this:
$post::where('id', $post_id)->update(array('published'=>1));
Updating in this way does not run those model events. My current solution is to just not use this method of updating and instead do:
$post = Post::find($post_id);
$post->published = 1;
$post->save();
But is there any way to make the model events work with the first example using query builder?
Model events will not work with a query builder at all.
One option is to use Event listener for illuminate.query from /Illuminate/Database/Connection.php. But this will work only for saved, updated and deleted. And requires a bit of work, involving processing the queries and looking for SQL clauses, not to mention the DB portability issues this way.
Second option, which you do not want, is Eloquent. You should still consider it, because you already have the events defined. This way you can use also events ending with -ing.

Sentry & Laravel, getting users within a group. changing findAllUsersWithAccess to have pagination

I'm trying to find all users w/ a specific permissions list in Sentry with laravel. The problem is that Sentry::findAllUsersWithAccess() returns an array().
as stated in their github repository i pinpointed their code to be
public function findAllWithAccess($permissions)
{
return array_filter($this->findAll(), function($user) use ($permissions)
{
return $user->hasAccess($permissions);
});
}
right now, it gets all users and filter it out with users with permission list. the big problem would be when I as a developer would get the set of users, it'll show ALL users, i'm developing an app which may hold thousands of users and i only need to get users with sepcific permission lists.
With regards to that would love to use one with a ->paginate() capability.
Any thoughts how to get it without getting all the users.
Why dont you override the findAllWithAccess() method and write your own implementation, which uses mysql where instead of array_filter().
I dont know your project structure and the underlying db schema, so all i can give you atm is the link to the eloquent documentation Querying Relations (whereHas).
In case you dont know where to start: its always a good idea to look at the ServiceProvider (SentryServiceProvider, where the UserProvider, which holds the findAllWidthAccess() method, is registered). Override the registerUserProvider method and return your own implementation of the UserProvider (with the edited findAllWithAccess() method).
Hope that will point you in the right direction.
In Laravel you can do pagination manually on arrays:
$paginator = Paginator::make($items, $totalItems, $perPage);
Check the docs: http://laravel.com/docs/pagination

Resources