when using laravel scout, searchable() isn't updating related models - laravel

I am having an issue getting related models to update with scout elastic search.
$event->priceranges()->delete();
$event->priceranges()->Create([
'price' => $ticket['ticket_price']
]);
$event->update([
'show_times' => $request->showtimes,
]);
$event->searchable();
In my database I see the event and price range tables update. However when I look at my elastic search data, only data on the event has been updated. Any related models are not updated.
If I make a second update to the model and price range model, then the elastic search data shows my data from the first update I made (it is always one update behind for related models)
I tried doing
$event->pricerange->searchable()
but gives me an error because I don't have a searchable index for pricerange, I am just using my event model and its relationship to index. Is there a way to force the update other than searchable()?

Looks like your relationship is being indexed in the Event model index, correct?
Probably it's not being updated because the relationships are already loaded, and Laravel doesn't update the relationship data that is already loaded, for example:
$event = Event::with('priceranges')->first()
var_dump($event->priceranges->count()): // outputs for example 5
$event->priceranges()->create([...]);
var_dump($event->priceranges->count()): // still outputs 5, meaning that the created pricerange is not loaded
So, to fix this issue you can reload the model before calling searchable():
$event = $event->fresh();
$event->searchable();
But, note that everytime you update, the searchable method is already being called, so you will be indexing it two times on every update.
So, alternatively you can update your toSearchableArray() method in Event model to get a fresh model before returning the data (I'm assuming that you are using the babenkoivan/scout-elasticsearch-driver).

Related

Laravel get collection of created/updated rows

new to laravel.
My use case:
Update multiple rows (say: resources table).
Create multiple users (users table).
Retrieve ids of created users
What I currently did:
First, Update the resources table whereIn('id', [1,2,3,4]). (Update eloquent)
Second, Get array of the updated resources (Another eloquent: Resource::whereIn('id', [1,2,3,4])->get()->toArray())
Bulk create a users. Refers to the resources collection above. (Another eloquent: User::create($resources))
Lastly, get the ids of the created users (Not resolved yet. But I might have to use another eloquent query)
All in all, there are 4 Eloquent queries, which I want to avoid, because this might have performance issue.
What I wanted to do is that, On first step(Update), I should be able to get a collection of models of the affected rows (But I can't find any reference on how to achieve this). And then use this collection to create the users, and get the users ids in one query with User::create() so that I will have 2 queries in total.
There is no need to invent performance problems that do not exist.
Update or Insert return only count of affected rows. Commands Select, Insert, Update performed separately. This is a SQL issue, not Laravel.
For single inserts (if you want add one row) you can use insertGetId method of a model.
For example,
$id = User::insertGetId([
'email' => 'john#example.com',
'name' => 'john'
]);
But you get only ID of record. You need to run select to get full data of the row.
To save multiple records with one query you can use
DB::table('table_name')->insert($data);
Since this won't be an eloquent method, you should pass all the columns including created_at and updated_at.
I don't know what is the method name for update.

How to use same model with less fields for another view in mvc5

My Question is not belonging to how to use two models on same razor view ! basically I have a user table in which i have fields like(userid,name,email,password,gender,country,department,IsActive) and my form is working fine i am able to insert update and delete i have not use EF , what i did i create the table in sql server and create the model in my model folder , in my view i have put the required field validator for all these columns and they are mandatory to input while inserting or updating.
Now I want to have another view with another controller where i do not want to show all 8 fields instead want to show just these four columns( username,email,gender,IsActive)
when i am using the same model for the other controller and view then it loads the record correctly on index view ,but when i update the required it fires validation error as all my fields are mark as required so it ask to input the rest of four fields value as well.
I have tried to remove these un-necessary fields from model in controller code before saving using Bind([Exclude]"""") but it did not work.
I have tried modelstate.remove("") this approach works fine for all fields but my password field is still throwing validation error . someone says you need to use viewmodel and in viewmodel you have to put both of your model like the full model and small model, I want to ask how my small model would be mapped to my user table (as table["tableName"] this cannot be applied to two models and pointing to same table without primary foriegn key relation .
Share example please i am confused
modelState.Remove("Password")
This remove all model values which are un-necessary but not removing the password field which gives error while updating the
You have required fields missing data or some fields are not null when being saved. What you are trying to do is completely ok. You are basically using a Virtual Model. This is what I do. In the "get", you fix your model and send it to the view. You work with it and then when you submit, you receive the model in the "post", but it is missing several REQUIRED fields. So what you need to do is, retrieve the same record from the database, update the 3 or 4 fields from the view model and then save that model that you retrieved with the new data. This is one way. (VM = view model)
1. send VM to view
2. add data in the view
3. submit
4. receive VM in POST Controller
5. get same record from DB
6. update the particular fields using the VM data
7. save the updated record to database. This is the record you retrieved in 5.
Another way is to have all the missing fields in the model but not showing them in the view. However you need to mention them in the view, otherwise they will not post. so you need to use
#Html.Hidefor( x=> x.NameOfField)
This will ensure that the field is sent back for posting. Any field with a [Required]... is required. You cannot miss it!
How will your small model be mapped to the Database. It will not. You will transfer the data from the small model to the model that is mapped in the database... with ALL the fields. Either you get the missing values from the database or from the view by using Hidefor.
example
dbmodel.Field1 = vm.field1;
dbmodel.Field2 = vm.field2;
dbmodel.Field3 = vm.field3;
I hope this helps

How to display in blade which rows are soft deleted?

I have created a polling system and in the backend (CMS area) I want the ability for admins to be able to remove polls. When an admin removes a poll, it should soft delete the poll. This is working as intended, however I also want to have the ability for the admin to be able to restore a poll. To do this I am displaying all of the polls (including the soft deleted polls) in the admin area.
PollController index() to get all polls
$polls = Poll::withTrashed()->get();
In the blade I want to have two different buttons for each poll. One of restoring and one for deleting but I only want to display 1 button for each poll depending on whether it can be restored or deleted.
To do this, I have put this inside the foreach in the blade:
#if($poll->trashed())
// Restore button
#else
// Delete button
#endif
However the issue is, trashed() keeps returning true for all the polls when only 1 out of the 3 polls I have are actually soft deleted. I am unsure as to why trashed() returns all of these are true?
How would I get this method working correctly? Thanks.
PART 1
It depends on your query. When using soft deletes, Laravel will query all models that are not soft-deleted by default. When you also want to get the soft-deleted models, you need to call the withTrashed() method on your query. Read more here:
https://laravel.com/docs/5.5/eloquent#querying-soft-deleted-models
To understand what withTrashed() does, you need to understand how soft-deleting works. Soft-deleting models works by adding a new column to your database tables called deleted_at. It's value defaults to null. When you soft-delete a model, Laravel will put the current timestamp into that column. Therefore, this field doesn't contain a null value anymore.
When querying models when using soft-deletes, Laravel appends a deleted_at is null condition to the query. Calling the withTrashed() method, removes that condition from the query.
Have a look on the source of the default query modifier and the withTrashed method.
PART 2
That are events. You can call that to tell Laravel, that it should execute that specific closure when this event happens. In your example, it is listening for the "deleting" event. See more on that here:
https://laravel.com/docs/5.5/eloquent#events
PART 3
You can entirely delete soft-deletable models with the forceDelete() method. See "Permanently Deleting Models" here:
https://laravel.com/docs/5.5/eloquent#querying-soft-deleted-models
FOR EXAMPLE
$items = App\Model::withTrashed()->get();
If you want to restore a single item, just find it by ID:
$item = App\Model::find($id);

Why Laravel/Eloquent single object save() updates multiple records

I'm fetching a specific record with a DB table using
$myTableObj = MyTable::where(['type' => $sometype])->first();
Getting it successfully, updating some fields and saving with
$myTableObj->save();
Surprisingly, this record is updated along with another record that also has 'type' = $sometype. What can be done to prevent this?
NOTE: originally the table did not have the auto increment id field, but I have read in forums that it may make problems in Laravel so I did add it, which did not solve the problem.
Method save() working with 'id' filed only.
You can try this
$myTableObj = MyTable::where(['type' => $sometype])->update(['something' => 'value']);
Source
I understand update() is to update, but, my answer works fine and fits good for update too. Its useful where you dont want columns to be defined once again for update, (sp when they are not fillable, its tested with primary key as condition)
$myTableObj->save(); basically its for saving new record, if you want to update that row you can update like below code:
$myTableObj=new MyTable;
$myTableObj->exists=true;
$myTableObj->type=$sometype;//this is your condition, identify
$myTableObj->update();
I think what's happening here is Laravel is saving as well as updating row.

Algolia Update Index On Relational Database change with Laravel Scout

I have implemented Aloglia for my Movies table with actors as relational table and it works fine.
Problem:
When I update any movie its also updating algolia index (its good). But how can I update index if I made any change in relational table (for example update an actor of movie).
How to push a specific record manually with laravel scout.
Thanks
The issue itself lies in laravel's events. Whats happening is scout is listening for an 'updated' event which only occurs in laravel when the model object is saved and is dirty (aka value differ from that in the db).
There are two ways you can do this.
The bad lazy way would simply be to add ->touch() to the model prior to save - this will force the updated_at field to update and ultimately trigger the updated event. This is bad as you're wasting a DB query.
The second and preferable way is to register another observer on 'saved' which triggers regardless of whether or not the object is dirty. Likely you either want to check if the model is dirty and only index when its not (to prevent double indexing from the updated event) or just de-register the 'updated' listener that comes in Scout.

Resources