I'm new to Laravel (and coding in general), and I have a little Pizza Ordering system that stores the orders placed by clients to a local Pizzaria.
Inside the "New Order" form, when you start typing down the name of a pizza (four cheeses, chicken, yada yada), the program returns a query search that is run every 2 keydowns with products with a similar name.
Here's the search query, a pretty simple and basic one:
$pesquisa = json_decode($request->getContent(), true);
$produtos = Produto::select('nome', 'valor')->where('nome', 'LIKE', '%'.$pesquisa.'%')->get();
return response()->json($produtos);
Here's the "problem" I'm having: The current database has about 50 items, and it takes about ~500ms to get a return. This in my local machine, the problem gets a little bigger when it's actually hosted in a server, where it can spike from ~500ms to ~2s, depending on user connection.
In my study, I've heard about caching, and that it can shorten or remove the need for queries (which was already implemented in the "show all orders placed" list, and REALLY minimized the speed of loading), but I don't know if caching can be done with user-inputted search?
First question: How would one go about saving those pizza names to a cache, while still sorting through them based on user input?
Second question: Is caching like this the "best" way to speed up user-inputted search? Is there something else I should be doing first? (I've heard that the 'LIKE' query search is the slowest there is... should I research and try another type?)
All explanations, tips and tricks are greatly appreciated! Thank you!
For Caching you can use this
$pesquisa = json_decode($request->getContent(), true);
return Cache::remember($pesquisa, $seconds, function ()use($pesquisa) {
$produtos = Produto::select('nome', 'valor')->where('nome', 'LIKE', '%'.$pesquisa.'%')->get();
return response()->json($produtos);
});
rememberForever can also be used instead of remember
Related
My situation is this: I have multiple components in my view that ultimately depend on the same data, but in some cases the view state is derived from the data. How do I make sure my whole view stays in sync when the underlying data changes? I'll illustrate with an example using everyone's favorite Star Wars API.
First, I show a list of all the films, with a query like this:
# ALL_FILMS
query {
allFilms {
id
title
releaseDate
}
}
Next, I want a separate component in the UI to highlight the most recent film. There's no query for that, so I'll implement it with a client-side resolver. The query would be:
# MOST_RECENT_FILM
query {
mostRecentFilm #client {
id
title
}
}
And the resolver:
function mostRecentFilmResolver(parent, variables, context) {
return context.client.query({ query: ALL_FILMS }).then(result => {
// Omitting the implementation here since it's not relevant
return deriveMostRecentFilm(result.data);
})
}
Now, where it gets interesting is when SWAPI gets around to adding The Last Jedi and The Rise of Skywalker to its film list. We can suppose I'm polling on the list so that it gets periodically refetched. That's great, now my list UI is up to date. But my "most recent film" UI isn't aware that anything has changed — it's still stuck in 2015 showing The Force Awakens, even though the user can clearly see there are newer films.
Maybe I'm spoiled; I come from the world of MobX where stuff like this Just Works™. But this doesn't feel like an uncommon problem. Is there a best practice in the realm of Apollo/GraphQL for keeping things in sync? Am I approaching this problem in entirely the wrong way?
A few ideas I've had:
My "most recent film" query could also poll periodically. But you don't want to poll too often; after all, Star Wars films only come out every other year or so. (Thanks, Disney!) And depending on how the polling intervals overlap there will still be a big window where things are out of sync.
Instead putting the deriveMostRecentFilm logic in a resolver, just put it in the component and share the ALL_FILMS query between components. That would work, but that's basically answering "How do I get this to work in Apollo?" with "Don't use Apollo."
Some complicated system of keeping track of the dependencies between queries and chaining refreshes based on that. (I'm not keen to invent this if I can avoid it!)
In Apollo observables are (in components) over queried values (cached data 'slots') but your mostRecentFilm is not an observable, is not based on cached values (they are cached) but on one time fired query result (updated on demand).
You're only missing an 'updating connection', f.e. like this:
# ALL_FILMS
query {
allFilms {
id
title
releaseDate
isMostRecentFilm #client
}
}
Use isMostRecentFilm local resolver to update mostRecentFilm value in cache.
Any query (useQuery) related to mostRecentFilm #client will be updated automatically. All without additional queries, polling etc. - Just Works? (not tested, it should work) ;)
I wanna move the business logic out of controller actions. I read a lot about repository pattern in laravel with tons of examples.
However they're usually pretty straightforward - we have a class that uses some repository to fetch a list of all possible records, the data is returned to the controller and passed to the view.
Now what if our list isn't all the possible records? What if it depends on many things. For example:
we display the list as "pages" so we might need X records for Y-th page
we might need to filter the list or even apply multiple filters (status, author, date from - to etc)
the user can change the sorting of the data (for example by clicking the table column titles)
we might need some data from other data sources (joined tables) or it might even be used for sorting (so lazy loading won't work)
Should I write a special method with all these cases in mind? Something like that:
public function getForDisplay(
$with = array(),
$filters = array(),
$count = 20,
$page = 0,
$orderBy = 'date',
$orderDir = 'DESC'
)
{
//all the code goes here
return $result;
}
And then call it like this from my controller:
$orders = $this->orders->getForDisplay(
array('customer', 'address', 'seller'),
Input::get('filters', array()),
20,
Input::get('page', 0),
Input::get('sort', 'date'),
Input::get('direction', 'DESC')
);
This looks wrong already and we didn't even get to the repositories yet.
What are the best/correct practices for solving situations like this? I'm pretty sure there has to be a way to achieve the desired results without adding all the possible combinations as a method arguments.
Use the repository pattern just for business model updates and you'll end up with very specific query methods (the Domain usually doesn't need many queries and they are pretty straightforward). For UI/reporting querying purposes, you can use a simple DAO/Service/ORM/QUery Handler , that will take some input and returns the desired data (at least part of the view model).
Since you're already using an ORM, you can use it directly. Note that you can use the ORM for domain updates also, but inside a repository's implementation i.e the app only sees the repository interface. We care about separation at the business layer, for UI querying you can skip the unneeded abstraction.
Btw, because we're talking about design, everything is subjective and thus, there's no single best/optimum way of doing things.
The way I see it, when building dynamic charts with filters, each time the user requests filtered data I can
Execute a new MySQL query, and use MySQL to do the filtering.
SELECT date,
SUM(IF( `column` = `condition`, 1, 0)) as count
...
Execute a new MySQL query, and use the server-side language (PHP in my case) to filter.
function getData(condition) {
$resultSet = mysqli_query($link, "SELECT date, column ... ");
$count = 0;
while ($row = mysqli_fetch_assoc($result_set)) {
if ($row['column'] == 'condition') {
$count++;
}
}
}
Initially execute a single MySQL query, pass all the data to the client, and use Javascript & d3 to do the filtering.
I expect the answer is not black-and-white. For instance, if some filter is barely requested, it may not make sense to make the other 95% of users wait for the relevant data, and thus the filter would necessitate a new data call. But I'm really thinking about edge cases - situations where filters are used regularly, but idiosyncratically. At times like this, is it better to put filtering logic in front-end, back-end, or within my database queries?
Generally, if the filtering can be done on the front end, it should be done there. The advantages are:
Doesn't matter if your server goes down
Saves you bandwidth costs
Saves the user waiting for round trip time
The disadvantages are that it may be slower and more complicated than it would be on the backend. However, dependent on data volume, there's a lot of cases (like your examples) where Javascript is plenty good enough. d3 even has a built-in filter function:
//remove anything that isn't cake
d3.selectAll('whatever')
.filter(function(d){return d.type != 'cake'})
.remove()
If you need more complex filtering, such as basic aggregates, you can use Crossfilter (also from Mike Bostock) or the excellent d3+crossfilter wrapper dc.js.
I need to run 2 sites, One for high end customers and one for low end customers.
They both pretty much do the same thing but look different. For example they both might have product A, but the price and description (no duplicate content) will be different.
However, for the most part, they will have different products, high end products for the high end site and low end products for the low end site.
It is easier if both sites use the same underlying structure, so the many overlapping parts won't have to be duplicated. Especially during changes. But it needs to allow for different logic per site when necessary.
So if I point 2 different domains (lets call them highend.com and lowend.com) to the same server. What is the best way to handle this?
Should/Can I determine what domain is being used in the app_controller and specify which layout to use or set a variable to be used by the child controllers? How?
Is there a way I can prefix some fields in the database. For example, if there is a products table and a products.highend_name and a products.lowend_name fields. Then in the code just refer to Product.name but depending on which domain is used, use the correct one. But for overlaping feilds like style_number there is no prefix just products.style_number since its the same either way. Is there a way to set something like this up? How?
Whats the best way to go about doing something like this? Is there a different framework that could handle this better?
In the controller:
$host = $_SERVER['SERVER_NAME'];
if($host=='www.posh.com')
{
$this->layout = 'posh';
}
elseif($host=='www.cheap.com')
{
$this->layout = 'cheap';
}
else
{
$this->layout = 'default';
}
Something I'm curious about.. What would be "most efficient" to cache the generation of, say, an RSS feed? Or an API response (like the response to /api/films/info/a12345).
For example, should I cache the entire feed, and try and return that, as psuedo code:
id = GET_PARAMS['id']
cached = memcache.get("feed_%s" % id)
if cached is not None:
return cached
else:
feed = generate_feed(id)
memcache.put("feed_%s" % id, feed)
return feed
Or cache the queries result, and generate the document each time?
id = sanitise(GET_PARMS['id'])
query = query("SELECT title, body FROM posts WHERE id=%%", id)
cached_query_result = memcache.get(query.hash())
if cached_query_result:
feed = generate_feed(cached_query_result)
return feed
else:
query_result = query.execute()
memcache.put("feed_%s" % id, query_result)
feed = generate_feed(query_result)
(Or, some other way I'm missing?)
As for my experience, You should use multiple levels of cache. Implement both of Your solutions (provided that it's not the only code that uses "SELECT title, body FROM posts WHERE id=%%". If it is use only the first one).
In the second version of code, You memcache.get(query.hash()), but memcache.put("feed_%s" % id, query_result). This might not work as You want it to (unless You have an unusual version of hash() ;) ).
I would avoid query.hash(). It's better to use something like posts-title-body-%id. Try deleting a video when it's stored in cache as query.hash(). It can hang there for months as a zombie-video.
By the way:
id = GET_PARMS['id']
query = query("SELECT title, body FROM posts WHERE id=%%", id)
You take something from GET and put it right into the sql query? That's bad (will result in SQL injection attacks).
Depends on the usage pattern, but all things equal I'd vote for the first way because you'll only do the work of generating the feed 1 time.
It really depends on what your app does... The only way to answer this is to get some performance numbers from your existing app. Then you can find the code that takes the largest amount of time and work on improving that one.
As others have suggested here I'd profile your code and work out what is the slowest or most expensive part of the operation.