Now, we are going to learn Models and Views by building a BookStore application.
The application can help to:
- Create a new book
- Get all books
- Update the book by id
- Delete the book by id
Below is the table design of the Books table
Small recap about MVC model
First of all, we need to initialize the Project
symfony new --webapp webg301-lecture-04-model-viewThen, go the the project folder and start the Visual Studio Code
cd webg301-lecture-04-model-view
code .Now, we are going to create the Book Model (or Entity) by using the command line and follow the steps below
php bin/console make:entityVoila, you can see the Book.php entity is created with the BookRepository.php
After we created the Model (Entity), we will connect to the database.
First of all, make sure you enable XAMPP
Then, you can navigate to the URL http://localhost/phpmyadmin/ to see the phpMyAdmin to manage the database
Next, we need to config the DATABASE_URL in the .env file
# .env
DATABASE_URL=mysql://root:@127.0.0.1:3306/book_store?serverVersion=mariadb-10.4.11Then, we can use command line to create the database
php bin/console doctrine:database:createIn the phpMyAdmin, you can see the database book_store is created.
Now, the database is created. We need to create the table from the Model.
To do so, we need to create a migration first.
php bin/console make:migrationYou can see the folder migrations is created with the migration file inside it.
Now, use the command below to start create the table
php bin/console doctrine:migrations:migrateNow, the table book is created in the phpMyAdmin.
Now, it's time to create the controller. Here the command to create it.
php bin/console make:controller BooksControllerThen, open the BooksController.php to modify the source. The source code will help to get all books in the database
// /src/Controller/BooksController.php
class BooksController extends AbstractController
{
/**
* @Route("/books", name="book_list")
*/
public function index(BookRepository $bookRepository): Response
{
$books = $bookRepository->findAll();
return $this->render('books/index.html.twig', [
'books' => $books,
]);
}
}Now, modify the view to display all the books from database.
To do that, modify the file /src/templates/books/index.html.twig
{% extends 'base.html.twig' %}
{% block body %}
<h1>Book List</h1>
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
</tr>
{% for book in books %}
<tr>
<td>{{ book.id }}</td>
<td>{{ book.name }}</td>
<td>{{ book.price }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}Now, start the project to see the result
symfony serveYou can navigate to the URL http://127.0.0.1:8000/books to see all the books.
At first, it is empty because there is nothing in the table. You need to insert some data into the table.
Use the SQL script below to insert data into the table in phpMyAdmin
USE book_store;
-- Insert dummy data into the book table
INSERT INTO book (name, price) VALUES
('Book 1', 20),
('Book 2', 30),
('Book 3', 25),
('Book 4', 15),
('Book 5', 40);Now, refresh the website to see all books
Now, we will create a new book.
To do that, we need to create a form. In Symfony, we will use form builder.
Open your terminal and run the following command to generate a form type:
php bin/console make:form BookTypeFollow the prompts and generate the form type. This will create a file named BookType.php in the src/Form directory.
In the BookController.php, add a new action named addBook():
use App\Form\BookType;
use Symfony\Component\HttpFoundation\Request;
// ...
/**
* @Route("/books/add", name="add_book")
*/
public function addBook(Request $request): Response
{
$newBook = new Book();
$form = $this->createForm(BookType::class, $newBook);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($newBook);
$entityManager->flush();
return $this->redirectToRoute('book_list');
}
return $this->render('book/add.html.twig', [
'form' => $form->createView(),
]);
}The image below explains the code above
Create a new Twig template named add.html.twig inside the templates/books directory:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Add a New Book</h1>
{{ form_start(form) }}
{{ form_row(form.name) }}
{{ form_row(form.price) }}
<button type="submit">Add Book</button>
{{ form_end(form) }}
{% endblock %}Now, navigate to http://127.0.0.1:8000/books/add to see the form.
Let's create an action to display the details of a specific book, update the book list view to include a link to view details, and then create the view to display the book details.
In the BookController.php, add a new action named viewBook()
/**
* @Route("/books/{id}", name="view_book")
*/
public function viewBook($id, BookRepository $bookRepository): Response
{
$book = $bookRepository->find($id);
if (!$book) {
throw $this->createNotFoundException('Book not found');
}
return $this->render('books/view.html.twig', [
'book' => $book,
]);
}The flowchart below shows how to show the details of a specific book with Id
Open the templates/books/index.html.twig file and update it to include a link for each book to view its details:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Book List</h1>
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
<th>Actions</th>
</tr>
{% for book in books %}
<tr>
<td>{{ book.id }}</td>
<td>{{ book.name }}</td>
<td>{{ book.price }}</td>
<td><a href="{{ path('view_book', {'id': book.id}) }}">View Details</a></td>
</tr>
{% endfor %}
</table>
{% endblock %}Now, we can see the link View Details
Create a new Twig template named view.html.twig inside the templates/books directory:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Book Details</h1>
<p><strong>Name:</strong> {{ book.name }}</p>
<p><strong>Price:</strong> {{ book.price }}</p>
<a href="{{ path('book_list') }}">Back to Book List</a>
{% endblock %}Now, click the View Details, we can see the details page
In the BookController.php, add a new action named deleteBook():
use App\Repository\BookRepository;
// ...
/**
* @Route("/books/{id}/delete", name="delete_book")
*/
public function deleteBook($id, BookRepository $bookRepository): Response
{
$book = $bookRepository->find($id);
if (!$book) {
throw $this->createNotFoundException('Book not found');
}
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($book);
$entityManager->flush();
return $this->redirectToRoute('book_list');
}The image below explains how to delete a book with the Id
Open the templates/books/index.html.twig file and update it to include a delete button for each book:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Book List</h1>
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
<th>Actions</th>
</tr>
{% for book in books %}
<tr>
<td>{{ book.id }}</td>
<td>{{ book.name }}</td>
<td>{{ book.price }}</td>
<td>
<a href="{{ path('view_book', {'id': book.id}) }}">View Details</a>
<a href="{{ path('delete_book', {'id': book.id}) }}" onclick="return confirm('Are you sure you want to delete this book?')">Delete</a>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}Now, we can use the Delete button to delete a book
Let's create an action to update a book and update the book list view to include an Edit button for each book.
In the BookController.php, add a new action named editBook():
use Symfony\Component\Routing\Annotation\Route;
// ...
/**
* @Route("/books/{id}/edit", name="edit_book")
*/
public function editBook($id, BookRepository $bookRepository, Request $request): Response
{
$book = $bookRepository->find($id);
if (!$book) {
throw $this->createNotFoundException('Book not found');
}
$form = $this->createForm(BookType::class, $book);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->flush();
return $this->redirectToRoute('book_list');
}
return $this->render('books/edit.html.twig', [
'book' => $book,
'form' => $form->createView(),
]);
}The image below explains the business of updating a book
Open the templates/books/index.html.twig file and update it to include an Edit button for each book:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Book List</h1>
<table>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
<th>Actions</th>
</tr>
{% for book in books %}
<tr>
<td>{{ book.id }}</td>
<td>{{ book.name }}</td>
<td>{{ book.price }}</td>
<td>
<a href="{{ path('view_book', {'id': book.id}) }}">View Details</a>
<a href="{{ path('edit_book', {'id': book.id}) }}">Edit</a>
<a href="{{ path('delete_book', {'id': book.id}) }}" onclick="return confirm('Are you sure you want to delete this book?')">Delete</a>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}
Now, you can see the Edit button
Create a new Twig template named edit.html.twig inside the templates/books directory:
{% extends 'base.html.twig' %}
{% block body %}
<h1>Edit Book</h1>
{{ form_start(form) }}
{{ form_row(form.name) }}
{{ form_row(form.price) }}
<button type="submit">Update Book</button>
{{ form_end(form) }}
<a href="{{ path('book_list') }}">Back to Book List</a>
{% endblock %}Now, we can have the form to update the book