Larvel Scout scoped to Logged in user via Auth::user() not working | Issues or technique wrong - laravel

Basically I have installed Laravel Scout with TNT Search, all of the configs are correct and they work, I even got a nice search bar to work with an ajax workflow
But the results from the models are from the entire database collection where as my requirement is only to show results of record of records created by the user who is currently logged in session
The models are related via Eloquent Relationships, they work when access normally via the Auth::user() method but the scout search seems to not be attached to any of the models via the Auth::user() example:
App\CourtCase::search($request->q)->get();
The above works but will return any result in the model (and underlying table) without any regards to if the record is owned or related to the user logged in. My intention is however
Auth::user()->courtcase()->search($request->q)->get();
The error i get is the following
message Call to undefined method Illuminate\Database\Eloquent\Relations\HasMany::search()
exception BadMethodCallException
file /home/vagrant/code/legistant/vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php
Clearly this is because Laravel Scout is not attached or available via the Auth::user() method, I can write code to filter the results after they are returned, but imagine the over head of results before being scope by user, it seems like a waste of all that querying, is there a way to scope to user then use Laravel scout search similar to Auth::user()
Can modify the Auth behavior to attach scout, will it be overwritten using a composer update of Laravel?

So as of 05/05/2019
Laravel Scout does not have a method of scoping queries to Auth user related models, because honestly it is a quite difficult logic to process, especially because the relationships are arbitrary and the keys can be different, a scoping function built internally or externally would have the same computational cost, there is no optimizing here
$results = App\CourtCase::search($request->q)->where('lawyer_id', Auth::id())->get();
This is how I solved it, by attaching a where condition to the search result where it will only show details matching the key, if you are modifying existing code of an application, remember initially scout might show cached results that don't match the filter, which confused me until I refreshed the code
Better code is welcome

Related

How to make an API endpoint in Laravel, that can be used to search any table we want?

Background
Let us consider a hypothetical scenario. Suppose we have five different tables in our database,
Customers
Categories
Products
Orders
OderDetails
Our client wants us to add a search bar to the frontend, where a user can search for a specific product and upon tapping the search button, matching products has to be displayed on the frontend.
My Approach for Tackling This Problem
In order to add the aforementioned functionality, I came across the following strategy.
👉 I would add an input box to ender the product name and a submit button.
👉 Upon pressing the submit button, a GET request will be sent to the backend. On the query parameters, the product name that has been entered by the user will be included
👉 Once the GET request is received to the backend, I will pass it down to the ProductsController and from within a method defined inside the ProductController, I will use the Product model to query the products table to see if there are any matching results.
👉 If there are any matching results, I will send them to the frontend inside a JSON object and if there aren't any matching results, I will set a success flag to false inside the JSON object and send it to the frontend
👉 In the frontend, if there are any matching results, I will display them on the screen. Else, I will display "No Results Found!"
Problem with My Approach
Everything works fine if we only want to search the products table. But what if, later our client tells us something like "Remember that search functionality you added for the products? I thought that functionality should be added to orders as well. I think as the list of orders by a user grows and grows, they should be able to search for their previous orders too."
👉 Now, since in our previous approach to search products was implemented in the ProductController using the Product model, when we are adding the same functionality to Orders, WE WOULD HAVE TO DO THE SAME THINGS WE DID IN THE ProductsController AGAIN INSIDE THE OrdersController USING THE Order model. It does not take seconds to understand that this leads to duplication of code.
Problem Summary
❓ How do we add an API endpoint in laravel for a search functionality that can be used to search any table in our database and get results, instead of the search functionality being scoped to a specific controller and a corresponding model?
A good start would be to create a trait called something like searchable and add it to all the models you want to search and put any shared logic between the different models in there. Possibly you'd want to manually allow different columns so in each model you have an array of searchable columns which you'd refer to in your trait.
Your controller would have to point to the related model and call a method in the trait that searches.
As others have pointed out this is a high level question so I won't go too much in detail. Of course there are a million ways to implement this, just try and keep shared logic in place.

Laravel Baum how to get related models on getAncestorsAndSelf() method

Using Laravels Baum package for nested sets I try to fetch the relational model on getAncestorsAndSelf() but does not work
$tree = \App\GroupDeprecator::where('id', $this->team_id)->with('lead')->first();
dd($tree); //this one shows the user object
dd($tree->getAncestorsAndSelf());//this one does not shows the user object
What went wrong in this case?
How you solved it? the code looks fine and should work, unless it has been manually updated in the database, I would do:
GroupDeprecator::rebuild(true)
to update the lft, rgt and depth columns

How to store log of all changes to Eloquent model in Laravel 5.4?

I want to store changes to all fields in Eloquent model into database.
I can do it using created and updated events but there is a problem with multiple foreign relations (described as separate tables).
Example:
User
login
Roles -> hasMany
When I update login field it is easy to write old and new value into database, but when I update Roles relation nothing happens.
How can I track all foreign relations (like hasMany, hasManyThrough, ManyToMany)?
Owen-it has a very nice Library called laravel-auditing, which keeps an easy to query list of all changes that are made to a model, and I think it does quite an awesome piece of work. Have used it and it is worth it to try out.
There is no embedded and simple method to do it.
Eloquent doesn't provide any method to implement observers on related models by design. Many proposal in this way have been rejected by Taylor (just one example).
The only thing you can do, is to create your own methods to do it.
You have many possibilities, here are some of them in order of complexity (some of them are "dirty" :-)
add a created and updated observer on each related model
override the save() or create your own saveAndFire() method on your eloquent instances, and from that method retrieve the parent and call its log methods before saving. (this is a little bit "dirty" imho)
encapsulate all your persistence layer and fire events yourself on saving objects (look at the repository pattern, for example)

Query builder on hasMany relation

Problem
I'm creating an API with Laravel. Every server can have more than one contact, and every contact belongs to one server - as such, contacts are set up with a belongsTo relationship, and servers have a hasMany relationship to the contacts.
A user can have many servers via its hasMany relationship. Thus, when creating a server, we simply invoke $user->server()->create([values]), which works just fine.
The issue is that when we try to invoke it further, as with contacts - we hit a wall where we get:
Call to undefined method Illuminate\Database\Query\Builder::contacts()
When using: $user->server()->contacts()->create([]).
The method does exist in the server model.
We also have a hasManyThrough relationship on the user model, specifying that a user has many contacts through servers.
When calling $user->contact()->create([]), we instead get:
Call to undefined method Illuminate\Database\Query\Builder::create()
Does anyone have a clue what might be causing this? Do query builders not allow for this type of chaining, or am I missing something blatantly obvious? As you can see, I've tried a few different methods but can't seem to produce a working result.
Cheers!
As you said:
A user can have many servers via its hasMany relationship.
so You cannot build another relation on relation that can return many results (becasue at the end we don't know exacly which server id we should set in contact new record).
So what you can do is to itterate over:
foreach ($user->server as $server) {
$server->contacts()->create($values);
}
You see: now you have specific server with id to put into new contact record.

Using two different slugs on a route

I'm upgrading an old procedural site to laravel 5.2, and I'm struggling with the old routes I made.
On this website, the routes were made like this : {user_slug}/{content_slug}.html. For the moment, I use cviebrock/eloquent-sluggable to generate the slugs, but I'm open to another one if this one cannot meet my needs.
I have two questions :
Can I make the content-slug unique, but per user ?
How can I write the route and the controller in order to match the correct user slug ad the correct content slug ?
I have not done this myself but I believe there would be a way in the validation rules to do this. Here is an untested rough draft to check content_slug in the posts table but only check uniqueness where the user_id field equals a variable:
'content_slug' => "unique:posts,content_slug,NULL,id,user_id,$user->id"
Depending on who you ask, they may advise you (either instead of or as well as doing the above) to set up a key in the database based on the user_id and content_slug fields. This way the database returns an error if an insert is attempted as well as gives a performance boost when running a query off that index. Queries off of an index can literally give an exponential performance increase.

Resources