Added book search.
This commit is contained in:
@@ -6,6 +6,7 @@ use App\Entity\Book;
|
||||
use App\Entity\File;
|
||||
use App\Form\BookType;
|
||||
use App\Form\FileType;
|
||||
use App\Form\SearchType;
|
||||
use App\Repository\BookRepository;
|
||||
use App\Repository\FileRepository;
|
||||
use App\Service\FileService;
|
||||
@@ -79,6 +80,21 @@ class BookController extends AbstractController
|
||||
return new JsonResponse($bookFinder->search($phrase));
|
||||
}
|
||||
|
||||
#[Route('/search/', name: 'app_book_search_book', methods: ['GET', 'POST'])]
|
||||
public function searchBook(BookRepository $bookRepository, Request $request): Response
|
||||
{
|
||||
$searchForm = $this->createForm(SearchType::class);
|
||||
$searchForm->handleRequest($request);
|
||||
$books = [];
|
||||
if ($searchForm->isSubmitted() && $searchForm->isValid()) {
|
||||
$books = $bookRepository->findByTitle($searchForm->get('search')->getData());
|
||||
}
|
||||
return $this->renderForm('book/search.html.twig', [
|
||||
'books' => $books,
|
||||
'searchTerm' => $searchForm->get('search')->getData()
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/info/{urlInBase64}', name: 'app_book_info', methods: ['GET'])]
|
||||
public function info(string $urlInBase64): JsonResponse
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ class Book
|
||||
private $id;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
private $Title;
|
||||
private $title;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 255)]
|
||||
private $language;
|
||||
@@ -72,12 +72,12 @@ class Book
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->Title;
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $Title): self
|
||||
public function setTitle(string $title): self
|
||||
{
|
||||
$this->Title = $Title;
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
35
src/Form/SearchType.php
Normal file
35
src/Form/SearchType.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
class SearchType extends AbstractType
|
||||
{
|
||||
private $router;
|
||||
|
||||
public function __construct(UrlGeneratorInterface $router)
|
||||
{
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->setAction($this->router->generate('app_book_search_book'))
|
||||
->add('search', TextType::class, ['attr' => ['placeholder' => 'Book title']])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
// Configure your form options here
|
||||
'attr' => ['class' => 'd-flex']
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -39,20 +39,18 @@ class BookRepository extends ServiceEntityRepository
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return Book[] Returns an array of Book objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('b')
|
||||
// ->andWhere('b.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('b.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
/**
|
||||
* @return Book[] Returns an array of Book objects
|
||||
*/
|
||||
public function findByTitle($value): array
|
||||
{
|
||||
return $this->createQueryBuilder('b')
|
||||
->andWhere('LOWER(b.title) LIKE :title')
|
||||
->setParameter('title', '%' . strtolower($value) . '%')
|
||||
->orderBy('b.id', 'ASC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
// public function findOneBySomeField($value): ?Book
|
||||
// {
|
||||
|
||||
@@ -60,7 +60,7 @@ class FileService
|
||||
$file = new File();
|
||||
$file->setFileName(trim($ebook->getClientOriginalName()));
|
||||
$file->setFileSize($ebook->getSize());
|
||||
$file->setExtension($ebook->guessClientExtension());
|
||||
$file->setExtension($ebook->getClientOriginalExtension());
|
||||
$file->setBook($book);
|
||||
$this->fileRepository->add($file, true);
|
||||
|
||||
|
||||
@@ -3,16 +3,27 @@
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use App\Form\SearchType;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
class AppExtension extends AbstractExtension
|
||||
{
|
||||
|
||||
private $formFactory;
|
||||
|
||||
public function __construct(FormFactoryInterface $formFactory)
|
||||
{
|
||||
$this->formFactory = $formFactory;
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('file_exists', [$this, 'file_exists']),
|
||||
new TwigFunction('render_search_form', [$this, 'render_search_form']),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -34,4 +45,9 @@ class AppExtension extends AbstractExtension
|
||||
$suffix = array("B", "KB", "MB", "GB", "TB")[floor($base)];
|
||||
return round(pow(1024, $base - floor($base)), 2) . $suffix;
|
||||
}
|
||||
|
||||
public function render_search_form()
|
||||
{
|
||||
return $this->formFactory->create(SearchType::class)->createView();
|
||||
}
|
||||
}
|
||||
@@ -49,10 +49,11 @@
|
||||
<a href="{{ path('app_book_new') }}" class="btn btn-primary">Add new book</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
{% set search_form = render_search_form() %}
|
||||
{{ form_start(search_form) }}
|
||||
{{ form_widget(search_form.search, { 'attr' : { 'class': 'me-2' }}) }}
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
{{ form_end(search_form) }}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Cover</th>
|
||||
<th>Id</th>
|
||||
<th></th>
|
||||
<th>Title</th>
|
||||
<th>Language</th>
|
||||
<th>Description</th>
|
||||
@@ -29,19 +28,23 @@
|
||||
<tbody>
|
||||
{% for book in books %}
|
||||
<tr>
|
||||
<td>{% if file_exists(asset('book_covers/cover_' ~ book.id ~ '.jpg')) %}<img class="img-thumbnail"
|
||||
<td>
|
||||
{% if file_exists(asset('book_covers/cover_' ~ book.id ~ '.jpg')) %}
|
||||
<a href="{{ path('app_book_show', {'id': book.id}) }}"><img class="img-thumbnail"
|
||||
style="max-width: 80px"
|
||||
src="{{ asset('book_covers/cover_' ~ book.id ~ '.jpg' ) }}" /> {% endif %}
|
||||
src="{{ asset('book_covers/cover_' ~ book.id ~ '.jpg' ) }}"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ book.id }}</td>
|
||||
<td>{{ book.Title }}</td>
|
||||
<td><a href="{{ path('app_book_show', {'id': book.id}) }}"
|
||||
class="text-decoration-none">{{ book.title }}</a></td>
|
||||
<td>{{ book.language }}</td>
|
||||
<td>{{ book.description | slice(0, 200) }}</td>
|
||||
<td>{{ book.publisher }}</td>
|
||||
<td>{{ book.publishDate ? book.publishDate|date('Y-m-d') : '' }}</td>
|
||||
<td>
|
||||
<a href="{{ path('app_book_show', {'id': book.id}) }}">show</a>
|
||||
<a href="{{ path('app_book_edit', {'id': book.id}) }}">edit</a>
|
||||
<a href="{{ path('app_book_show', {'id': book.id}) }}" class="text-decoration-none">show</a>
|
||||
<a href="{{ path('app_book_edit', {'id': book.id}) }}" class="text-decoration-none">edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
|
||||
54
templates/book/search.html.twig
Normal file
54
templates/book/search.html.twig
Normal file
@@ -0,0 +1,54 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Book search{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="d-flex flex-row justify-content-between align-items-center">
|
||||
<div>
|
||||
<h1>Book search: {{ searchTerm }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Title</th>
|
||||
<th>Language</th>
|
||||
<th>Description</th>
|
||||
<th>Publisher</th>
|
||||
<th>Publish_date</th>
|
||||
<th>actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for book in books %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if file_exists(asset('book_covers/cover_' ~ book.id ~ '.jpg')) %}
|
||||
<a href="{{ path('app_book_show', {'id': book.id}) }}"><img class="img-thumbnail"
|
||||
style="max-width: 80px"
|
||||
src="{{ asset('book_covers/cover_' ~ book.id ~ '.jpg' ) }}"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><a href="{{ path('app_book_show', {'id': book.id}) }}"
|
||||
class="text-decoration-none">{{ book.title }}</a></td>
|
||||
<td>{{ book.language }}</td>
|
||||
<td>{{ book.description | slice(0, 200) }}</td>
|
||||
<td>{{ book.publisher }}</td>
|
||||
<td>{{ book.publishDate ? book.publishDate|date('Y-m-d') : '' }}</td>
|
||||
<td>
|
||||
<a href="{{ path('app_book_show', {'id': book.id}) }}" class="text-decoration-none">show</a>
|
||||
<a href="{{ path('app_book_edit', {'id': book.id}) }}" class="text-decoration-none">edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="9">no records found</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -3,33 +3,56 @@
|
||||
{% block title %}Book{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>Book</h1>
|
||||
<div class="d-flex">
|
||||
|
||||
<div>
|
||||
|
||||
{% if file_exists(asset('book_covers/cover_' ~ book.id ~ '.jpg')) %}
|
||||
<img class="img-thumbnail"
|
||||
style="max-width: 80px"
|
||||
src="{{ asset('book_covers/cover_' ~ book.id ~ '.jpg' ) }}"/> {% endif %}
|
||||
</div>
|
||||
<div class="mx-3">
|
||||
<h1>{{ book.title }}</h1>
|
||||
<h3>{{ book.subtitle }}</h3>
|
||||
<p>{{ book.author }}</p>
|
||||
<p>{% for i in range(1, book.rating) %}⭐{% endfor %}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<td>{{ book.id }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<td>{{ book.Title }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Language</th>
|
||||
<td>{{ book.language }}</td>
|
||||
<th>Category</th>
|
||||
<td colspan="3">{{ book.category }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<td>{{ book.description | nl2br }}</td>
|
||||
<td colspan="3">{{ book.description | nl2br }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Publisher</th>
|
||||
<td>{{ book.publisher }}</td>
|
||||
<th>Publish date</th>
|
||||
<td>{{ book.publishDate ? book.publishDate|date('Y-m-d') : '' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Publish_date</th>
|
||||
<td>{{ book.publishDate ? book.publishDate|date('Y-m-d') : '' }}</td>
|
||||
<th>Isbn</th>
|
||||
<td>{{ book.isbn }}</td>
|
||||
<th>Pages</th>
|
||||
<td>{{ book.pages }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Tags</th>
|
||||
<td colspan="3">{{ book.tags }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Series</th>
|
||||
<td colspan="3">{{ book.series }} {{ book.volume }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Language</th>
|
||||
<td colspan="3">{{ book.language }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user