Revenue Graph Goal0:00
Let's imagine that you've been assigned the task of generating a graph to display revenue grouped by each month. So in this case we can see in January they made $12,000, in February $9,000, March $17,000, and you get the basic idea. Okay, so we're going to have to perform a database query, we're going to have to massage that data in some way to prepare it for the graph, and then of course we have to render the graph. Let's see how. If I switch over to my routes file, yeah, here's the basic flow. We need to fetch the revenue, group it by the month, and then we need to load a welcome
Inspecting Performance Data0:29
If I switch over to my routes file, yeah, here's the basic flow. We need to fetch the revenue, group it by the month, and then we need to load a welcome page. Okay, so we're going to return a view called welcome, and then pass through the revenue. Now that takes care of the second task, but what about the revenue? How's that going to work? Okay, well check this out. I've set up a table. So if I boot up php artisan tinker, you'll see that I have a Performance model, and if we fetch all the records, yeah, you can see each one of these represents a row in our database.
So if I boot up php artisan tinker, you'll see that I have a Performance model, and if we fetch all the records, yeah, you can see each one of these represents a row in our database. So we list the revenue for the day, the new users, the total number of users. It basically calculates all of these figures once per day. All right, so how can we do this? Fetch the revenue, so at the top I'll say use App\Performance, and then I also know I'm going to use Carbon, so I will go ahead and pull that in as well. Okay, so yeah, we could say Performance::all(), but as we just saw, that's going to give me a list of all of these records, and really I want to do some calculations here. I want to group them according to the month, and then I also want to figure out the total.
Querying and Grouping Revenue1:28
a list of all of these records, and really I want to do some calculations here. I want to group them according to the month, and then I also want to figure out the total sum for the revenue. So let's take this in a few steps. First we could say performance where the created_at field is greater than or equal to Carbon::now()->startOfYear(). I love Carbon. So all we're saying here is create a where constraint so that we can only return the rows that were created this year. But now notice that when I come back to this in six months, it's going to take me a minute.
rows that were created this year. But now notice that when I come back to this in six months, it's going to take me a minute to kind of parse that and figure out what it's doing. So if I switch to my Performance model, you'll see that I've added a custom scope here called thisYear. I think that works well. So that means I can say performance::thisYear() and get the exact same thing. Okay, what else? Well, I need to perform a select query. And I know I'm going to be doing some calculations here, so I will make this a raw query.
Well, I need to perform a select query. And I know I'm going to be doing some calculations here, so I will make this a raw query. And that way Eloquent won't try to manipulate it in any way. Okay, so in this case, I am using SQLite. If I were using something like MySQL, I could use functions like month and year. In general though, a quick tip, be a little careful about those because they mess up your indexes. So if you're dealing with a massive data set, that could be a problem and you may not want to reach for those functions. But in our case, it's okay.
to reach for those functions. But in our case, it's okay. Anyways, with SQLite, those functions don't exist. So yeah, you end up having to do things like strftime. So for example, if I want the month, I could say, yeah, translate the given date, create it at, and all I want is the month number. So 01, 02, 03. Okay, so we want the month name and I will alias that to month. Next, I want the total revenue, right? So I could say, give me the sum for the revenue column as revenue.
Next, I want the total revenue, right? So I could say, give me the sum for the revenue column as revenue. But now in order for these sorts of calculations to work, yeah, you have to perform a groupBy so it knows how to calculate that sum. So we can do that here. Group by the month. Finally, a lot of those columns I don't need. I don't care about the users or the newUsers. So why don't we just pluck the revenue? And let's see what this looks like.
So why don't we just pluck the revenue? And let's see what this looks like. So I'm going to save this. We still have a bit more work to do, but let's dd and dump the revenue and view this in the browser. All right, so if we take a look, we do have a collection of four items. It looks like we're calculating that properly, but notice the keys here, 01, yeah, that's just the default there. Maybe we can make the key equal to the month, like this. I can pass a second argument here.
Maybe we can make the key equal to the month, like this. I can pass a second argument here. So month. And now if I come back, that will correspond to January, February, March, and April. So that looks pretty good to me. We fetched our data. We, of course, you can store this anywhere you want. If you want a repository or a method on the model, anything's fine. It doesn't matter in this case. But anyways, we make our data, we fetch our data, and now we pass it to our view.
It doesn't matter in this case. But anyways, we make our data, we fetch our data, and now we pass it to our view. Let's go to welcome. In this case, it's empty. Now it's true. You can create dedicated components for each type of graph, like we talked about in the last lesson. But generally, I would say this is most useful when there's a lot of logic associated with that graph. So maybe to render, you have to perform an AJAX query, and then for the data that's returned,
Extracting Keys and Values5:20
And that should take care of the majority of your needs. Okay, so as before, this would accept your keys, and it would accept the values. Okay, but now how do we parse that? How do we get the keys? Well take a look at this. Right now we have a Collection object, right? So if I refresh, it's a Collection. Okay, well that means there's so many different methods you can play around with. For example, I can call a keys method, and this is sort of like calling array_keys.
For example, I can call a keys method, and this is sort of like calling array_keys. It'll give me a new array that contains only the keys for the collection. And then further, we have a method called values. And that'll do the opposite. Okay, so that's a nice way to extract these into two different arrays. And yeah, we can either pass that through in this way, or of course you can pull in something like underscore or a similar library and perform these extractions with JavaScript rather than on the server side. Either option is fine.
rather than on the server side. Either option is fine. In this case, we'll do it through php. So we want revenue keys. And remember, that's still going to return a collection, so it will automatically be converted to JSON. Okay, next, revenue values. What next? Well, let's go into our resources directory, assets.js. And right now, I have my main.js file, but we don't have anything else yet.
Reusing Graph Component6:35
Well, let's go into our resources directory, assets.js. And right now, I have my main.js file, but we don't have anything else yet. Why don't we pull over that graph component we made in the last lesson? So if we switch over to the repo for that, yeah, let's see what we have. resources, assets, js, components, graph. And we were treating that almost like an abstract class in the last lesson. But now we'll modify it just a bit so that we don't always have to create a custom graph component. Anyway, so let's create this resources, assets, js, components, graph.js. And I will paste that in.
Anyway, so let's create this resources/assets/js/components/graph.js. And I will paste that in. Next, if we go back to main.js, I will import that graph. So import graph from components/graph. And then I will nest that as a child component for our root view instance. But now we're still not done. If I go back to the graph, yeah, if we want to be able to use this as it is, well, at some point, we do have to call the render method and we do have to prepare the data. So why don't we do this? Why don't we add a ready method directly here?
Rendering Chart with Props7:42
So why don't we do this? Why don't we add a ready method directly here? And then we can call render like this. Okay, and just so you don't have to watch me write all of this out, I will paste this in and clean this up. Okay, so what's going on here? We're calling a render method, and then we're passing through all of the chart.js specific configuration. Like the labels we want to use, I'm going to assume that we can reference a keys property here.
Like the labels we want to use, I'm going to assume that we can reference a keys property here. Next, for the data set itself, I'm hard coding the label here, but of course, you can pass that in as a custom property too. Finally, down here, like the keys we passed in, we'll also pass in an array of values. So let's make sure that we can read these. I'll say props, keys, and values. And now I think that should do the trick. So from our welcome view, yeah, we pass these in, our keys, our values. If we switch back, once the component has loaded, we call a render method and we pass
So from our welcome view, yeah, we pass these in, our keys, our values. If we switch back, once the component has loaded, we call a render method and we pass those specific keys and values through to our render method, which creates the chart, binds the data, and generates the legend. Okay, so this looks pretty good to me. Real quick, why don't we switch that over to a default line graph. So let's compile this down. Looks good, no errors. And if I switch back to Chrome, there we go. We're rendering the data.
