Removed encore added books crud

This commit is contained in:
krzysiej
2022-05-20 14:53:13 +02:00
parent ac3f054dec
commit 210318eca4
31 changed files with 611 additions and 299 deletions

2
.gitignore vendored
View File

@@ -69,3 +69,5 @@ npm-debug.log
yarn-error.log yarn-error.log
###< symfony/webpack-encore-bundle ### ###< symfony/webpack-encore-bundle ###
/public/book_covers/
/public/book_files/

2
.idea/biblio.iml generated
View File

@@ -62,7 +62,6 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/var-dumper" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/var-dumper" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/webpack-encore-bundle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/options-resolver" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/options-resolver" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/flex" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/flex" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/password-hasher" /> <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/password-hasher" />
@@ -105,6 +104,7 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/annotations" /> <excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/annotations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/inflector" /> <excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/inflector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/migrations" /> <excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/migrations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/apache-pack" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

1
.idea/php.xml generated
View File

@@ -59,7 +59,6 @@
<path value="$PROJECT_DIR$/vendor/symfony/http-foundation" /> <path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
<path value="$PROJECT_DIR$/vendor/symfony/var-dumper" /> <path value="$PROJECT_DIR$/vendor/symfony/var-dumper" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" /> <path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/symfony/webpack-encore-bundle" />
<path value="$PROJECT_DIR$/vendor/symfony/options-resolver" /> <path value="$PROJECT_DIR$/vendor/symfony/options-resolver" />
<path value="$PROJECT_DIR$/vendor/symfony/flex" /> <path value="$PROJECT_DIR$/vendor/symfony/flex" />
<path value="$PROJECT_DIR$/vendor/symfony/password-hasher" /> <path value="$PROJECT_DIR$/vendor/symfony/password-hasher" />

View File

@@ -1,12 +0,0 @@
/*
* Welcome to your app's main JavaScript file!
*
* We recommend including the built version of this JavaScript file
* (and its CSS file) in your base layout (base.html.twig).
*/
// any CSS you import will output into a single css file (app.css in this case)
import './styles/app.css';
// start the Stimulus application
import './bootstrap';

11
assets/bootstrap.js vendored
View File

@@ -1,11 +0,0 @@
import { startStimulusApp } from '@symfony/stimulus-bridge';
// Registers Stimulus controllers from controllers.json and in the controllers/ directory
export const app = startStimulusApp(require.context(
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
true,
/\.[jt]sx?$/
));
// register any custom, 3rd party controllers here
// app.register('some_controller_name', SomeImportedController);

View File

@@ -1,4 +0,0 @@
{
"controllers": [],
"entrypoints": []
}

View File

@@ -1,16 +0,0 @@
import { Controller } from '@hotwired/stimulus';
/*
* This is an example Stimulus controller!
*
* Any element with a data-controller="hello" attribute will cause
* this controller to be executed. The name "hello" comes from the filename:
* hello_controller.js -> "hello"
*
* Delete this file or adapt it for your use!
*/
export default class extends Controller {
connect() {
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
}
}

View File

@@ -1,3 +0,0 @@
body {
background-color: lightgray;
}

View File

@@ -42,7 +42,6 @@
"symfony/validator": "6.0.*", "symfony/validator": "6.0.*",
"symfony/web-link": "6.0.*", "symfony/web-link": "6.0.*",
"symfony/webapp-meta": "^1.0", "symfony/webapp-meta": "^1.0",
"symfony/webpack-encore-bundle": "^1.12",
"symfony/yaml": "6.0.*", "symfony/yaml": "6.0.*",
"twig/extra-bundle": "^2.12|^3.0", "twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0" "twig/twig": "^2.12|^3.0"

73
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "9e2f5ddba7a9c27abf3decbe843a4160", "content-hash": "bfe2f245934ec051aa999bacee2521d8",
"packages": [ "packages": [
{ {
"name": "doctrine/annotations", "name": "doctrine/annotations",
@@ -7281,77 +7281,6 @@
], ],
"time": "2021-10-20T16:02:05+00:00" "time": "2021-10-20T16:02:05+00:00"
}, },
{
"name": "symfony/webpack-encore-bundle",
"version": "v1.14.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/webpack-encore-bundle.git",
"reference": "6b18be99bf2a5a402b80a2c00043fe0b365fe836"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/webpack-encore-bundle/zipball/6b18be99bf2a5a402b80a2c00043fe0b365fe836",
"reference": "6b18be99bf2a5a402b80a2c00043fe0b365fe836",
"shasum": ""
},
"require": {
"php": ">=7.1.3",
"symfony/asset": "^4.4 || ^5.0 || ^6.0",
"symfony/config": "^4.4 || ^5.0 || ^6.0",
"symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0",
"symfony/http-kernel": "^4.4 || ^5.0 || ^6.0",
"symfony/service-contracts": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0",
"symfony/phpunit-bridge": "^5.3 || ^6.0",
"symfony/twig-bundle": "^4.4 || ^5.0 || ^6.0",
"symfony/web-link": "^4.4 || ^5.0 || ^6.0"
},
"type": "symfony-bundle",
"extra": {
"thanks": {
"name": "symfony/webpack-encore",
"url": "https://github.com/symfony/webpack-encore"
}
},
"autoload": {
"psr-4": {
"Symfony\\WebpackEncoreBundle\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Integration with your Symfony app & Webpack Encore!",
"support": {
"issues": "https://github.com/symfony/webpack-encore-bundle/issues",
"source": "https://github.com/symfony/webpack-encore-bundle/tree/v1.14.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2022-05-03T17:55:32+00:00"
},
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
"version": "v6.0.3", "version": "v6.0.3",

View File

@@ -7,7 +7,6 @@ return [
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],

View File

@@ -1,6 +1,6 @@
twig: twig:
default_path: '%kernel.project_dir%/templates' default_path: '%kernel.project_dir%/templates'
form_theme: 'bootstrap_5_layout.html.twig'
when@test: when@test:
twig: twig:
strict_variables: true strict_variables: true

View File

@@ -1,49 +0,0 @@
webpack_encore:
# The path where Encore is building the assets - i.e. Encore.setOutputPath()
output_path: '%kernel.project_dir%/public/build'
# If multiple builds are defined (as shown below), you can disable the default build:
# output_path: false
# Set attributes that will be rendered on all script and link tags
script_attributes:
defer: true
# Uncomment (also under link_attributes) if using Turbo Drive
# https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change
# 'data-turbo-track': reload
# link_attributes:
# Uncomment if using Turbo Drive
# 'data-turbo-track': reload
# If using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials')
# crossorigin: 'anonymous'
# Preload all rendered script and link tags automatically via the HTTP/2 Link header
# preload: true
# Throw an exception if the entrypoints.json file is missing or an entry is missing from the data
# strict_mode: false
# If you have multiple builds:
# builds:
# pass "frontend" as the 3rg arg to the Twig functions
# {{ encore_entry_script_tags('entry1', null, 'frontend') }}
# frontend: '%kernel.project_dir%/public/frontend/build'
# Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
# Put in config/packages/prod/webpack_encore.yaml
# cache: true
framework:
assets:
json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'
#when@prod:
# webpack_encore:
# # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes)
# # Available in version 1.2
# cache: true
#when@test:
# webpack_encore:
# strict_mode: false

View File

@@ -4,6 +4,8 @@
# Put parameters here that don't need to change on each machine where the app is deployed # Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters: parameters:
book_covers: '%kernel.project_dir%/public/book_covers/'
book_files: '%kernel.project_dir%/public/book_files/'
services: services:
# default configuration for services in *this* file # default configuration for services in *this* file

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20220520082729 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE book (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) NOT NULL, language VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, publisher VARCHAR(255) DEFAULT NULL, publish_date DATE DEFAULT NULL, filename VARCHAR(255) DEFAULT NULL, original_filename VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE `user` (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_8D93D649E7927C74 (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL, INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE book');
$this->addSql('DROP TABLE `user`');
$this->addSql('DROP TABLE messenger_messages');
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20220520124026 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE book ADD subtitle VARCHAR(255) DEFAULT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE book DROP subtitle');
}
}

View File

@@ -1,18 +0,0 @@
{
"devDependencies": {
"@hotwired/stimulus": "^3.0.0",
"@symfony/stimulus-bridge": "^3.0.0",
"@symfony/webpack-encore": "^2.0.0",
"core-js": "^3.0.0",
"regenerator-runtime": "^0.13.2",
"webpack-notifier": "^1.6.0"
},
"license": "UNLICENSED",
"private": true,
"scripts": {
"dev-server": "encore dev-server",
"dev": "encore dev",
"watch": "encore dev --watch",
"build": "encore production --progress"
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace App\Controller;
use App\Entity\Book;
use App\Form\BookType;
use App\Repository\BookRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[Route('/book')]
class BookController extends AbstractController
{
#[Route('/', name: 'app_book_index', methods: ['GET'])]
public function index(BookRepository $bookRepository): Response
{
return $this->render('book/index.html.twig', [
'books' => $bookRepository->findAll(),
]);
}
#[Route('/new', name: 'app_book_new', methods: ['GET', 'POST'])]
public function new(Request $request, BookRepository $bookRepository): Response
{
$book = new Book();
$form = $this->createForm(BookType::class, $book);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$bookRepository->add($book, true);
$cover = $request->files->get('book')['cover'];
if ($cover) {
$cover->move(
$this->getParameter('book_covers'),
'cover_' . $book->getId() . '.' . $cover->guessClientExtension()
);
}
$ebook = $request->files->get('book')['ebook'];
if ($ebook) {
$ebook->move(
$this->getParameter('book_files'),
'ebook_' . $book->getId() . '.' . $ebook->guessClientExtension()
);
}
dd($book->getId());
return $this->redirectToRoute('app_book_index', [], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('book/new.html.twig', [
'book' => $book,
'form' => $form,
]);
}
#[Route('/{id}', name: 'app_book_show', methods: ['GET'])]
public function show(Book $book): Response
{
return $this->render('book/show.html.twig', [
'book' => $book,
]);
}
#[Route('/{id}/edit', name: 'app_book_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Book $book, BookRepository $bookRepository): Response
{
$form = $this->createForm(BookType::class, $book);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$cover = $request->files->get('book')['cover'];
if ($cover) {
$cover->move(
$this->getParameter('book_covers'),
'cover_' . $book->getId() . '.jpg'
);
}
/** @var \Symfony\Component\HttpFoundation\File\UploadedFile $ebook */
$ebook = $request->files->get('book')['ebook'];
if ($ebook) {
$ebook->move(
$this->getParameter('book_files'),
'ebook_' . $book->getId() . '.' . $ebook->guessClientExtension()
);
}
$book->setOriginalFilename($ebook->getClientOriginalName());
$bookRepository->add($book, true);
return $this->redirectToRoute('app_book_index', [], Response::HTTP_SEE_OTHER);
}
return $this->renderForm('book/edit.html.twig', [
'book' => $book,
'form' => $form,
]);
}
#[Route('/{id}', name: 'app_book_delete', methods: ['POST'])]
public function delete(Request $request, Book $book, BookRepository $bookRepository): Response
{
if ($this->isCsrfTokenValid('delete' . $book->getId(), $request->request->get('_token'))) {
$bookRepository->remove($book, true);
}
return $this->redirectToRoute('app_book_index', [], Response::HTTP_SEE_OTHER);
}
#[Route('/download/{id}', name: 'app_book_download', methods: ['GET'])]
public function downloadBook(Book $book): BinaryFileResponse
{
return $this->file(
$this->getParameter('book_files') . '/' . 'ebook_' . $book->getId() . '.'.pathinfo($book->getOriginalFilename(), PATHINFO_EXTENSION),
$book->getOriginalFilename()
);
}
}

140
src/Entity/Book.php Normal file
View File

@@ -0,0 +1,140 @@
<?php
namespace App\Entity;
use App\Repository\BookRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: BookRepository::class)]
class Book
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 255)]
private $Title;
#[ORM\Column(type: 'string', length: 255)]
private $language;
#[ORM\Column(type: 'text', nullable: true)]
private $description;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $publisher;
#[ORM\Column(type: 'date', nullable: true)]
private $publish_date;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $filename;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $original_filename;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $subtitle;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->Title;
}
public function setTitle(string $Title): self
{
$this->Title = $Title;
return $this;
}
public function getLanguage(): ?string
{
return $this->language;
}
public function setLanguage(string $language): self
{
$this->language = $language;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getPublisher(): ?string
{
return $this->publisher;
}
public function setPublisher(?string $publisher): self
{
$this->publisher = $publisher;
return $this;
}
public function getPublishDate(): ?\DateTimeInterface
{
return $this->publish_date;
}
public function setPublishDate(?\DateTimeInterface $publish_date): self
{
$this->publish_date = $publish_date;
return $this;
}
public function getFilename(): ?string
{
return $this->filename;
}
public function setFilename(?string $filename): self
{
$this->filename = $filename;
return $this;
}
public function getOriginalFilename(): ?string
{
return $this->original_filename;
}
public function setOriginalFilename(?string $original_filename): self
{
$this->original_filename = $original_filename;
return $this;
}
public function getSubtitle(): ?string
{
return $this->subtitle;
}
public function setSubtitle(?string $subtitle): self
{
$this->subtitle = $subtitle;
return $this;
}
}

32
src/Form/BookType.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace App\Form;
use App\Entity\Book;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class BookType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('Title')
->add('language')
->add('description')
->add('publisher')
->add('publish_date')
->add('ebook', FileType::class, ['mapped' => false, 'data_class' => null, 'required' => false])
->add('cover', FileType::class, ['mapped' => false, 'data_class' => null, 'required' => false])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Book::class,
]);
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Repository;
use App\Entity\Book;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Book>
*
* @method Book|null find($id, $lockMode = null, $lockVersion = null)
* @method Book|null findOneBy(array $criteria, array $orderBy = null)
* @method Book[] findAll()
* @method Book[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class BookRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Book::class);
}
public function add(Book $entity, bool $flush = false): void
{
$this->getEntityManager()->persist($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
public function remove(Book $entity, bool $flush = false): void
{
$this->getEntityManager()->remove($entity);
if ($flush) {
$this->getEntityManager()->flush();
}
}
// /**
// * @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()
// ;
// }
// public function findOneBySomeField($value): ?Book
// {
// return $this->createQueryBuilder('b')
// ->andWhere('b.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

22
src/Twig/AppExtension.php Normal file
View File

@@ -0,0 +1,22 @@
<?php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class AppExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new TwigFunction('file_exists', [$this, 'file_exists']),
];
}
public function file_exists(string $file): bool
{
return file_exists(ltrim($file, '/'));
}
}

View File

@@ -484,25 +484,6 @@
"./config/packages/messenger.yaml" "./config/packages/messenger.yaml"
] ]
}, },
"symfony/webpack-encore-bundle": {
"version": "1.14",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.10",
"ref": "91f4d29f5ebbd2fcccc71ca55a17d14a17b308e8"
},
"files": [
"./assets/app.js",
"./assets/bootstrap.js",
"./assets/controllers.json",
"./assets/controllers/hello_controller.js",
"./assets/styles/app.css",
"./config/packages/webpack_encore.yaml",
"./package.json",
"./webpack.config.js"
]
},
"symfony/yaml": { "symfony/yaml": {
"version": "v6.0.3" "version": "v6.0.3"
}, },

View File

@@ -3,17 +3,23 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title> <title>{% block title %}Welcome!{% endblock %}</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>"> <link rel="icon"
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #} {# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
{% block stylesheets %} {% block stylesheets %}
{{ encore_entry_link_tags('app') }} {# {{ encore_entry_link_tags('app') }} #}
{% endblock %} {% endblock %}
{% block javascripts %} {% block javascripts %}
{{ encore_entry_script_tags('app') }} {# {{ encore_entry_script_tags('app') }} #}
{% endblock %} {% endblock %}
</head> </head>
<body> <body>
<div class="container">
{% block body %}{% endblock %} {% block body %}{% endblock %}
</div>
</body> </body>
</html> </html>

View File

@@ -0,0 +1,4 @@
<form method="post" action="{{ path('app_book_delete', {'id': book.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ book.id) }}">
<button class="btn btn-danger">Delete</button>
</form>

View File

@@ -0,0 +1,4 @@
{{ form_start(form) }}
{{ form_widget(form) }}
<button class="btn btn-primary">{{ button_label|default('Save') }}</button>
{{ form_end(form) }}

View File

@@ -0,0 +1,13 @@
{% extends 'base.html.twig' %}
{% block title %}Edit Book{% endblock %}
{% block body %}
<h1>Edit Book</h1>
{{ include('book/_form.html.twig', {'button_label': 'Update'}) }}
<a href="{{ path('app_book_index') }}">back to list</a>
{{ include('book/_delete_form.html.twig') }}
{% endblock %}

View File

@@ -0,0 +1,49 @@
{% extends 'base.html.twig' %}
{% block title %}Book index{% endblock %}
{% block body %}
<h1>Book index</h1>
<table class="table">
<thead>
<tr>
<th>Cover</th>
<th>Id</th>
<th>Title</th>
<th>Language</th>
<th>Description</th>
<th>Publisher</th>
<th>Publish_date</th>
<th>Filename</th>
<th>Original_filename</th>
<th>actions</th>
</tr>
</thead>
<tbody>
{% for book in books %}
<tr>
<td>{% if file_exists(asset('book_covers/cover_' ~ book.id ~ '.jpg')) %}<img class="img-thumbnail" src="{{ asset('book_covers/cover_' ~ book.id ~ '.jpg' ) }}" /> {% endif %}</td>
<td>{{ book.id }}</td>
<td>{{ book.Title }}</td>
<td>{{ book.language }}</td>
<td>{{ book.description }}</td>
<td>{{ book.publisher }}</td>
<td>{{ book.publishDate ? book.publishDate|date('Y-m-d') : '' }}</td>
<td>{{ book.filename }}</td>
<td><a href="{{ path('app_book_download', {'id': book.id}) }} ">{{ book.originalFilename }}</a></td>
<td>
<a href="{{ path('app_book_show', {'id': book.id}) }}">show</a>
<a href="{{ path('app_book_edit', {'id': book.id}) }}">edit</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="9">no records found</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ path('app_book_new') }}">Create new</a>
{% endblock %}

View File

@@ -0,0 +1,11 @@
{% extends 'base.html.twig' %}
{% block title %}New Book{% endblock %}
{% block body %}
<h1>Create new Book</h1>
{{ include('book/_form.html.twig') }}
<a href="{{ path('app_book_index') }}">back to list</a>
{% endblock %}

View File

@@ -0,0 +1,50 @@
{% extends 'base.html.twig' %}
{% block title %}Book{% endblock %}
{% block body %}
<h1>Book</h1>
<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>
</tr>
<tr>
<th>Description</th>
<td>{{ book.description }}</td>
</tr>
<tr>
<th>Publisher</th>
<td>{{ book.publisher }}</td>
</tr>
<tr>
<th>Publish_date</th>
<td>{{ book.publishDate ? book.publishDate|date('Y-m-d') : '' }}</td>
</tr>
<tr>
<th>Filename</th>
<td>{{ book.filename }}</td>
</tr>
<tr>
<th>Original_filename</th>
<td>{{ book.originalFilename }}</td>
</tr>
</tbody>
</table>
<a href="{{ path('app_book_index') }}">back to list</a>
<a href="{{ path('app_book_edit', {'id': book.id}) }}">edit</a>
{{ include('book/_delete_form.html.twig') }}
{% endblock %}

View File

@@ -1,75 +0,0 @@
const Encore = require('@symfony/webpack-encore');
// Manually configure the runtime environment if not already configured yet by the "encore" command.
// It's useful when you use tools that rely on webpack.config.js file.
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
//.setManifestKeyPrefix('build/')
/*
* ENTRY CONFIG
*
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if your JavaScript imports CSS.
*/
.addEntry('app', './assets/app.js')
// enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js)
.enableStimulusBridge('./assets/controllers.json')
// When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
.splitEntryChunks()
// will require an extra script tag for runtime.js
// but, you probably want this, unless you're building a single-page app
.enableSingleRuntimeChunk()
/*
* FEATURE CONFIG
*
* Enable & configure other features below. For a full
* list of features, see:
* https://symfony.com/doc/current/frontend.html#adding-more-features
*/
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
.configureBabel((config) => {
config.plugins.push('@babel/plugin-proposal-class-properties');
})
// enables @babel/preset-env polyfills
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = 3;
})
// enables Sass/SCSS support
//.enableSassLoader()
// uncomment if you use TypeScript
//.enableTypeScriptLoader()
// uncomment if you use React
//.enableReactPreset()
// uncomment to get integrity="..." attributes on your script & link tags
// requires WebpackEncoreBundle 1.4 or higher
//.enableIntegrityHashes(Encore.isProduction())
// uncomment if you're having problems with a jQuery plugin
//.autoProvidejQuery()
;
module.exports = Encore.getWebpackConfig();