Building a Blog with Laravel, Livewire, and Laravel Breeze
Learn how to create a dynamic blog application using Laravel, Livewire, and Laravel Breeze for authentication and Neon.
Laravel is a powerful PHP framework that makes it easy to build web applications. When combined with Livewire, a full-stack framework for Laravel, you can create dynamic, reactive interfaces with minimal JavaScript. In this guide, we'll build a blog application using Laravel and Livewire, and we'll use Laravel Breeze to handle authentication, along with Neon Postgres.
By the end of this tutorial, you'll have a fully functional blog where users can create, read, update, and delete posts. We'll also implement comments and a simple tagging system.
Prerequisites
Before we start, make sure you have the following:
- PHP 8.1 or higher installed on your system
- Composer for managing PHP dependencies
- Node.js and npm for managing front-end assets
- A Neon account for database hosting
- Basic knowledge of Laravel, Livewire, and Tailwind CSS
Setting up the Project
Let's start by creating a new Laravel project and setting up the necessary components. We'll use Laravel Breeze for authentication, Livewire for building interactive components, and Tailwind CSS for styling.
Creating a New Laravel Project
Open your terminal and run the following command to create a new Laravel project:
This command creates a new Laravel project in a directory named laravel-livewire-blog
and installs all the necessary dependencies.
Installing Laravel Breeze
Laravel Breeze provides a minimal and simple starting point for building a Laravel application with authentication.
An alternative to Laravel Breeze is Laravel Jetstream, which provides more features out of the box, such as team management and two-factor authentication. However, for this tutorial, we'll use Laravel Breeze for its simplicity.
Let's install Laravel Breeze with the Blade views:
This command installs Breeze and sets up the necessary views and routes for authentication.
While in the terminal, also install the Livewire package:
Setting up the Database
Update your .env
file with your Neon database credentials:
Make sure to replace your-neon-hostname
, your_database_name
, your_username
, and your_password
with your actual database details and save the file.
Compiling Assets
Laravel Breeze uses Tailwind CSS for styling, so we need to compile the assets to generate the CSS file.
To compile the assets, run:
Keep the Vite development server running in the background as you continue with the next steps. This will automatically compile the assets when changes are made so you can see the updates in real-time.
Creating the Blog Structure
Now that we have our basic setup, we are ready to create the structure for our blog, including models, migrations, and Livewire components, routes, policies, and views.
Creating the Post Model and Migration
Models in Laravel are used to interact with the database using the Eloquent ORM. We'll create models for posts, comments, and tags, along with their respective migrations.
Run the following command to create a Post
model with its migration:
Open the migration file in database/migrations
and update it:
This migration creates a posts
table with columns for the post title, content, publication status, and publication date. It also includes a foreign key to the users
table for the post author. The slug
column will be used to generate SEO-friendly URLs.
Creating the Comment Model and Migration
Now, let's create a Comment
model and its migration:
The comments
table will store the comments for each post, along with the user who made the comment, the post ID, and the comment content.
With that in mind, let's update the migration file:
Creating the Tag Model and Migration
To take this a step further, we can add a tagging system to our blog. This will allow us to categorize posts based on different topics.
The tags
table will store the tags that can be associated with posts. Update the migration file as follows:
We'll also create a pivot table to manage the many-to-many relationship between posts and tags. The convention for naming this table is to combine the singular form of the related models in alphabetical order. In this case, the models are Post
and Tag
, so the pivot table will be named post_tag
.
Update the migration file as follows:
We don't need to create a model for the pivot table, as it will be managed by Laravel's Eloquent ORM.
Now, run the migrations to create all the tables in the Neon database:
This command will create the posts
, comments
, tags
, and post_tag
tables in your database and keep track of the migrations that have been run. If you need to rollback the migrations, you can run php artisan migrate:rollback
or if you were to add a new migration, you can run php artisan migrate
and it will only run the new migrations.
Updating the Models
Let's update our models to define the relationships. What we want to achieve is:
- A post belongs to a user
- A post has many comments
- A post can have many tags
- A comment belongs to a user
- A comment belongs to a post
- A tag can be associated with many posts
We already have that structure in our database, but we need to define these relationships in our models so we can access them easily in our application.
In app/Models/Post.php
we define the relationships to the User
, Comment
, and Tag
models:
In app/Models/Comment.php
we define the relationships to the User
and Post
models so we can get the user and post associated with a comment:
In app/Models/Tag.php
we define the relationship to the Post
model, this will allow us to get all posts associated with a tag:
Finally, update app/Models/User.php
to include the relationship with posts and comments, where a user can have many posts and many comments:
With these relationships defined, we can now easily access the related models and data using Eloquent.
Seeding the Database
To populate the database with some sample data, let's create seeders for Tag
models so we can associate tags with posts.
Create a seeder for the Tag
model:
Update the seeder file in database/seeders/TagSeeder.php
:
Now, update the main DatabaseSeeder
in database/seeders/DatabaseSeeder.php
to include these new seeder:
Finally, to seed your database with this sample data, run:
This command will run the TagSeeder
and populate the tags
table with the sample tags which we can associate with posts later on when users create new posts.
Implementing the Blog Functionality
Now that we have our models and migrations set up, we can go ahead and implement the blog functionality using Livewire components.
We will start by creating two Livewire components:
PostList
to display a list of blog postsPostForm
to create and edit posts
Creating the Post List Component
First, let's create a Livewire component to display the list of blog posts:
This command creates a new Livewire component in the app/Livewire
directory, along with a view file in resources/views/livewire
.
Update app/Livewire/PostList.php
to fetch the posts and handle search functionality:
In the render
method, we fetch the posts that are published and match the search query.
An important thing to note here is that we also eager load the user
and tags
relationships to avoid additional queries when accessing these relationships in the view.
To learn more about how to implement search functionality in Livewire, check out the Building a Simple Real-Time Search with Laravel, Livewire, and Neon guide.
Now, update the view in resources/views/livewire/post-list.blade.php
to display the list of posts:
This view displays the list of posts along with the post title, author, publication date, content, and tags. It also includes a search input field to filter the posts based on the search query.
Creating the Post Form Component
Now, let's create a Livewire component for creating and editing posts.
Update app/Livewire/PostForm.php
to handle post creation and editing:
Rundown of the methods in the PostForm
component:
- The
mount
method is used to set the initial values for the form fields when editing a post. The post data is passed to the component as a parameter. - The
save
method is called when the form is submitted. It validates the form fields, creates a new post or updates an existing one, and redirects to the post detail page. - The
render
method fetches all tags from the database and passes them to the view. - In the
rules
property, we define the validation rules for the form fields.
After that, update the resources/views/livewire/post-form.blade.php
view to display the post form:
This view includes form fields for the post title, content, and tags. The tags are displayed as checkboxes, allowing the user to select multiple tags for the post when creating or editing it.
Creating Routes and Controllers
Now that we have our Livewire components ready, let's create the necessary routes and controllers to handle the blog functionality.
Routes are defined in the routes/web.php
file, and controllers are used to handle the logic for each route.
Next, create a controller which will handle the blog functionality for the above routes that we just defined:
The above command creates a new controller in the app/Http/Controllers
directory.
Update app/Http/Controllers/PostController.php
to include the necessary methods:
For all the methods, we return the corresponding views. The edit
method also includes an authorization gate to check if the current user is authorized to edit the post which we will define later.
Creating the Views
With the routes and controllers in place, let's create the views for the blog functionality. The views will include the layout, navigation, and content for the blog posts.
Let's start by creating a resources/views/posts/index.blade.php
view to display the list of blog posts:
This view includes the PostList
Livewire component to display the list of blog posts.
Next, create the resources/views/posts/show.blade.php
view to display a single blog post:
This view displays the post title, author, publication date, content, and tags. It also includes a link to edit the post if the current user is authorized to do so.
After that, create the resources/views/posts/create.blade.php
and resources/views/posts/edit.blade.php
views for creating and editing posts, respectively. These views will include the PostForm
Livewire component, which we created earlier, and handle the form submission.
Create the resources/views/posts/create.blade.php
view with the following content:
Using the @livewire
directive, we include the PostForm
component to create a new post.
With the same structure, create the resources/views/posts/edit.blade.php
view:
This view includes the PostForm
component with the post data passed as a parameter to edit the post. The form fields will be pre-filled with the existing post data when users edit one of their posts.
Adding Authorization
As this will be a multi-user blog, we need to implement authorization to ensure that users can only edit their own posts. Our goal is to allow users to edit posts only if they are the authors of those posts.
Laravel provides a simple way to define authorization policies using policies and gates. Policies are classes that define the authorization logic for a particular model, while gates are more general-purpose authorization checks.
Let's create a policy for the Post
model:
Update app/Policies/PostPolicy.php
to define the authorization logic for updating and deleting posts:
In the PostPolicy
class, we define the update
and delete
methods to check if the current user is the author of the post. If the user is the author, the method returns true
, allowing the user to update or delete the post. Otherwise, it returns false
to deny access and prevent unauthorized actions.
Implementing Comments
By now we have the basic functionality of our blog in place. If you were to visit the blog, you would see a list of posts, be able to view individual posts, and create new posts. However, a blog wouldn't be complete without the ability to add comments to posts!
Let's add the comment system to our blog posts! First, create a new Livewire component:
Update app/Livewire/CommentSection.php
to handle adding comments to a post:
Here, we have the CommentSection
Livewire component with methods to add and delete comments. The addComment
method creates a new comment for the post, while the deleteComment
method deletes a comment if the current user is the author of the comment. You can also see the rules
property defining the validation rules for the comment content and create a policy for the Comment
model to handle authorization instead of checking it in the component itself.
Next, update the view in resources/views/livewire/comment-section.blade.php
to display comments and allow users to add new comments:
After that, go back to the resources/views/posts/show.blade.php
view and update it to include the comment section:
Adding Navigation Links
Laravel Breeze provides a simple layout with a navigation menu that includes links for logging in and registering. Let's add links for creating new posts and logging out.
Update the existing resources/views/layouts/navigation.blade.php
view to include links for creating new posts:
Testing
To ensure our blog functionality works as expected, it's important to test the application.
To learn more about testing in Laravel along Neon, check out the Testing Laravel Applications with Neon's Database Branching guide.
Conclusion
In this tutorial, we've built a fully functional blog application using Laravel, Livewire, and Laravel Breeze. We've implemented features such as user authentication, creating and editing blog posts, adding comments, and basic authorization.
This implementation provides a solid foundation for a blog, but there are always ways to improve and expand its functionality:
- Implement a more advanced authorization system with roles and permissions
- Add a rich text editor for post content
- Implement a more robust tagging system with the ability to create new tags
- Add a search functionality for posts
- Implement social sharing features
- Add an admin panel for managing posts, users, and comments
By combining the power of Laravel, the simplicity of Livewire, and the authentication scaffolding provided by Laravel Breeze, you can quickly create dynamic and interactive web applications that meet your users' needs.