Laravel model event to restrict query - events

Is there a way in L4 to use model events to restrict all queries run using that model.
i.e. An event that fires before the query that allows me to modify the query.
Under certain circumstances I want to further restrict the records that are returned from all queries against a particular table/model.
e.g. A user of type customer should only see orders relating to them, so would like to restrict all Order::get(), Order::first() etc. by adding Order::where('customerid', '=', '').
It would be good if it's possible to set this up on the model so I don't have to consider it each time I run a query - I can run it knowing that these basic conditions will be applied automatically where needed.

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.

DynamoDB Appsync Query on multiple attributes

My app uses AppSync resolvers to fetch data from DDB and return it to our front-end. One table we have is for Notifications. A Notification can be either pending or default (non-pending). The table itself has a primary key of notification_id and we have a GSI called userIndex to grab the notifications for a user, with a sort key of timestamp.
In the app, I show all notifications in a list, pending first and then default. Given that a user may have many notifications, I'd like to implement pagination to fetch a batch at a time. The only way I've been able to do this is to
change the query to include a isPending parameter, which I use as a filter expression for the query to only return notifications that are isPending or isNotPending.
Store two "nextTokens", one for each isPending and isNotPending, along with corresponding lists.
Make separate queries for pending/non-pending, and use the filter to return to the appropriate list.
This is obviously inefficient and I am re-reading data from DynamoDB. My question is, given my DynamoDB table/requirements, is there a way I can paginate so that I can get all the pending notifications first (sorted by timestamp) and then all the default notifications next (sorted by timestamp) by using one query and one nextToken
I've seen the use of #model and #key, but I haven't been able to make it work in my app.
Thanks!
No, not really. There is a hard limit on returns for a Dynamodb query - and that cannot be bypassed. the only way to make use of nextToken is another query.
However, it is also worth noting that the FilterExpression happens after the data has already been retrieved and is filtered client side. It does not reduce the documents pulled from the query - only whats displayed. So the next token is still going to be (relatively) the same for each query. You can instead filter it yourself after the call before the next pagination query and save yourself a little bit in terms of multiple calls.

Laravel Lighthouse: how to delete records that match certain conditions (rather than deleting via primary key)

In Laravel Lighthouse GraphQL, I'd love to be able to delete records that match certain conditions rather than passing just an individual ID.
I get this error:
The #delete directive requires the field deletePostTag to only contain a single argument.
This functionality seems currently unsupported, but if I'm wrong and this is actually supported, please let me know, because this would be the most straightforward approach.
So then my second approach was to try to first run an #find query to retrieve the ID of the record that I want to delete (based on certain fields equaling certain values).
But https://lighthouse-php.com/4.16/api-reference/directives.html#find shows:
type Query {
userById(id: ID! #eq): User #find
}
and does not show how I could provide (instead of the primary key ID) 2 arguments: a foreign key ID, and a string.
How can I most simply accomplish my goal of deleting records that match certain conditions (rather than deleting via primary key)?
I'm not sure about the #delete functionality regarding multiple arguments, but from what you've posted that appears to be unsupported at the moment. Regarding your query, you should instead use something like #all in conjunction with #where which would allow you to filter the collection by as many vars/args as you'd like. If your argument list grows beyond 3 or so, I would take a look at Complex Where Conditions. They have worked very well for my team so far, and allow a lot of filtering flexibility.
Also take a look at the directive's docs stating:
You can also delete multiple models at once. Define a field that takes a list of IDs and returns a collection of the deleted models.
So if you return multiple models you'd like to delete from your query, you may use this approach to delete them all at once.

What is the best way to get all eloquent model instances meeting multiple requirements inside different relations

I have multiple Orders which have related Payments and related InvoiceCorrections.
I want to fetch all orders where the
order->payments->sum('amount')differs from(order->amount - order->invoiceCorrections->sum('amount')).
What would be the best way to archive this, while keeping a good performance?
The payments should have their own column containing correction information which gets updated via a model observer, otherwise your queries are going to get very complex and messy very fast.

When does laravel make a new query when getting relation

If i get users company name like this:
Auth::users()->company->name
And if i do it multiple times on same function, it only makes one query for relation and the rest it uses the value retrieved the first time.
I want to know, how deep this goes and is it possible to make it go deeper. If i, for example, use it in controller and then again on the view, does it make a new query? How can i make it so it would only make the same query once per request (page view)?
Clarification:
Question was not about eager loading. The question was about how to avoid making the SAME query more than once. Something that is used a lot, like, users company, users tag, users settings. Data that many parts of my application may need but making a new query every time in different parts of my app feels like wasting resources.
When using Eloquent, if you have a model related to another one, you can eager-load the related models in order to minimize the number of queries.
For example, if you have a Movie model related to a Genre model (a movie belongsTo a genre, a genre hasMany movies). If you have 10 movies:
$movies = Movie::all();
foreach ($movies as $m) echo $m->genre->name;
// 11 queries are gemerated (get all + 1 per item)
whereas :
$movies = Movie::with('genre')->get();
foreach ($movies as $m) echo $m->genre->name;
// 2 queries are gemerated (get all movies + get all genres for those movies)
Note that you can eager-load multiple and/or nested relations.
e.g.: with(['genre', 'actors', 'actors.profile'])
Edit following clarification:
You can then use a middleware which will load your common data. Register that middleware globally so that it can be executed for all requests.
If needed, make an additional facade and a kind of global data manager to make that data available later on.

Resources