Building Dynamic Charts with Laravel, Livewire, and Neon Postgres
Learn how to build dynamic charts with Laravel, Livewire, and Neon Postgres
Laravel is an amazing PHP framework for building web applications, while Livewire provides a simple way to build dynamic interfaces using PHP.
In this guide, we'll walk through the process of creating a dynamic analytics dashboard for a SaaS application using Laravel Breeze for authentication, Livewire Charts for data visualization, and Neon Postgres for data storage.
We'll build interactive charts that display key metrics such as daily active users, feature usage trends, and user signups vs. cancellations.
Prerequisites
Before we begin, make sure you have:
- PHP 8.1 or higher installed
- Composer for managing PHP dependencies
- A Neon account for Postgres hosting
- Basic familiarity with Laravel, Livewire, and Postgres
Setting up the Project
-
Create a new Laravel project:
-
Install Laravel Breeze with Livewire:
-
Install the Livewire Charts package which we'll use for data visualization:
-
Install the Livewire Charts assets which include the necessary JavaScript and CSS files:
-
Set up your Neon Postgres connection in the
.env
file: -
Run the migrations to set up the users table and other Breeze-related tables in your Neon Postgres database:
This will create the necessary tables for user authentication and session management.
Additional Database Tables
Now that we have the users
table set up by Breeze, let's create migrations for our additional SaaS analytics data.
For the purpose of this guide, we'll track feature usage and subscriptions. You can adjust these tables based on your specific application requirements.
-
Create migrations:
Note that the naming convention for the migration files is important to make sure that your migrations are named correctly with the
create_
prefix followed by the table name. -
Update the migration files:
The above commands will create two migration files in the
database/migrations
directory. Update the migration files as follows:For the
create_feature_usage_table
we'll track the usage of different features by users, so we'll store theuser_id
,feature_name
, and theused_at
timestamp:For the
create_subscriptions_table
we'll track user subscriptions, including theuser_id
,plan
,started_at
, andended_at
timestamps: -
With the migrations in place, run the migrations to create the tables in your Neon Postgres database:
This will create the
feature_usage
andsubscriptions
tables in your database.
Creating Models
Laravel's Eloquent ORM provides a convenient way to interact with your database.
By defining models, we can represent and manipulate the data in the FeatureUsage
and Subscription
tables, which we created earlier through migrations.
In this step, we'll create models and set up relationships to ensure efficient data retrieval and interaction.
Step 1: Generate the Models
Start by creating the FeatureUsage
and Subscription
models using Laravel's Artisan command:
This will generate two model files in the app/Models
directory corresponding to the feature_usage
and subscriptions
tables in your database.
Step 2: Define Relationships in the Models
Now, let's update the model classes to define relationships between the tables. The FeatureUsage
and Subscription
models will be connected to the User
model via foreign keys.
FeatureUsage
Model
2.1 In the app/Models/FeatureUsage.php
file, define the relationship with the User
model. Since each feature usage entry belongs to a specific user, we will use a belongsTo
relationship:
The above defines the following:
fillable
: Specifies which attributes can be mass-assigned, in this case,user_id
,feature_name
, andused_at
.user()
: Defines abelongsTo
relationship, meaning eachFeatureUsage
belongs to a singleUser
.casts
: Automatically casts theused_at
column to adatetime
object for easier manipulation in PHP.
Subscription
Model
2.2 In the app/Models/Subscription.php
file, define relationships with both the User
model and handle timestamps (started_at
and ended_at
) correctly. This indicates that each subscription belongs to a user and includes a plan
:
The above defines the following:
fillable
: Makes sure that the fieldsuser_id
,plan
,started_at
, andended_at
are mass assignable.user()
: Defines abelongsTo
relationship where eachSubscription
is linked to a specificUser
.casts
: Automatically casts thestarted_at
andended_at
columns todatetime
objects.
Step 3: Database Relationships
Once these relationships are defined, Eloquent provides methods to interact with related data. For example:
- Access a user's feature usages with
$user->featureUsages()
. - Retrieve a user's subscriptions with
$user->subscriptions()
. - Easily manipulate and retrieve data for timestamps (e.g.,
started_at
,ended_at
, andused_at
).
Building the Dashboard
We’ll create a simple SaaS dashboard that showcases our dynamic charts which will include daily active users, feature usage trends, and user signups vs. cancellations. This dashboard will use Livewire for interactivity and Tailwind CSS for styling along with the Livewire Charts package for creating the dynamic charts.
Step 1: Create the Livewire Component
First, generate a new Livewire component for the dashboard:
This will create both the Dashboard
class in app/Http/Livewire
and the a view in resources/views/livewire/dashboard.blade.php
.
Dashboard.php
Step 2: Update In the app/Http/Livewire/Dashboard.php
file, we’ll render the dashboard view inside the main layout:
Note that we're using the layout('layouts.app')
method to specify the main layout file for the dashboard view. This layout file will contains the main structure of the dashboard.
Step 3: Create the Dashboard View
Now, let’s update the dashboard.blade.php
view with a grid layout that displays multiple charts, along with some Tailwind CSS styling to improve the design.
This view includes a header with a title and a refresh button, followed by a grid layout that displays the three charts: 'Daily Active Users', 'Feature Usage Trends', and 'User Signups vs. Cancellations'. We will create those charts components next.
Setting up Routes
With the charts dashboard view and the Livewire component in place, let's set up the routes to display the dashboard.
This route will display the dashboard view when the /charts
URL is accessed. The auth
middleware ensures that only authenticated users can access the dashboard.
Set Up Livewire Charts for the Dashboard
Now with everything in place, let's implement individual chart components.
The Livewire Charts package provides a wide range of chart types, including area charts, radar charts, and treemaps, offering flexibility to create various data visualizations.
We'll use LivewireLineChart
for 'Daily Active Users', LivewireColumnChart
for 'Feature Usage Trends', and LivewirePieChart
for 'User Signups vs. Cancellations'. To get a full list of available chart types, check out the Livewire Charts documentation.
2.1 Daily Active Users Chart
Create a Livewire component for the daily active users chart:
In app/Livewire/DailyActiveUsersChart.php
, define the logic to fetch the data:
Create the corresponding Blade view in resources/views/livewire/daily-active-users-chart.blade.php
:
2.2 Feature Usage Trends Chart
Create another Livewire component for feature usage trends:
In app/Livewire/FeatureUsageTrendsChart.php
, define the data logic:
In resources/views/livewire/feature-usage-trends-chart.blade.php
:
2.3 User Signups vs. Cancellations Chart
Create a Livewire component for user signups vs. cancellations:
In app/Livewire/UserSignupsVsCancellationsChart.php
, define the data logic:
In resources/views/livewire/user-signups-vs-cancellations-chart.blade.php
:
Step 3: Add Chart Scripts
Include the chart scripts in your main layout file (resources/views/layouts/app.blade.php
) by adding:
This will load the necessary JavaScript files for Livewire and Livewire Charts to render the interactive charts on the dashboard.
Step 4: Test the Dashboard
Run the server to access your charts dashboard if you haven't already:
Navigate to the /charts
route, and you should see the real-time interactive charts displayed on your dashboard.
Seeding the Database with Sample Data
If you don't have any data yet, you can seed the database with sample data to test the charts. First, create a seeder that populates the FeatureUsage
and Subscription
tables with mock data:
-
Generate the seeder:
-
Open the newly created seeder file (
database/seeders/SampleDataSeeder.php
) and populate it with sample data for feature usage and subscriptions: -
Run the seeder to populate the database with test data:
Once the database is seeded, refresh the charts dashboard, and you should see the charts populated with real-time data.
For more information on seeding the database, check out the Laravel documentation.
Optimizing Performance
When working with large datasets, you will have to make sure that your application is optimized for performance. This includes optimizing database queries, caching results, and using efficient algorithms.
We will cover some optimization techniques for improving the performance of your Neon Postgres application below but you should also check out the Performance tips for Neon Postgres blog post for more specific tips.
1. Database Indexing for Frequently Queried Columns
Database indexing is a key technique to speed up query execution, especially for columns used frequently in WHERE
, JOIN
, and ORDER BY
clauses. With indexes in place, the database can find records faster, making your queries more efficient.
This can be especially useful for tables like FeatureUsage
and Subscription
, where you might frequently query by user_id
, used_at
, started_at
, and ended_at
.
Here’s how to add indexes for the FeatureUsage
and Subscription
tables:
These indexes will optimize queries related to filtering or grouping by user_id
, used_at
, started_at
, and ended_at
, which are common in analytics.
To learn more about indexing in Neon Postgres, check out the Neon documentation on indexes.
2. Implement Caching for Expensive Queries
Caching is a great way to reduce the load on your database by storing the results of expensive queries and retrieving them from memory when needed. This avoids running the same query multiple times for data that doesn't change frequently.
Here's how you can cache the results of a query for daily active users for a specific time period:
Quick explanation of the code:
Cache::remember
: Caches the query result for 5 minutes (60 * 5
seconds). If the data is already cached, it retrieves the result from the cache; otherwise, it runs the query and stores the result.- This is useful for queries that don’t need real-time updates and can tolerate slight delays, such as historical data or reports.
The Cache::remember
method is a convenient way to cache query results in Laravel. You can adjust the cache duration based on your application's requirements. However, be cautious with caching, as it can lead to stale data if not managed properly.
Conclusion
In this guide, we've built a simple dynamic SaaS dashboard using Laravel Breeze for authentication, Livewire Charts for data visualization, and Neon Postgres for data storage. This setup provides a good starting point for tracking key metrics in your SaaS or web application.
To go further, consider the following next steps:
- Implementing more detailed drill-down features for each chart.
- Adding user-specific analytics for personalized insights.
- Implementing real-time updates using Livewire's polling feature or websockets.
Additional Resources
Need help?
Join our Discord Server to ask questions or see what others are doing with Neon. Users on paid plans can open a support ticket from the console. For more details, see Getting Support.