From 2222e4f2c828f755dea650afc428de186b0fbe6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Legrand?= Date: Sat, 11 Feb 2023 15:45:10 +0100 Subject: [PATCH] Symfony Quest Nr.27 Challenge --- README.md | 12 +++++++--- assets/app.js | 19 ++++++++------- assets/styles/app.scss | 13 +++++++++++ composer.lock | 12 +++++----- migrations/Version20230130065732.php | 35 ++++++++++++++++++++++++++++ src/Controller/ProgramController.php | 28 ++++++++++++++++++++++ src/Entity/Program.php | 31 ++++++++++++++++++++++++ src/Entity/User.php | 34 +++++++++++++++++++++++++++ templates/base.html.twig | 4 +++- templates/program/show.html.twig | 19 ++++++++++----- webpack.config.js | 3 +++ yarn.lock | 2 +- 12 files changed, 187 insertions(+), 25 deletions(-) create mode 100644 migrations/Version20230130065732.php diff --git a/README.md b/README.md index fdf6bd5..1d202ef 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,18 @@ -Démonstration vidéo des fonctionnalités +# Démonstration vidéo des fonctionnalités Symfony Quest Nr.14 Challenge -https://webmshare.com/play/EedXy + + - Ajout d'une catégorie - Ajout d'un programme dans cette nouvelle catégorie Symfony Quest Nr.16 Challenge -https://webmshare.com/play/rE14Q + + - Contrôles serveur sur valeurs du formulaire de création de série - Champs obligatoires, taille maximale, unicité du titre, pas de "plus belle la vie" dans le synopsis +Symfony Quest Nr.27 Challenge + + +- Ajout/Suppression d'une série à la Watchlist pour un utilisateur diff --git a/assets/app.js b/assets/app.js index 0bd8f5b..fda1b58 100644 --- a/assets/app.js +++ b/assets/app.js @@ -5,21 +5,24 @@ * (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.scss'; - // start the Stimulus application import './bootstrap'; -// Message de test -console.log('Hello Webpack Encore !') +// const $ = require('jquery'); +// this "modifies" the jquery module: adding behavior to it +// the bootstrap module doesn't export/return anything +// Message de test +// console.log('Hello Webpack Encore !') -// const $ = require('jquery'); -// // this "modifies" the jquery module: adding behavior to it -// // the bootstrap module doesn't export/return anything require('bootstrap'); +// Import of Bootstrap-icons +import 'bootstrap-icons/font/bootstrap-icons.css'; + +// any CSS you import will output into a single css file (app.css in this case) +import './styles/app.scss'; + // // or you can include specific pieces // // require('bootstrap/js/dist/tooltip'); // // require('bootstrap/js/dist/popover'); diff --git a/assets/styles/app.scss b/assets/styles/app.scss index 60155cc..f384a0b 100644 --- a/assets/styles/app.scss +++ b/assets/styles/app.scss @@ -44,8 +44,21 @@ main { h1 { color: $primary; + font-size: 2em; + margin: 0.5em; + margin-left: 0; } h2 { color: $secondary; +} + +.watchlist-icons { + color: $primary; + font-size: 2em; +} + +.program-poster { + max-height: 400px; + max-height: 50vh; } \ No newline at end of file diff --git a/composer.lock b/composer.lock index 1577ef3..bb4cc4b 100644 --- a/composer.lock +++ b/composer.lock @@ -7414,16 +7414,16 @@ }, { "name": "symfony/webpack-encore-bundle", - "version": "v1.16.0", + "version": "v1.16.1", "source": { "type": "git", "url": "https://github.com/symfony/webpack-encore-bundle.git", - "reference": "bb399930c0299866258b616a74a27b50b94c5d45" + "reference": "1862d71e483769b40278548a30e756ce13ef9d4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/webpack-encore-bundle/zipball/bb399930c0299866258b616a74a27b50b94c5d45", - "reference": "bb399930c0299866258b616a74a27b50b94c5d45", + "url": "https://api.github.com/repos/symfony/webpack-encore-bundle/zipball/1862d71e483769b40278548a30e756ce13ef9d4c", + "reference": "1862d71e483769b40278548a30e756ce13ef9d4c", "shasum": "" }, "require": { @@ -7467,7 +7467,7 @@ "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.16.0" + "source": "https://github.com/symfony/webpack-encore-bundle/tree/v1.16.1" }, "funding": [ { @@ -7483,7 +7483,7 @@ "type": "tidelift" } ], - "time": "2022-10-18T15:21:06+00:00" + "time": "2023-01-18T19:37:55+00:00" }, { "name": "symfony/yaml", diff --git a/migrations/Version20230130065732.php b/migrations/Version20230130065732.php new file mode 100644 index 0000000..2a7756f --- /dev/null +++ b/migrations/Version20230130065732.php @@ -0,0 +1,35 @@ +addSql('CREATE TABLE watchlist (user_id INT NOT NULL, program_id INT NOT NULL, INDEX IDX_340388D3A76ED395 (user_id), INDEX IDX_340388D33EB8070A (program_id), PRIMARY KEY(user_id, program_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE watchlist ADD CONSTRAINT FK_340388D3A76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE'); + $this->addSql('ALTER TABLE watchlist ADD CONSTRAINT FK_340388D33EB8070A FOREIGN KEY (program_id) REFERENCES program (id) ON DELETE CASCADE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE watchlist DROP FOREIGN KEY FK_340388D3A76ED395'); + $this->addSql('ALTER TABLE watchlist DROP FOREIGN KEY FK_340388D33EB8070A'); + $this->addSql('DROP TABLE watchlist'); + } +} diff --git a/src/Controller/ProgramController.php b/src/Controller/ProgramController.php index ffb26b8..85282ae 100644 --- a/src/Controller/ProgramController.php +++ b/src/Controller/ProgramController.php @@ -16,12 +16,14 @@ use App\Form\ProgramType; use App\Form\SearchProgramType; use App\Repository\CommentRepository; +use App\Repository\UserRepository; use App\Service\ProgramDuration; use DateTime; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Email; use Symfony\Component\String\Slugger\SluggerInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\Security\Core\User\UserInterface; #[Route('/program', name: 'program_')] class ProgramController extends AbstractController @@ -204,6 +206,32 @@ public function showEpisode( ]); } + // WATCHLIST START + + #[Route('/{id}/watchlist', methods: ['GET', 'POST'], name: 'watchlist')] + public function addToWatchlist(Program $program, UserRepository $userRepository): Response + { + if (!$program) { + throw $this->createNotFoundException( + 'Aucune série correspondante en base.' + ); + } + + /** @var \App\Entity\User */ + $user = $this->getUser(); + if ($user->isInWatchlist($program)) { + $user->removeFromWatchlist($program); + } else { + $user->addToWatchlist($program); + } + + $userRepository->save($user, true); + + return $this->redirectToRoute('program_show', ['slug' => $program->getSlug()], Response::HTTP_SEE_OTHER); + } + + // WATCHLIST END + // DELETE PROGRAM START #[Route('/{id}', name: 'delete', methods: ['POST'])] diff --git a/src/Entity/Program.php b/src/Entity/Program.php index 6ae8292..26edb3f 100644 --- a/src/Entity/Program.php +++ b/src/Entity/Program.php @@ -82,11 +82,15 @@ class Program #[ORM\JoinColumn(nullable: false)] private ?User $Owner = null; + #[ORM\ManyToMany(targetEntity: User::class, mappedBy: 'watchlist')] + private Collection $viewers; + public function __construct() { // $this->number = new ArrayCollection(); $this->seasons = new ArrayCollection(); $this->actors = new ArrayCollection(); + $this->viewers = new ArrayCollection(); } @@ -249,4 +253,31 @@ public function setOwner(?User $Owner): self return $this; } + + /** + * @return Collection + */ + public function getViewers(): Collection + { + return $this->viewers; + } + + public function addViewer(User $viewer): self + { + if (!$this->viewers->contains($viewer)) { + $this->viewers->add($viewer); + $viewer->addToWatchlist($this); + } + + return $this; + } + + public function removeViewer(User $viewer): self + { + if ($this->viewers->removeElement($viewer)) { + $viewer->removeFromWatchlist($this); + } + + return $this; + } } diff --git a/src/Entity/User.php b/src/Entity/User.php index 24321ae..65c45c8 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -40,10 +40,15 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\Column(type: 'boolean')] private $isVerified = false; + #[ORM\ManyToMany(targetEntity: Program::class, inversedBy: 'viewers')] + #[ORM\JoinTable(name: 'watchlist')] + private Collection $watchlist; + public function __construct() { $this->comments = new ArrayCollection(); $this->programs = new ArrayCollection(); + $this->watchlist = new ArrayCollection(); } public function getId(): ?int @@ -187,4 +192,33 @@ public function setIsVerified(bool $isVerified): self return $this; } + + /** + * @return Collection + */ + public function getWatchlist(): Collection + { + return $this->watchlist; + } + + public function addToWatchlist(Program $watchlist): self + { + if (!$this->watchlist->contains($watchlist)) { + $this->watchlist->add($watchlist); + } + + return $this; + } + + public function removeFromWatchlist(Program $watchlist): self + { + $this->watchlist->removeElement($watchlist); + + return $this; + } + + public function isInWatchlist(Program $program): bool + { + return $this->watchlist->contains($program) ? true : false; + } } diff --git a/templates/base.html.twig b/templates/base.html.twig index 584843f..968976c 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -1,7 +1,9 @@ - + + + {% block title %}Welcome! diff --git a/templates/program/show.html.twig b/templates/program/show.html.twig index 2fc1062..fba624f 100644 --- a/templates/program/show.html.twig +++ b/templates/program/show.html.twig @@ -5,20 +5,27 @@ {% block body %} <div class="media"> - {% if program.poster is empty %} - <div></div> - {% else %} + {% if program.poster %} {% if program.poster starts with 'http' %} <div> - <img class="align-self-start mr-3" src="{{program.poster}}" alt="{{ program.title }} poster"> + <img class="align-self-start mr-3 program-poster" src="{{program.poster}}" alt="{{ program.title }} poster"> </div> {% else %} <div> - <img class="align-self-start mr-3" src="{{ vich_uploader_asset(program) }}" alt="{{ program.title }} poster"> + <img class="align-self-start mr-3 program-poster" src="{{ vich_uploader_asset(program) }}" alt="{{ program.title }} poster"> </div> {% endif %} {% endif %} - <h1 class="mt-0">{{ program.title }}</h1> + <div class="d-flex flex-row align-items-center"> + <h1>{{ program.title }}</h1> + {% if app.user %} + <div> + <a href="{{ path('program_watchlist', {id: program.id})}}"> + <i class="watchlist-icons bi {{ app.user.isInWatchlist(program) ? 'bi-heart-fill' : 'bi-heart' }}" alt="watchlist"></i> + </a> + </div> + {% endif %} + </div> <p>{{ program.synopsis }}</p> <p>Catégorie : {{ program.category.name }}</p> diff --git a/webpack.config.js b/webpack.config.js index 7fb2dc4..5591936 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -97,5 +97,8 @@ fullConfig.devServer = { watchFiles: { paths: ['templates/**/*.html.twig'], }, + headers: { + 'Access-Control-Allow-Origin': '*', + }, }; module.exports = fullConfig; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4283dce..5de96be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1604,7 +1604,7 @@ boolbase@^1.0.0: bootstrap-icons@^1.10.3: version "1.10.3" - resolved "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.10.3.tgz" + resolved "https://registry.yarnpkg.com/bootstrap-icons/-/bootstrap-icons-1.10.3.tgz#c587b078ca6743bef4653fe90434b4aebfba53b2" integrity sha512-7Qvj0j0idEm/DdX9Q0CpxAnJYqBCFCiUI6qzSPYfERMcokVuV9Mdm/AJiVZI8+Gawe4h/l6zFcOzvV7oXCZArw== bootstrap@^5.2.3: