Added book search.

This commit is contained in:
krzysiej
2022-06-03 15:17:46 +02:00
parent b21d213b19
commit 314f4f2a14
10 changed files with 192 additions and 46 deletions

View File

@@ -6,6 +6,7 @@ use App\Entity\Book;
use App\Entity\File; use App\Entity\File;
use App\Form\BookType; use App\Form\BookType;
use App\Form\FileType; use App\Form\FileType;
use App\Form\SearchType;
use App\Repository\BookRepository; use App\Repository\BookRepository;
use App\Repository\FileRepository; use App\Repository\FileRepository;
use App\Service\FileService; use App\Service\FileService;
@@ -79,6 +80,21 @@ class BookController extends AbstractController
return new JsonResponse($bookFinder->search($phrase)); 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'])] #[Route('/info/{urlInBase64}', name: 'app_book_info', methods: ['GET'])]
public function info(string $urlInBase64): JsonResponse public function info(string $urlInBase64): JsonResponse
{ {

View File

@@ -16,7 +16,7 @@ class Book
private $id; private $id;
#[ORM\Column(type: 'string', length: 255)] #[ORM\Column(type: 'string', length: 255)]
private $Title; private $title;
#[ORM\Column(type: 'string', length: 255)] #[ORM\Column(type: 'string', length: 255)]
private $language; private $language;
@@ -72,12 +72,12 @@ class Book
public function getTitle(): ?string 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; return $this;
} }

35
src/Form/SearchType.php Normal file
View 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']
]);
}
}

View File

@@ -39,20 +39,18 @@ class BookRepository extends ServiceEntityRepository
} }
} }
// /** /**
// * @return Book[] Returns an array of Book objects * @return Book[] Returns an array of Book objects
// */ */
// public function findByExampleField($value): array public function findByTitle($value): array
// { {
// return $this->createQueryBuilder('b') return $this->createQueryBuilder('b')
// ->andWhere('b.exampleField = :val') ->andWhere('LOWER(b.title) LIKE :title')
// ->setParameter('val', $value) ->setParameter('title', '%' . strtolower($value) . '%')
// ->orderBy('b.id', 'ASC') ->orderBy('b.id', 'ASC')
// ->setMaxResults(10) ->getQuery()
// ->getQuery() ->getResult();
// ->getResult() }
// ;
// }
// public function findOneBySomeField($value): ?Book // public function findOneBySomeField($value): ?Book
// { // {

View File

@@ -60,7 +60,7 @@ class FileService
$file = new File(); $file = new File();
$file->setFileName(trim($ebook->getClientOriginalName())); $file->setFileName(trim($ebook->getClientOriginalName()));
$file->setFileSize($ebook->getSize()); $file->setFileSize($ebook->getSize());
$file->setExtension($ebook->guessClientExtension()); $file->setExtension($ebook->getClientOriginalExtension());
$file->setBook($book); $file->setBook($book);
$this->fileRepository->add($file, true); $this->fileRepository->add($file, true);

View File

@@ -3,16 +3,27 @@
namespace App\Twig; namespace App\Twig;
use App\Form\SearchType;
use Symfony\Component\Form\FormFactoryInterface;
use Twig\Extension\AbstractExtension; use Twig\Extension\AbstractExtension;
use Twig\TwigFilter; use Twig\TwigFilter;
use Twig\TwigFunction; use Twig\TwigFunction;
class AppExtension extends AbstractExtension class AppExtension extends AbstractExtension
{ {
private $formFactory;
public function __construct(FormFactoryInterface $formFactory)
{
$this->formFactory = $formFactory;
}
public function getFunctions() public function getFunctions()
{ {
return [ return [
new TwigFunction('file_exists', [$this, 'file_exists']), 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)]; $suffix = array("B", "KB", "MB", "GB", "TB")[floor($base)];
return round(pow(1024, $base - floor($base)), 2) . $suffix; return round(pow(1024, $base - floor($base)), 2) . $suffix;
} }
public function render_search_form()
{
return $this->formFactory->create(SearchType::class)->createView();
}
} }

View File

@@ -49,10 +49,11 @@
<a href="{{ path('app_book_new') }}" class="btn btn-primary">Add new book</a> <a href="{{ path('app_book_new') }}" class="btn btn-primary">Add new book</a>
</li> </li>
</ul> </ul>
<form class="d-flex"> {% set search_form = render_search_form() %}
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search"> {{ form_start(search_form) }}
{{ form_widget(search_form.search, { 'attr' : { 'class': 'me-2' }}) }}
<button class="btn btn-outline-success" type="submit">Search</button> <button class="btn btn-outline-success" type="submit">Search</button>
</form> {{ form_end(search_form) }}
</div> </div>
</div> </div>
</nav> </nav>

View File

@@ -16,8 +16,7 @@
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th>Cover</th> <th></th>
<th>Id</th>
<th>Title</th> <th>Title</th>
<th>Language</th> <th>Language</th>
<th>Description</th> <th>Description</th>
@@ -29,19 +28,23 @@
<tbody> <tbody>
{% for book in books %} {% for book in books %}
<tr> <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" 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>
<td>{{ book.id }}</td> <td><a href="{{ path('app_book_show', {'id': book.id}) }}"
<td>{{ book.Title }}</td> class="text-decoration-none">{{ book.title }}</a></td>
<td>{{ book.language }}</td> <td>{{ book.language }}</td>
<td>{{ book.description | slice(0, 200) }}</td> <td>{{ book.description | slice(0, 200) }}</td>
<td>{{ book.publisher }}</td> <td>{{ book.publisher }}</td>
<td>{{ book.publishDate ? book.publishDate|date('Y-m-d') : '' }}</td> <td>{{ book.publishDate ? book.publishDate|date('Y-m-d') : '' }}</td>
<td> <td>
<a href="{{ path('app_book_show', {'id': book.id}) }}">show</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}) }}">edit</a> <a href="{{ path('app_book_edit', {'id': book.id}) }}" class="text-decoration-none">edit</a>
</td> </td>
</tr> </tr>
{% else %} {% else %}

View 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 %}

View File

@@ -3,33 +3,56 @@
{% block title %}Book{% endblock %} {% block title %}Book{% endblock %}
{% block body %} {% 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"> <table class="table">
<tbody> <tbody>
<tr> <tr>
<th>Id</th> <th>Category</th>
<td>{{ book.id }}</td> <td colspan="3">{{ book.category }}</td>
</tr>
<tr>
<th>Title</th>
<td>{{ book.Title }}</td>
</tr>
<tr>
<th>Language</th>
<td>{{ book.language }}</td>
</tr> </tr>
<tr> <tr>
<th>Description</th> <th>Description</th>
<td>{{ book.description | nl2br }}</td> <td colspan="3">{{ book.description | nl2br }}</td>
</tr> </tr>
<tr> <tr>
<th>Publisher</th> <th>Publisher</th>
<td>{{ book.publisher }}</td> <td>{{ book.publisher }}</td>
<th>Publish date</th>
<td>{{ book.publishDate ? book.publishDate|date('Y-m-d') : '' }}</td>
</tr> </tr>
<tr> <tr>
<th>Publish_date</th> <th>Isbn</th>
<td>{{ book.publishDate ? book.publishDate|date('Y-m-d') : '' }}</td> <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> </tr>
</tbody> </tbody>
</table> </table>