Unleashing Efficiency: Building an Advanced Library Management System with Phoenix

Michael Munavu
6 min readMay 28, 2023

Welcome, dear friends! Today, we embark on an exciting journey as we delve into the realm of software development and create a remarkable library management system using Phoenix. In this incredible adventure, we will not only organize and manage a collection of books but also explore the fascinating world of reviews associated with each book. So fasten your seatbelts and get ready to witness the birth of a sophisticated system that will revolutionize the way libraries operate. Together, we shall harness the power of Phoenix to craft a remarkable solution that will streamline the process of book management and enhance the reading experience for all. Let’s embark on this thrilling endeavor and unlock the potential of our library management system with Phoenix!

To get started.

mix phx.new library_management

Configure your database to allow you to create a database.

This will be done in the config/dev.exs .

Run

mix ecto.migrate

to create your database.

We now Generate models, views, and Controller for the books using the mix phx.gen.html command

mix phx.gen.html Books Book books title:string isbn:integer publication_date:date

This generates a book model with fields of a title, isbn and publication_date

Add the resource to your browser scope in lib/library_management_web/router.ex:

resources “/books”, BookController

Change the root route from

 get "/", PageController, :index

to

 get "/", BookController, :index

Run

mix ecto.migrate

You can now run the server and see what we have

mix phx.server

Now when we go to localhost:4000 , we see the following

You can now add a book!

When you click on New Book you will be redirected to a form where you can add a book, after adding the book, you will be redirected to the show page of the book as below

It would be more ideal if we were redirected back to the root page after adding a new book , to do this we will change the code in our book_controller .

Inside lib/library_management_web/controllers/book_controller.ex

Inside the create action/function, look for this line of code

 |> redirect(to: Routes.book_path(conn, :show, book))

And replace it with

 |> redirect(to: Routes.book_path(conn, :index))

Now if you add a new Book , you are redirected back to its index page .

You can also do the same for the update action/function.

We can now add a few Books to our application.

We have already added a model with the Books , for the application to the complete, we will be adding Reviews for each book .

Each review will have a name and content .

A book will have many reviews and a review will belong to a book.

To create this , we will use the phx.gen.context Command .

Run

mix phx.gen.context Reviews Review reviews name:string content:string book_id:references:books

This will create a reviews table with a name , content and book_id column .

  • We have to define the relationships discussed above into our code ie

(a book has many reviews and a review belongs to a book)

To do this , go to lib/library_management/books/book.ex and in the schema , after the last field , add

 has_many(:reviews, LibraryManagement.Books.Review)

And now in lib/library_management/reviews/review.ex and in the schema , remove this line

 field :book_id, :id

And replace it with

 belongs_to(:book, LibraryManagement.Books.Book)

Now , let us configure a route that will allow us to make reviews for a certain book , to do this , We will use nested routes .

Open lib/library_management_web/router.ex and replace

 resources "/book", BookController 

with

 resources "/book", BookController do
post "/review", BookController, :add_a_review
end

Open your terminal and type .

mix phx.routes

We now have a new route books/:id/reviews where we can post our comment .

A closer look at this line of code

post "/review", BookController, :add_a_review

We see that it is a post request to the route “/books/:id/review , it points to the BookController and to the create_review action inside the book_controller.ex

Let us navigate to lib/library_management_web/controllers/book_controller.ex .

Here we will add the following action/ function

def add_a_review(conn, %{"review" => review_params, "book_id" => book_id}) do
book =
book_id
|> Books.get_book!()
|> Repo.preload([:reviews])
case Books.add_review(book_id, review_params) do
{:ok, _review} ->
conn
|> put_flash(:info, "review added :)")
|> redirect(to: Routes.book_path(conn, :show, book))
{:error, _error} ->
conn
|> put_flash(:error, "review not added :(")
|> redirect(to: Routes.book_path(conn, :show, book))
end
end

at the case , we have a method called add_review that takes in a book_id and the params for a review .

To be able to access the methods in the reviews model , make sure to add these at the top of book_controller.ex

 alias LibraryManagement.Reviews.Review
alias LibraryManagement.Reviews
alias LibraryManagement.Repo

In Phoenix , we break everything in modules , if you look at the index action , you will see it calls a function list_books() that is imported from the Book model file in lib/library_management/books/books.ex .

We write smaller methods in the model file then use them in the controller file .

Let us define the add_review function that we have used in the controller .

We shall do this in the Book model file lib/library_management/books/books.ex

Add this to access the method that we will use to create a comment imported from lib/library_management/reviews.ex — the method we are importing is create_review

  • Add this on top of lib/library_management/books.ex , add this so as to access the create_review method inside the book module
alias LibraryManagement.Reviews

And the add_review method as follows

 def add_review(book_id, review_params) do
review_params
|> Map.put("book_id", book_id)
|> Reviews.create_review()
end

Next, we are going to implement a simple Add Review form and add it to the page that displays a single post. For that to happen, we need to modify the show action in the post controller. We are going to add the Review.changesetand pass it to the show.html template.

 def show(conn, %{"id" => id}) do
book =
id
|> Books.get_post!
|> Repo.preload([:reviews])
changeset = Review.changeset(%Review{}, %{})
render(conn, "show.html", book: book, changeset: changeset)
end

After that we are going to create a new review_form.html.heex template that will be used to enter and save comment data.

This will be inside the lib/library_management_web/templates/book

create the file and add

<%= form_for @changeset, @action, fn f -> %>
<div class="form-group">
<label>Name</label>
<%= text_input f, :name %>
</div>
<div class="form-group">
<label>Content</label>
<%= textarea f, :content %>
</div>

<div class="form-group">
<%= submit "Add Review" %>
</div>
<% end %>

Lastly, we need to add the review_form.html.heex template to the show.html.heex and make sure to pass it the correct data:


<%= render "review_form.html", book: @book, changeset: @changeset, action: Routes.book_book_path(@conn, :add_a_review, @book) %>

Before we can try this live , let us go back to our model schema for reviews and ensure that we allow book_id as one of the params .

We do this in lib/library_management/reviews/review.ex

Change the changeset function to

  def changeset(review, attrs) do
review
|> cast(attrs, [:name, :content, :book_id])
|> validate_required([:name, :content, :book_id])
end

Now we can add a new Review at the show page of each book

To view the comments on the show page of the book

Navigate to lib/lib_management_web/templates/book/show.html.heex

and at the bottom add

<%= for review <- @book.reviews do %>
<div class="review">
<p>Name of reviewer:<%= review.name %> </p>
<p>Content: <%= review.content %></p>
</div>

<% end %>

Now , we can see the reviews for each book

In conclusion, we’ve embarked on an exciting journey to create a library management system with Phoenix. By leveraging this powerful framework, we’ve revolutionized the way libraries operate, incorporating book reviews to enhance the reading experience and foster a sense of community. To explore the full implementation, documentation, and resources, visit our GitHub repository at [https://github.com/MICHAELMUNAVU83/library_management_phoenix]. Let’s continue pushing the boundaries of library management systems and inspire a love for books that knows no bounds. Happy coding, friends!

--

--