From 1433e2fd39a63d2610bb90a1ece94cc8d6e80e94 Mon Sep 17 00:00:00 2001 From: ArtyomDev Date: Fri, 6 May 2022 21:50:59 +0500 Subject: [PATCH 1/4] Lesson 2.8 --- migrations/Version20220506101824.php | 38 ++++++++ migrations/Version20220506111051.php | 33 +++++++ migrations/Version20220506132158.php | 35 ++++++++ src/Controller/ProjectController.php | 83 +++++++++++++++++ src/Controller/TaskController.php | 11 ++- src/Entity/Project.php | 118 +++++++++++++++++++++++++ src/Entity/Task.php | 30 ++++++- src/Entity/User.php | 13 +++ src/Repository/ProjectRepository.php | 16 ++++ src/Type/ProjectType.php | 25 ++++++ src/Type/TaskType.php | 28 ++++++ templates/projects/create.html.twig | 13 +++ templates/projects/list.html.twig | 42 +++++++++ templates/task/list.html.twig | 9 +- templates/task/project_tasks.html.twig | 40 +++++++++ 15 files changed, 523 insertions(+), 11 deletions(-) create mode 100644 migrations/Version20220506101824.php create mode 100644 migrations/Version20220506111051.php create mode 100644 migrations/Version20220506132158.php create mode 100644 src/Controller/ProjectController.php create mode 100644 src/Entity/Project.php create mode 100644 src/Repository/ProjectRepository.php create mode 100644 src/Type/ProjectType.php create mode 100644 templates/projects/create.html.twig create mode 100644 templates/projects/list.html.twig create mode 100644 templates/task/project_tasks.html.twig diff --git a/migrations/Version20220506101824.php b/migrations/Version20220506101824.php new file mode 100644 index 0000000..111659f --- /dev/null +++ b/migrations/Version20220506101824.php @@ -0,0 +1,38 @@ +addSql('CREATE TABLE project (id INT AUTO_INCREMENT NOT NULL, owner_id INT DEFAULT NULL, `key` VARCHAR(5) NOT NULL, name VARCHAR(255) NOT NULL, INDEX IDX_2FB3D0EE7E3C61F9 (owner_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE project ADD CONSTRAINT FK_2FB3D0EE7E3C61F9 FOREIGN KEY (owner_id) REFERENCES user (id)'); + $this->addSql('ALTER TABLE task ADD project_id INT DEFAULT NULL, CHANGE is_completed is_completed TINYINT(1) DEFAULT 0 NOT NULL'); + $this->addSql('ALTER TABLE task ADD CONSTRAINT FK_527EDB25166D1F9C FOREIGN KEY (project_id) REFERENCES project (id)'); + $this->addSql('CREATE INDEX IDX_527EDB25166D1F9C ON task (project_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE task DROP FOREIGN KEY FK_527EDB25166D1F9C'); + $this->addSql('DROP TABLE project'); + $this->addSql('DROP INDEX IDX_527EDB25166D1F9C ON task'); + $this->addSql('ALTER TABLE task DROP project_id, CHANGE is_completed is_completed TINYINT(1) DEFAULT NULL'); + } +} diff --git a/migrations/Version20220506111051.php b/migrations/Version20220506111051.php new file mode 100644 index 0000000..eccb6aa --- /dev/null +++ b/migrations/Version20220506111051.php @@ -0,0 +1,33 @@ +addSql('ALTER TABLE project CHANGE `key` `key` VARCHAR(255) NOT NULL'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_2FB3D0EE8A90ABA9 ON project (`key`)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX UNIQ_2FB3D0EE8A90ABA9 ON project'); + $this->addSql('ALTER TABLE project CHANGE `key` `key` VARCHAR(5) NOT NULL'); + } +} diff --git a/migrations/Version20220506132158.php b/migrations/Version20220506132158.php new file mode 100644 index 0000000..6b8c35a --- /dev/null +++ b/migrations/Version20220506132158.php @@ -0,0 +1,35 @@ +addSql('DROP INDEX UNIQ_2FB3D0EE8A90ABA9 ON project'); + $this->addSql('ALTER TABLE project CHANGE `key` project_key VARCHAR(255) NOT NULL'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_2FB3D0EE18EB714A ON project (project_key)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP INDEX UNIQ_2FB3D0EE18EB714A ON project'); + $this->addSql('ALTER TABLE project CHANGE project_key `key` VARCHAR(255) NOT NULL'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_2FB3D0EE8A90ABA9 ON project (`key`)'); + } +} diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php new file mode 100644 index 0000000..296da60 --- /dev/null +++ b/src/Controller/ProjectController.php @@ -0,0 +1,83 @@ + getUser(); + + if (in_array("ROLE_ADMIN", $user -> getRoles())) { + $projects = $this -> getDoctrine() -> getManager() -> getRepository(Project::class) -> findAll(); + } else { + $projects = $this -> getDoctrine() -> getManager() -> getRepository(Project::class) -> findBy(['owner' => $user]); + } + return $this -> render("projects/list.html.twig", [ + "projects" => $projects, + ]); + } + + /** + * @Route("/projects/create", name="project_create") + * @param Request $request + * @return Response + */ + public function create(Request $request): Response + { + $project = new Project(); + $form = $this -> createForm(ProjectType::class, $project); + + $form -> handleRequest($request); + + if ($form -> isSubmitted() && $form -> isValid()) { + + $project -> setOwner($this -> getUser()); + + $this -> getDoctrine() -> getManager() -> persist($project); + $this -> getDoctrine() -> getManager() -> flush(); + + return $this -> redirectToRoute("project_list"); + } + + return $this -> render("projects/create.html.twig", [ + "form" => $form -> createView(), + ]); + } + + /** + * @Route("/projects/{slug}", name="project_info") + * @return Response + */ + public function info($slug, Request $request): Response + { + $project = $this -> getDoctrine() -> getRepository(Project::class) -> + findBy(["projectKey" => $slug]); + + $tasks = $this -> getDoctrine() -> getRepository(Task::class) -> findBy(["project" => $project]); + + return $this->render('task/project_tasks.html.twig', [ + 'tasks' => $tasks, + "project_name" => $project[0] -> getName(), + ]); + } + +} \ No newline at end of file diff --git a/src/Controller/TaskController.php b/src/Controller/TaskController.php index 0d9f9ad..60f99c0 100644 --- a/src/Controller/TaskController.php +++ b/src/Controller/TaskController.php @@ -2,6 +2,7 @@ namespace App\Controller; +use App\Entity\Project; use App\Entity\Task; use App\Type\TaskFilterType; use App\Type\TaskType; @@ -22,12 +23,13 @@ class TaskController extends AbstractController public function create(Request $request): Response { $task = new Task(); - $form = $this->createForm(TaskType::class, $task); + $form = $this->createForm(TaskType::class, $task, [ + "projects_list" => $this -> getDoctrine() -> getRepository(Project::class) -> findAll() + ]); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - $task->setAuthor($this->getUser()); $this->getDoctrine()->getManager()->persist($task); @@ -39,7 +41,6 @@ public function create(Request $request): Response return $this->render("task/create.html.twig", [ 'form' => $form->createView() ]); - } /** @@ -73,11 +74,9 @@ public function list(Request $request): Response ]); } - - return $this->render('task/list.html.twig', [ 'tasks' => $tasks, - 'filterForm' => $taskFilterForm->createView() + 'filterForm' => $taskFilterForm->createView(), ]); } diff --git a/src/Entity/Project.php b/src/Entity/Project.php new file mode 100644 index 0000000..d41abb5 --- /dev/null +++ b/src/Entity/Project.php @@ -0,0 +1,118 @@ +tasks = new ArrayCollection(); + } + + /** + * @return Collection|Task[] + */ + public function getTasks(): Collection + { + return $this->tasks; + } + + public function __toString() + { + return $this->getName(); + } + + /** + * set project Owner + * @param User | null $owner + * @return void + */ + public function setOwner(UserInterface $owner = null) + { + $this -> owner = $owner; + } + + /** + * return project owner + * @return User | null + */ + public function getOwner(): ?User + { + return $this -> owner; + } + + /** + * @return mixed + */ + public function getName() + { + return $this -> name; + } + + /** + * @param mixed $name + */ + public function setName($name): void + { + $this -> name = $name; + } + + /** + * @return mixed + */ + public function getProjectKey() + { + return $this -> projectKey; + } + + /** + * @param mixed $projectKey + */ + public function setProjectKey($projectKey): void + { + $this -> projectKey = $projectKey; + } +} \ No newline at end of file diff --git a/src/Entity/Task.php b/src/Entity/Task.php index 66a4b57..d28097f 100644 --- a/src/Entity/Task.php +++ b/src/Entity/Task.php @@ -4,6 +4,7 @@ use App\Repository\TaskRepository; use Symfony\Component\Security\Core\User\UserInterface; +//use Symfony\Component\Security\Core\; use Symfony\Component\Validator\Constraints as Assert; use Doctrine\ORM\Mapping as ORM; @@ -55,6 +56,12 @@ class Task */ protected $author; + /** + * @ORM\ManyToOne(targetEntity="App\Entity\Project", inversedBy="project") + * @var Project + */ + protected $project; + /** * Create empty task */ @@ -75,16 +82,35 @@ public function setAuthor(UserInterface $author = null) $this->author = $author; } - /** * Return task author * @return User|null */ - public function getAuthor() + public function getAuthor(): ?User { return $this->author; } + /** + * Set task Project + * @param Project|null $project + * @return void + */ + public function setProject(Project $project = null) + { + $this->project = $project; + } + + + /** + * Return task Project + * @return Project|null + */ + public function getProject(): ?Project + { + return $this->project; + } + /** * @return mixed diff --git a/src/Entity/User.php b/src/Entity/User.php index 98648d0..5680de4 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -79,6 +79,19 @@ public function getRoles(): array return array_unique($roles); } + /** + * @param string $role + * @return bool + */ + public function checkRole(string $role): bool + { + return in_array($role, $this -> roles); + } + + /** + * @param array $roles + * @return $this + */ public function setRoles(array $roles): self { $this->roles = $roles; diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php new file mode 100644 index 0000000..5d73eb3 --- /dev/null +++ b/src/Repository/ProjectRepository.php @@ -0,0 +1,16 @@ +add('project_key', TextType::class, [ + "label" => "Введите slug ключ" + ]) + ->add('name', TextType::class, [ + "label" => "Название проекта" + ]) + ->add('save', SubmitType::class) + ; + } + +} \ No newline at end of file diff --git a/src/Type/TaskType.php b/src/Type/TaskType.php index 3b58193..ee33313 100644 --- a/src/Type/TaskType.php +++ b/src/Type/TaskType.php @@ -2,15 +2,36 @@ namespace App\Type; +use App\Entity\Project; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class TaskType extends AbstractType { + private function getProjectName($item, $key) + { + $item = $item -> getName(); + } + + public function configureOptions(OptionsResolver $resolver) + { +// parent::configureOptions($resolver); // TODO: Change the autogenerated stub + + $resolver->setDefaults([ + 'projects_list' => [], + ]); + + $resolver->setAllowedTypes('projects_list', 'array'); + + } + public function buildForm(FormBuilderInterface $builder, array $options) { $builder @@ -19,6 +40,13 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->add('dueDate', DateType::class, [ 'years' => range(2022,2023) ]) + ->add('project', ChoiceType::class, [ + "choices" => $options["projects_list"], + 'choice_label' => function(?Project $entity) { + return $entity ? $entity -> getProjectKey() : ""; + }, + "label" => "Выберите проект", + ]) ->add('save', SubmitType::class) ; } diff --git a/templates/projects/create.html.twig b/templates/projects/create.html.twig new file mode 100644 index 0000000..012ece3 --- /dev/null +++ b/templates/projects/create.html.twig @@ -0,0 +1,13 @@ +{% extends "base.html.twig" %} + +{% block body %} +
+
+
+
+ {{ form(form, {"action": path("project_create"), "method": "POST"}) }} +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/projects/list.html.twig b/templates/projects/list.html.twig new file mode 100644 index 0000000..d7bc36d --- /dev/null +++ b/templates/projects/list.html.twig @@ -0,0 +1,42 @@ +{% extends 'base.html.twig' %} + +{% block body %} +{% set admin = app.user.checkRole("ROLE_ADMIN") %} +
+
+
+
+
+ + + + + + {% if admin %} + + {% endif %} + + + + + {% for project in projects %} + + + + {% if admin %} + + {% endif %} + + + {% endfor %} + +
КлючНазваниеВладелецДействия
+ {{ project.projectKey }}{{ project.name }}{{ project.owner }}Удалить
+

Создать новый проект

+

Список всех задач

+
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/task/list.html.twig b/templates/task/list.html.twig index 0d50fd4..c3d896f 100644 --- a/templates/task/list.html.twig +++ b/templates/task/list.html.twig @@ -1,5 +1,9 @@ {% extends 'base.html.twig' %} +{% block title %} + "Tasks" +{% endblock title %} + {% block body %}
diff --git a/templates/task/project_tasks.html.twig b/templates/task/project_tasks.html.twig new file mode 100644 index 0000000..a51a6a8 --- /dev/null +++ b/templates/task/project_tasks.html.twig @@ -0,0 +1,40 @@ +{% extends "base.html.twig" %} + +{% block title %} + {{ project_name }} +{% endblock %} + +{% block body %} + +
+
+
+
+
+ + + + + + + + + + + {% for task in tasks %} + + + + + + + {% endfor %} + +
НазваниеОписаниеСрок выполненияВыполнена
{{ task.name }}{{ task.description }}{{ task.dueDate|date("d.m.Y") }}{{ task.isCompleted ? 'Выполнена' : 'Не выполнена' }}
+

Список всех проектов

+
+
+
+
+
+{% endblock %} \ No newline at end of file From 90b78ec097333bffa77f13f52a7e06237c077899 Mon Sep 17 00:00:00 2001 From: ArtyomDev Date: Sat, 21 May 2022 11:01:48 +0500 Subject: [PATCH 2/4] =?UTF-8?q?=D0=9A=D0=BE=D0=BC=D0=B8=D1=82=20=D1=81?= =?UTF-8?q?=D0=BE=20=D0=B2=D1=81=D0=B5=D0=BC=D0=B8=20=D0=BD=D1=83=D0=B6?= =?UTF-8?q?=D0=BD=D1=8B=D0=BC=D0=B8=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Controller/ProjectController.php | 60 ++++++++++++--- src/Controller/TaskController.php | 106 +++++++++++++++++++++++---- src/Entity/Project.php | 12 ++- src/Repository/ProjectRepository.php | 17 +++++ src/Repository/TaskRepository.php | 78 ++++++++++++++++++++ src/Type/TaskFilterType.php | 73 ++++++++++++++++-- src/Type/TaskType.php | 5 -- templates/projects/create.html.twig | 5 +- templates/projects/list.html.twig | 5 +- templates/task/create.html.twig | 5 +- templates/task/list.html.twig | 7 +- 11 files changed, 328 insertions(+), 45 deletions(-) diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index 296da60..b3d1fa6 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -5,14 +5,11 @@ use App\Entity\Project; use App\Entity\Task; use App\Type\ProjectType; -use App\Type\TaskFilterType; -use phpDocumentor\Reflection\Types\Null_; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Annotation\Route; -use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; use const http\Client\Curl\PROXY_HTTP; +use Symfony\Component\Routing\Annotation\Route; class ProjectController extends AbstractController { @@ -25,11 +22,12 @@ class ProjectController extends AbstractController public function list(Request $request): Response { $user = $this -> getUser(); + $project_repository = $this -> getDoctrine() -> getManager() -> getRepository(Project::class); if (in_array("ROLE_ADMIN", $user -> getRoles())) { - $projects = $this -> getDoctrine() -> getManager() -> getRepository(Project::class) -> findAll(); + $projects = $project_repository -> findAll(); } else { - $projects = $this -> getDoctrine() -> getManager() -> getRepository(Project::class) -> findBy(['owner' => $user]); + $projects = $project_repository -> findBy(['owner' => $user]); } return $this -> render("projects/list.html.twig", [ "projects" => $projects, @@ -60,6 +58,7 @@ public function create(Request $request): Response return $this -> render("projects/create.html.twig", [ "form" => $form -> createView(), + "action" => "creating" ]); } @@ -69,15 +68,58 @@ public function create(Request $request): Response */ public function info($slug, Request $request): Response { - $project = $this -> getDoctrine() -> getRepository(Project::class) -> - findBy(["projectKey" => $slug]); + $project = $this -> getDoctrine() -> getManager() -> find(Project::class, $slug); $tasks = $this -> getDoctrine() -> getRepository(Task::class) -> findBy(["project" => $project]); return $this->render('task/project_tasks.html.twig', [ 'tasks' => $tasks, - "project_name" => $project[0] -> getName(), + "project_name" => $project -> getName(), ]); } + /** + * @Route("/projects/{slug}/edit", name="project_edit") + * @param $slug + * @param Request $request + * @return Response + */ + public function edit($slug, Request $request): Response + { + $project = $this->getDoctrine()->getRepository(Project::class) -> findBy(["projectKey" => $slug])[0]; + + $form = $this->createForm(ProjectType::class, $project); + + $form -> handleRequest($request); + + if ($form -> isSubmitted() && $form -> isValid()) { + $this -> getDoctrine() -> getManager() -> persist($project); + $this -> getDoctrine() -> getManager() -> flush(); + + return $this -> redirectToRoute("project_list"); + } + + return $this->render("projects/create.html.twig", [ + "form" => $form->createView(), + "action" => "editing", + "slug" => $slug + ]); + } + + /** + * @Route("/projects/{slug}/delete", name="project_delete") + * @param $slug + * @param Request $request + * @return Response + */ + public function delete($slug, Request $request): Response + { + $em = $this->getDoctrine()->getManager(); + $project = $this->getDoctrine()->getRepository(Project::class) -> findBy(["projectKey" => $slug])[0]; + + $em -> remove($project); + $em -> flush(); + + return $this->redirectToRoute("project_list"); + } } \ No newline at end of file diff --git a/src/Controller/TaskController.php b/src/Controller/TaskController.php index 60f99c0..50adcb6 100644 --- a/src/Controller/TaskController.php +++ b/src/Controller/TaskController.php @@ -6,11 +6,15 @@ use App\Entity\Task; use App\Type\TaskFilterType; use App\Type\TaskType; +use Doctrine\Common\Collections\Criteria; +use phpDocumentor\Reflection\Types\Integer; +use PhpParser\Node\Expr\Array_; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\HttpFoundation\Request; +use const http\Client\Curl\PROXY_HTTP; class TaskController extends AbstractController { @@ -23,8 +27,13 @@ class TaskController extends AbstractController public function create(Request $request): Response { $task = new Task(); + $user = $this -> getUser(); + $project_repository = $this -> getDoctrine() -> getRepository(Project::class); + $form = $this->createForm(TaskType::class, $task, [ - "projects_list" => $this -> getDoctrine() -> getRepository(Project::class) -> findAll() + "projects_list" => in_array("ROLE_ADMIN", $user -> getRoles()) ? + $project_repository -> findAll() : + $project_repository -> findBy(["owner" => $user]) ]); $form->handleRequest($request); @@ -39,7 +48,8 @@ public function create(Request $request): Response } return $this->render("task/create.html.twig", [ - 'form' => $form->createView() + 'form' => $form->createView(), + "action" => "creating", ]); } @@ -49,29 +59,60 @@ public function create(Request $request): Response */ public function list(Request $request): Response { - $taskFilterForm = $this->createForm(TaskFilterType::class); + $user = $this -> getUser(); + $user_is_admin = in_array("ROLE_ADMIN", $user -> getRoles()); + + $project_repository = $this -> getDoctrine() -> getRepository(Project::class); + $task_repository = $this->getDoctrine()->getRepository(Task::class); + + $projects_list = $user_is_admin ? + $project_repository -> findAll() : + $project_repository -> findBy(["owner" => $user]); + + $taskFilterForm = $this->createForm(TaskFilterType::class, options: [ + "projects_list" => $projects_list, + "authors_list" => $user_is_admin ? + $task_repository ->getTasksAuthors() : + $task_repository -> getTasksAuthorsByProjectOwnerId($user -> getId()), + "owners_list" => $user_is_admin ? + $project_repository -> getProjectOwners() : + Array($user -> getUsername() => $user -> getId()), + ]); $taskFilterForm->handleRequest($request); if ($taskFilterForm->isSubmitted() && $taskFilterForm->isValid()) { - $filter = $taskFilterForm->getData(); - if ($filter['isCompleted'] === null) { - unset($filter['isCompleted']); + $data = $taskFilterForm->getData(); + + $filters = Array(); +// dd($data); + if ($data["isCompleted"] !== null) { + $filters[] = "t.is_completed=" . (int)$data["isCompleted"]; + } + + if ($data["filter-by-project"]) { + $filters[] = "t.project_id=" . $data["project"] -> getId(); + } + + if ($data["filter-by-author"]) { + $filters[] = "t.author_id=" . $data["author"]; } - $tasks = $this->getDoctrine()->getRepository(Task::class) - ->findBy($filter, [ - 'dueDate' => 'DESC' + if ($data["filter-by-owner"]) { + $filters[] = "p.owner_id=" . $data["project-owner"]; + } + + $tasks = $task_repository -> findByFilters($filters, + orders: [ + "dueDate" => $data["sort-by-date"] ? "ASC" : "DESC", + "name" => $data["sort-by-name"] ? "ASC" : "DESC" ]); } else { - /** @var $tasks */ - $tasks = $this->getDoctrine()->getManager() - ->getRepository(Task::class) - ->findBy([], [ - 'dueDate' => 'DESC' - ]); + $tasks = $user_is_admin ? + $task_repository -> findBy([], ["dueDate" => "DESC"]) : + $task_repository -> getTasksByProjectOwnerId($user -> getId()); } return $this->render('task/list.html.twig', [ @@ -103,4 +144,39 @@ public function complete($id): Response return $this->redirectToRoute('task_list'); } + + /** + * @Route("/tasks/{id}/edit", name="task_edit") + * @param $id + * @param Request $request + * @return Response + */ + public function edit($id, Request $request): Response + { + $task = $this->getDoctrine()->getManager()->find(Task::class, $id); + + $user = $this -> getUser(); + $project_repository = $this -> getDoctrine() -> getRepository(Project::class); + + $form = $this->createForm(TaskType::class, data: $task, options: [ + "projects_list" => in_array("ROLE_ADMIN", $user -> getRoles()) ? + $project_repository -> findAll() : + $project_repository -> findBy(["owner" => $user]) + ]); + + $form -> handleRequest($request); + + if ($form -> isSubmitted() && $form -> isValid()) { + $this->getDoctrine()->getManager()->persist($task); + $this->getDoctrine()->getManager()->flush(); + + return $this->redirectToRoute('task_list'); + } + + return $this -> render('task/create.html.twig', [ + "form" => $form -> createView(), + "action" => "editing", + "id" => $id + ]); + } } \ No newline at end of file diff --git a/src/Entity/Project.php b/src/Entity/Project.php index d41abb5..5e28c81 100644 --- a/src/Entity/Project.php +++ b/src/Entity/Project.php @@ -11,7 +11,7 @@ use Symfony\Component\Security\Core\User\UserInterface; /** - * @ORM\Entity(repositoryClass=ProjectRepository::class) + * @ORM\Entity(repositoryClass="App\Repository\ProjectRepository") */ class Project { @@ -43,7 +43,7 @@ class Project protected $owner; /** - * @ORM\OneToMany(targetEntity="App\Entity\Task", mappedBy="project") + * @ORM\OneToMany(targetEntity="App\Entity\Task", mappedBy="project", orphanRemoval=true) */ protected $tasks; @@ -92,6 +92,14 @@ public function getName() return $this -> name; } + /** + * @return mixed + */ + public function getId() + { + return $this -> id; + } + /** * @param mixed $name */ diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php index 5d73eb3..84bbb3b 100644 --- a/src/Repository/ProjectRepository.php +++ b/src/Repository/ProjectRepository.php @@ -4,6 +4,7 @@ use App\Entity\Project; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ManagerRegistry; @@ -13,4 +14,20 @@ public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Project::class); } + + /** + * @throws Exception + */ + public function getProjectOwners() : array + { + $conn = $this->getEntityManager()->getConnection(); + + $sql = " + SELECT DISTINCT u.email, u.id + FROM project as p INNER JOIN user as u + ON p.owner_id = u.id; + "; + $stmt = $conn->prepare($sql); + return $stmt ->executeQuery() -> fetchAllAssociative(); + } } \ No newline at end of file diff --git a/src/Repository/TaskRepository.php b/src/Repository/TaskRepository.php index 43cc1ba..3986b57 100644 --- a/src/Repository/TaskRepository.php +++ b/src/Repository/TaskRepository.php @@ -4,6 +4,7 @@ use App\Entity\Task; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ManagerRegistry; @@ -14,5 +15,82 @@ public function __construct(ManagerRegistry $registry) parent::__construct($registry, Task::class); } + /** + * @throws Exception + */ + public function getTasksByProjectOwnerId(int $id) : array + { + $conn = $this -> getEntityManager() -> getConnection(); + + $sql = " + SELECT t.id, t.name, t.description, t.due_date as dueDate, + u.email as author, t.is_completed as isCompleted + FROM task as t INNER JOIN user as u ON t.author_id = u.id + WHERE project_id in (SELECT id FROM project WHERE owner_id = :id); + "; + $stmt = $conn -> prepare($sql); + return $stmt ->executeQuery(["id" => $id]) -> fetchAllAssociative(); + } + + /** + * @throws Exception + */ + public function getTasksAuthors() : array + { + $conn = $this -> getEntityManager() -> getConnection(); + + $sql = " + SELECT DISTINCT u.email, u.id + FROM task as t inner join user as u + on t.author_id = u.id; + "; + $stmt = $conn -> prepare($sql); + return $stmt ->executeQuery() -> fetchAllAssociative(); + } + + /** + * @throws Exception + */ + public function getTasksAuthorsByProjectOwnerId(int $id) : array + { + $conn = $this -> getEntityManager() -> getConnection(); + + $sql = " + SELECT DISTINCT u.email, u.id + FROM task as t INNER JOIN user as u ON t.author_id = u.id + WHERE project_id in (SELECT id FROM project WHERE owner_id = :id); + "; + $stmt = $conn -> prepare($sql); + return $stmt ->executeQuery(["id" => $id]) -> fetchAllKeyValue(); + } + + + /** + * @throws Exception + */ + public function findByFilters(array $filters, array $orders=null) : array + { + $conn = $this -> getEntityManager() -> getConnection(); + + $sql = "SELECT t.id, t.name, t.description, t.due_date as dueDate, + t.author_id as author, t.is_completed as isCompleted + from task as t + inner join project as p + on t.project_id = p.id"; + + if (count($filters) !== 0) { + $sql .= " where " . implode(" and ", $filters); + } + + if ($orders !== null) { + $sql .= " order by t.due_date " . $orders["dueDate"] . ", t.name " . $orders["name"]; + } + + $sql .= ";"; + + $stmt = $conn -> prepare($sql); + return $stmt ->executeQuery() -> fetchAllAssociative(); + } + } \ No newline at end of file diff --git a/src/Type/TaskFilterType.php b/src/Type/TaskFilterType.php index 885d59a..d18c69f 100644 --- a/src/Type/TaskFilterType.php +++ b/src/Type/TaskFilterType.php @@ -2,28 +2,85 @@ namespace App\Type; +use App\Entity\Project; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; class TaskFilterType extends AbstractType { + public function configureOptions(OptionsResolver $resolver) + { +// parent::configureOptions($resolver); // TODO: Change the autogenerated stub + + $resolver->setDefaults([ + 'projects_list' => [], + "authors_list" => [], + "owners_list" => [] + ]); + + $resolver->setAllowedTypes('projects_list', 'array'); + $resolver->setAllowedTypes('authors_list', 'array'); + $resolver->setAllowedTypes('owners_list', 'array'); + + } public function buildForm(FormBuilderInterface $builder, array $options) { +// dd($options); $builder ->setMethod('GET') ->add('isCompleted', ChoiceType::class, [ - 'choices' => [ - 'Да' => true, - 'Нет' => false, - 'Любое' => null - ], - 'label' => 'Выполнена' - ]) + 'choices' => [ + 'Да' => true, + 'Нет' => false, + 'Любое' => null, + ], + 'label' => 'Выполнена' + ]) + ->add("filter-by-project", CheckboxType::class, [ + "label" => "Отметьте, если нужно отфильтровать по проектам", + "required" => false + ]) + ->add("project", ChoiceType::class, [ + "choices" => $options["projects_list"], + "choice_label" => function(?Project $entity) { + return $entity ? $entity -> getProjectKey() : ""; + }, + ]) + ->add("filter-by-author", CheckboxType::class, [ + "label" => "Отметьте, если нужно отфильтровать по авторам задач", + "required" => false + ]) + ->add("author", ChoiceType::class, [ + "choices" => $options["authors_list"], + ]) + ->add("filter-by-owner", CheckboxType::class, [ + "label" => "Отметьте, если нужно отфильтровать по владельцам проктов", + "required" => false + ]) + ->add("project-owner", ChoiceType::class, [ + "choices" => $options["owners_list"], + ]) + ->add("sort-by-name", ChoiceType::class, [ + "choices" => [ + "В лексикографическом порядке" => true, + "В обратном порядке" => false + ], + "label" => "Отсортировать по названию" + ]) + ->add("sort-by-date", ChoiceType::class, [ + "choices" => [ + "От старых к новым" => true, + "От новых к сатрым" => false + ], + "label" => "Отсортировать по дате" + ]) ->add('submit', SubmitType::class, [ - 'label' => 'Отфильтровать' + 'label' => 'Применить введённые данные' ]); } diff --git a/src/Type/TaskType.php b/src/Type/TaskType.php index ee33313..ce78ff2 100644 --- a/src/Type/TaskType.php +++ b/src/Type/TaskType.php @@ -15,11 +15,6 @@ class TaskType extends AbstractType { - private function getProjectName($item, $key) - { - $item = $item -> getName(); - } - public function configureOptions(OptionsResolver $resolver) { // parent::configureOptions($resolver); // TODO: Change the autogenerated stub diff --git a/templates/projects/create.html.twig b/templates/projects/create.html.twig index 012ece3..bdc308d 100644 --- a/templates/projects/create.html.twig +++ b/templates/projects/create.html.twig @@ -5,7 +5,10 @@
- {{ form(form, {"action": path("project_create"), "method": "POST"}) }} + {{ action == "creating" ? + form(form, {"action": path("project_create"), "method": "POST"}) : + form(form, {"action": path("project_edit", {"slug": slug}), "method": "POST"}) + }}
diff --git a/templates/projects/list.html.twig b/templates/projects/list.html.twig index d7bc36d..e3d691c 100644 --- a/templates/projects/list.html.twig +++ b/templates/projects/list.html.twig @@ -27,7 +27,10 @@ {% if admin %} {{ project.owner }} {% endif %} - Удалить + +

Удалить

+

Edit

+ {% endfor %} diff --git a/templates/task/create.html.twig b/templates/task/create.html.twig index 4043e9a..f352061 100644 --- a/templates/task/create.html.twig +++ b/templates/task/create.html.twig @@ -7,7 +7,10 @@
- {{ form(form, {'action': path('task_create'), 'method': 'POST'}) }} + {{ action == "creating" ? + form(form, {"action": path("task_create"), "method": "POST"}) : + form(form, {"action": path("task_edit", {"id": id}), "method": "POST"}) + }}
diff --git a/templates/task/list.html.twig b/templates/task/list.html.twig index c3d896f..a96f758 100644 --- a/templates/task/list.html.twig +++ b/templates/task/list.html.twig @@ -1,7 +1,7 @@ {% extends 'base.html.twig' %} {% block title %} - "Tasks" + Tasks {% endblock title %} {% block body %} @@ -33,9 +33,10 @@ {{ task.author }} {{ task.isCompleted ? 'Выполнена' : 'Не выполнена' }} - {% if (is_granted("complete", task)) %} - Сделано + {% if (not task.isCompleted) %} +

Сделано

{% endif %} +

Edit

{% endfor %} From 1a11d912e00779821c315abbe9bd7824fd204356 Mon Sep 17 00:00:00 2001 From: ArtyomDev Date: Sat, 28 May 2022 04:10:27 +0500 Subject: [PATCH 3/4] =?UTF-8?q?=D0=9F=D0=BE=D1=81=D1=82=D0=B0=D1=80=D0=B0?= =?UTF-8?q?=D0=BB=D1=81=D1=8F=20=D1=83=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20?= =?UTF-8?q?=D0=B2=D1=81=D0=B5=20=D0=BE=D1=81=D0=BD=D0=BE=D0=B2=D0=BD=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8,=20=D1=81=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=BE=D0=BA=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B9=20=D0=BD=D0=B0=D0=BF=D0=B8=D1=88=D1=83=20?= =?UTF-8?q?=D0=B2=20=D1=82=D0=B5=D0=BB=D0=B5=D0=B3=D1=80=D0=B0=D0=BC=D0=BC?= =?UTF-8?q?=D0=B5,=20=D1=87=D1=82=D0=BE=D0=B1=D1=8B=20=D0=B1=D1=8B=D0=BB?= =?UTF-8?q?=D0=BE=20=D1=83=D0=B4=D0=BE=D0=B1=D0=BD=D0=B5=D0=B5=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D1=8F=D1=82=D1=8C=20:)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Controller/ProjectController.php | 31 ++++++----- src/Controller/TaskController.php | 75 ++++++++------------------ src/Entity/User.php | 2 +- src/Repository/ProjectRepository.php | 28 +++++----- src/Repository/TaskRepository.php | 81 +++++++++++++++------------- src/Repository/UserRepository.php | 28 ++++++++++ src/Type/TaskFilterType.php | 42 +++++++-------- src/Type/TaskType.php | 10 ++-- templates/projects/create.html.twig | 2 +- templates/projects/list.html.twig | 4 +- templates/task/create.html.twig | 2 +- 11 files changed, 158 insertions(+), 147 deletions(-) diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index b3d1fa6..5d3b02b 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -21,13 +21,13 @@ class ProjectController extends AbstractController public function list(Request $request): Response { - $user = $this -> getUser(); - $project_repository = $this -> getDoctrine() -> getManager() -> getRepository(Project::class); + $user = $this->getUser(); + $projectRepository = $this->getDoctrine() -> getManager() -> getRepository(Project::class); if (in_array("ROLE_ADMIN", $user -> getRoles())) { - $projects = $project_repository -> findAll(); + $projects = $projectRepository -> findAll(); } else { - $projects = $project_repository -> findBy(['owner' => $user]); + $projects = $projectRepository -> findBy(['owner' => $user]); } return $this -> render("projects/list.html.twig", [ "projects" => $projects, @@ -58,19 +58,19 @@ public function create(Request $request): Response return $this -> render("projects/create.html.twig", [ "form" => $form -> createView(), - "action" => "creating" + "action" => "create" ]); } /** - * @Route("/projects/{slug}", name="project_info") + * @Route("/projects/{slug}", name="project_show") * @return Response */ - public function info($slug, Request $request): Response + public function show($slug, Request $request): Response { - $project = $this -> getDoctrine() -> getManager() -> find(Project::class, $slug); - - $tasks = $this -> getDoctrine() -> getRepository(Task::class) -> findBy(["project" => $project]); + $project = $this->getDoctrine()->getRepository(Project::class)->findOneBy(["projectKey" => $slug]); +// dd($project); + $tasks = $project -> getTasks(); return $this->render('task/project_tasks.html.twig', [ 'tasks' => $tasks, @@ -86,7 +86,7 @@ public function info($slug, Request $request): Response */ public function edit($slug, Request $request): Response { - $project = $this->getDoctrine()->getRepository(Project::class) -> findBy(["projectKey" => $slug])[0]; + $project = $this->getDoctrine()->getRepository(Project::class) -> findOneBy(["projectKey" => $slug]); $form = $this->createForm(ProjectType::class, $project); @@ -101,7 +101,7 @@ public function edit($slug, Request $request): Response return $this->render("projects/create.html.twig", [ "form" => $form->createView(), - "action" => "editing", + "action" => "edit", "slug" => $slug ]); } @@ -115,7 +115,12 @@ public function edit($slug, Request $request): Response public function delete($slug, Request $request): Response { $em = $this->getDoctrine()->getManager(); - $project = $this->getDoctrine()->getRepository(Project::class) -> findBy(["projectKey" => $slug])[0]; + $project = $this->getDoctrine()->getRepository( + Project::class) -> findOneBy(["projectKey" => $slug]); + + if ($project === null) { + throw $this->createNotFoundException(sprintf("Task with key %key not found", $slug)); + } $em -> remove($project); $em -> flush(); diff --git a/src/Controller/TaskController.php b/src/Controller/TaskController.php index 50adcb6..b1c77ac 100644 --- a/src/Controller/TaskController.php +++ b/src/Controller/TaskController.php @@ -4,6 +4,7 @@ use App\Entity\Project; use App\Entity\Task; +use App\Entity\User; use App\Type\TaskFilterType; use App\Type\TaskType; use Doctrine\Common\Collections\Criteria; @@ -28,12 +29,10 @@ public function create(Request $request): Response { $task = new Task(); $user = $this -> getUser(); - $project_repository = $this -> getDoctrine() -> getRepository(Project::class); + $projectRepository = $this -> getDoctrine() -> getRepository(Project::class); $form = $this->createForm(TaskType::class, $task, [ - "projects_list" => in_array("ROLE_ADMIN", $user -> getRoles()) ? - $project_repository -> findAll() : - $project_repository -> findBy(["owner" => $user]) + "projectsList" => $projectRepository -> findByUserRole($user -> getId()) ]); $form->handleRequest($request); @@ -49,7 +48,7 @@ public function create(Request $request): Response return $this->render("task/create.html.twig", [ 'form' => $form->createView(), - "action" => "creating", + "action" => "create", ]); } @@ -60,59 +59,29 @@ public function create(Request $request): Response public function list(Request $request): Response { $user = $this -> getUser(); - $user_is_admin = in_array("ROLE_ADMIN", $user -> getRoles()); + $userIsAdmin = $this -> isGranted("ROLE_ADMIN"); - $project_repository = $this -> getDoctrine() -> getRepository(Project::class); - $task_repository = $this->getDoctrine()->getRepository(Task::class); - - $projects_list = $user_is_admin ? - $project_repository -> findAll() : - $project_repository -> findBy(["owner" => $user]); + $projectRepository = $this -> getDoctrine() -> getRepository(Project::class); + $userRepository = $this->getDoctrine()->getRepository(User::class); + $projectsList = $projectRepository -> findByUserRole($user -> getId()); + $projectsIdList = array_map(function ($item) { return $item->getId(); }, $projectsList); $taskFilterForm = $this->createForm(TaskFilterType::class, options: [ - "projects_list" => $projects_list, - "authors_list" => $user_is_admin ? - $task_repository ->getTasksAuthors() : - $task_repository -> getTasksAuthorsByProjectOwnerId($user -> getId()), - "owners_list" => $user_is_admin ? - $project_repository -> getProjectOwners() : - Array($user -> getUsername() => $user -> getId()), + "projectsList" => $projectsList, + "authorsList" => $userRepository->findByTaskAuthorshipInProjectList($projectsIdList), + "ownersList" => $userRepository->findByProjectOwnershipInProjectList($projectsIdList), ]); $taskFilterForm->handleRequest($request); + $taskRepository = $this->getDoctrine()->getRepository(Task::class); if ($taskFilterForm->isSubmitted() && $taskFilterForm->isValid()) { - - $data = $taskFilterForm->getData(); - - $filters = Array(); -// dd($data); - if ($data["isCompleted"] !== null) { - $filters[] = "t.is_completed=" . (int)$data["isCompleted"]; - } - - if ($data["filter-by-project"]) { - $filters[] = "t.project_id=" . $data["project"] -> getId(); - } - - if ($data["filter-by-author"]) { - $filters[] = "t.author_id=" . $data["author"]; - } - - if ($data["filter-by-owner"]) { - $filters[] = "p.owner_id=" . $data["project-owner"]; - } - - $tasks = $task_repository -> findByFilters($filters, - orders: [ - "dueDate" => $data["sort-by-date"] ? "ASC" : "DESC", - "name" => $data["sort-by-name"] ? "ASC" : "DESC" - ]); - + $tasks = $taskRepository -> findByFilterData($taskFilterForm->getData(), + !($this->isGranted("ROLE_ADMIN")) ? $this->getUser()->getId() : null); } else { - $tasks = $user_is_admin ? - $task_repository -> findBy([], ["dueDate" => "DESC"]) : - $task_repository -> getTasksByProjectOwnerId($user -> getId()); + $tasks = $userIsAdmin ? + $taskRepository -> findBy([], ["dueDate" => "DESC"]) : + $taskRepository -> findByProjectOwnerId($user -> getId()); } return $this->render('task/list.html.twig', [ @@ -156,12 +125,10 @@ public function edit($id, Request $request): Response $task = $this->getDoctrine()->getManager()->find(Task::class, $id); $user = $this -> getUser(); - $project_repository = $this -> getDoctrine() -> getRepository(Project::class); + $projectRepository = $this -> getDoctrine() -> getRepository(Project::class); $form = $this->createForm(TaskType::class, data: $task, options: [ - "projects_list" => in_array("ROLE_ADMIN", $user -> getRoles()) ? - $project_repository -> findAll() : - $project_repository -> findBy(["owner" => $user]) + "projectsList" => $projectRepository -> findByUserRole($user -> getId()), ]); $form -> handleRequest($request); @@ -175,7 +142,7 @@ public function edit($id, Request $request): Response return $this -> render('task/create.html.twig', [ "form" => $form -> createView(), - "action" => "editing", + "action" => "edit", "id" => $id ]); } diff --git a/src/Entity/User.php b/src/Entity/User.php index 5680de4..b3c0923 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -83,7 +83,7 @@ public function getRoles(): array * @param string $role * @return bool */ - public function checkRole(string $role): bool + public function hasRole(string $role): bool { return in_array($role, $this -> roles); } diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php index 84bbb3b..dfc8c71 100644 --- a/src/Repository/ProjectRepository.php +++ b/src/Repository/ProjectRepository.php @@ -3,8 +3,10 @@ namespace App\Repository; use App\Entity\Project; +use App\Entity\User; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Result; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ManagerRegistry; @@ -15,19 +17,21 @@ public function __construct(ManagerRegistry $registry) parent::__construct($registry, Project::class); } - /** - * @throws Exception - */ - public function getProjectOwners() : array + public function findByUserRole($id) : array { - $conn = $this->getEntityManager()->getConnection(); + if ($this->getEntityManager()->find(User::class, $id)->hasRole("ROLE_ADMIN")) { + return $this->findAll(); + } else { + return $this->findBy(["owner" => $id]); + } + } - $sql = " - SELECT DISTINCT u.email, u.id - FROM project as p INNER JOIN user as u - ON p.owner_id = u.id; - "; - $stmt = $conn->prepare($sql); - return $stmt ->executeQuery() -> fetchAllAssociative(); + public function getProjectOwners() : array + { + return $this->createQueryBuilder("p") + ->select(["distinct u.email", "u.id"]) + ->innerJoin("p.owner", "u") + ->getQuery() + ->getResult(); } } \ No newline at end of file diff --git a/src/Repository/TaskRepository.php b/src/Repository/TaskRepository.php index 3986b57..0bcb46b 100644 --- a/src/Repository/TaskRepository.php +++ b/src/Repository/TaskRepository.php @@ -6,6 +6,7 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Query; use Doctrine\Persistence\ManagerRegistry; class TaskRepository extends ServiceEntityRepository @@ -18,7 +19,7 @@ public function __construct(ManagerRegistry $registry) /** * @throws Exception */ - public function getTasksByProjectOwnerId(int $id) : array + public function findByProjectOwnerId(int $id) : array { $conn = $this -> getEntityManager() -> getConnection(); @@ -32,64 +33,70 @@ public function getTasksByProjectOwnerId(int $id) : array return $stmt ->executeQuery(["id" => $id]) -> fetchAllAssociative(); } - /** - * @throws Exception - */ public function getTasksAuthors() : array { - $conn = $this -> getEntityManager() -> getConnection(); - - $sql = " - SELECT DISTINCT u.email, u.id - FROM task as t inner join user as u - on t.author_id = u.id; - "; - $stmt = $conn -> prepare($sql); - return $stmt ->executeQuery() -> fetchAllAssociative(); + return $this->createQueryBuilder("t") + ->select(["distinct u.email", "u.id"]) + ->innerJoin("t.author", "u") + ->getQuery() + ->getArrayResult(); } /** * @throws Exception */ - public function getTasksAuthorsByProjectOwnerId(int $id) : array + public function findTasksAuthorsByProjectOwnerId(int $id) : array { - $conn = $this -> getEntityManager() -> getConnection(); - - $sql = " - SELECT DISTINCT u.email, u.id - FROM task as t INNER JOIN user as u ON t.author_id = u.id - WHERE project_id in (SELECT id FROM project WHERE owner_id = :id); - "; - $stmt = $conn -> prepare($sql); - return $stmt ->executeQuery(["id" => $id]) -> fetchAllKeyValue(); + return $this->createQueryBuilder("t") + ->select(["distinct u.email", "u.id"]) + ->innerJoin("t.author", "u") + ->getQuery() + ->getArrayResult(); + +// $conn = $this -> getEntityManager() -> getConnection(); +// +// $sql = " +// SELECT DISTINCT u.email, u.id +// FROM task as t INNER JOIN user as u ON t.author_id = u.id +// WHERE project_id in (SELECT id FROM project WHERE owner_id = :id); +// "; +// $stmt = $conn -> prepare($sql); +// return $stmt ->executeQuery(["id" => $id]) -> fetchAllKeyValue(); } /** * @throws Exception */ - public function findByFilters(array $filters, array $orders=null) : array + public function findByFilterData(array $data, ?int $user_id) : array { - $conn = $this -> getEntityManager() -> getConnection(); +// dd($data); + $qb = $this->createQueryBuilder("t"); + $qb->select("t"); + $qb->join("App\Entity\User", "u", "WITH", "t.author = u.id"); + $qb->join("App\Entity\Project", "p", "WITH", "t.project = p.id"); + + if ($user_id !== null) { + $qb->add("where", $qb->expr()->eq("p.owner", $user_id)); + } - $sql = "SELECT t.id, t.name, t.description, t.due_date as dueDate, - t.author_id as author, t.is_completed as isCompleted - from task as t - inner join project as p - on t.project_id = p.id"; + if ($data["project"] !== null) { + $qb->add("where", $qb->expr()->eq("t.project", $data["project"]->getId())); + } - if (count($filters) !== 0) { - $sql .= " where " . implode(" and ", $filters); + if ($data["author"] !== null) { + $qb->add("where", $qb->expr()->eq("u.author", $data["author"]->getId())); } - if ($orders !== null) { - $sql .= " order by t.due_date " . $orders["dueDate"] . ", t.name " . $orders["name"]; + if ($data["project-owner"] !== null) { + $qb->add("where", $qb->expr()->eq("p.owner", $data["owner"]->getId())); } - $sql .= ";"; +// $qb->orderBy() + $qb->orderBy("t.dueDate", $data["sort-by-date"] ? "ASC" : "DESC"); + $qb->orderBy("t.name", $data["sort-by-name"] ? "ASC" : "DESC"); - $stmt = $conn -> prepare($sql); - return $stmt ->executeQuery() -> fetchAllAssociative(); + return $qb->getQuery()->getResult(); } diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index aabb343..269475a 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -2,8 +2,10 @@ namespace App\Repository; +use App\Entity\Task; use App\Entity\User; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\DBAL\Result; use Doctrine\ORM\OptimisticLockException; use Doctrine\ORM\ORMException; use Doctrine\Persistence\ManagerRegistry; @@ -90,4 +92,30 @@ public function findOneBySomeField($value): ?User ; } */ + + public function findByTaskAuthorshipInProjectList(?array $projectsIdList): array + { + $qb = $this->createQueryBuilder("u"); + return $qb + ->select("u") + ->distinct() + ->innerJoin("App\Entity\Task", "t", "WITH", "u.id = t.author") + ->where($qb->expr()->in("t.project", $projectsIdList)) + ->getQuery() + ->getResult(); + } + + public function findByProjectOwnershipInProjectList(?array $projectsIdtList): array + { + $qb = $this->createQueryBuilder("u"); + + return $qb + ->select("u") + ->distinct() + ->innerJoin("App\Entity\Project", "p", "WITH", "u.id = p.owner") + ->where($qb->expr()->in("p.id", $projectsIdtList)) + ->getQuery() + ->getResult(); + } + } diff --git a/src/Type/TaskFilterType.php b/src/Type/TaskFilterType.php index d18c69f..1062708 100644 --- a/src/Type/TaskFilterType.php +++ b/src/Type/TaskFilterType.php @@ -17,14 +17,14 @@ public function configureOptions(OptionsResolver $resolver) // parent::configureOptions($resolver); // TODO: Change the autogenerated stub $resolver->setDefaults([ - 'projects_list' => [], - "authors_list" => [], - "owners_list" => [] + 'projectsList' => [], + "authorsList" => [], + "ownersList" => [] ]); - $resolver->setAllowedTypes('projects_list', 'array'); - $resolver->setAllowedTypes('authors_list', 'array'); - $resolver->setAllowedTypes('owners_list', 'array'); + $resolver->setAllowedTypes('projectsList', 'array'); + $resolver->setAllowedTypes('authorsList', 'array'); + $resolver->setAllowedTypes('ownersList', 'array'); } @@ -41,29 +41,29 @@ public function buildForm(FormBuilderInterface $builder, array $options) ], 'label' => 'Выполнена' ]) - ->add("filter-by-project", CheckboxType::class, [ - "label" => "Отметьте, если нужно отфильтровать по проектам", - "required" => false - ]) ->add("project", ChoiceType::class, [ - "choices" => $options["projects_list"], + "placeholder" => "Любой", + "required" => false, + "choices" => $options["projectsList"], "choice_label" => function(?Project $entity) { return $entity ? $entity -> getProjectKey() : ""; }, ]) - ->add("filter-by-author", CheckboxType::class, [ - "label" => "Отметьте, если нужно отфильтровать по авторам задач", - "required" => false - ]) ->add("author", ChoiceType::class, [ - "choices" => $options["authors_list"], - ]) - ->add("filter-by-owner", CheckboxType::class, [ - "label" => "Отметьте, если нужно отфильтровать по владельцам проктов", - "required" => false + "placeholder" => "Любой", + "required" => false, + "choices" => $options["authorsList"], + "choice_label" => function ($choice) { + return $choice; + } ]) ->add("project-owner", ChoiceType::class, [ - "choices" => $options["owners_list"], + "placeholder" => "Любой", + "required" => false, + "choices" => $options["ownersList"], + "choice_label" => function ($choice) { + return $choice; + } ]) ->add("sort-by-name", ChoiceType::class, [ "choices" => [ diff --git a/src/Type/TaskType.php b/src/Type/TaskType.php index ce78ff2..5efe14e 100644 --- a/src/Type/TaskType.php +++ b/src/Type/TaskType.php @@ -20,10 +20,10 @@ public function configureOptions(OptionsResolver $resolver) // parent::configureOptions($resolver); // TODO: Change the autogenerated stub $resolver->setDefaults([ - 'projects_list' => [], + 'projectsList' => [], ]); - $resolver->setAllowedTypes('projects_list', 'array'); + $resolver->setAllowedTypes('projectsList', 'array'); } @@ -36,9 +36,9 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'years' => range(2022,2023) ]) ->add('project', ChoiceType::class, [ - "choices" => $options["projects_list"], - 'choice_label' => function(?Project $entity) { - return $entity ? $entity -> getProjectKey() : ""; + "choices" => $options["projectsList"], + 'choice_label' => function ($choice) { + return $choice; }, "label" => "Выберите проект", ]) diff --git a/templates/projects/create.html.twig b/templates/projects/create.html.twig index bdc308d..6852b7b 100644 --- a/templates/projects/create.html.twig +++ b/templates/projects/create.html.twig @@ -5,7 +5,7 @@
- {{ action == "creating" ? + {{ action == "create" ? form(form, {"action": path("project_create"), "method": "POST"}) : form(form, {"action": path("project_edit", {"slug": slug}), "method": "POST"}) }} diff --git a/templates/projects/list.html.twig b/templates/projects/list.html.twig index e3d691c..71a5dd7 100644 --- a/templates/projects/list.html.twig +++ b/templates/projects/list.html.twig @@ -1,7 +1,7 @@ {% extends 'base.html.twig' %} {% block body %} -{% set admin = app.user.checkRole("ROLE_ADMIN") %} +{% set admin = app.user.hasRole("ROLE_ADMIN") %}
@@ -21,7 +21,7 @@ {% for project in projects %} - + {{ project.projectKey }} {{ project.name }} {% if admin %} diff --git a/templates/task/create.html.twig b/templates/task/create.html.twig index f352061..d5541d2 100644 --- a/templates/task/create.html.twig +++ b/templates/task/create.html.twig @@ -7,7 +7,7 @@
- {{ action == "creating" ? + {{ action == "create" ? form(form, {"action": path("task_create"), "method": "POST"}) : form(form, {"action": path("task_edit", {"id": id}), "method": "POST"}) }} From 8be23c2890e3e63f4df6ed49c66cf817eb3bc619 Mon Sep 17 00:00:00 2001 From: ArtyomDev Date: Fri, 3 Jun 2022 18:48:35 +0500 Subject: [PATCH 4/4] =?UTF-8?q?=D0=94=D0=BE=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB?= =?UTF-8?q?=20=D0=B2=D0=BE=D1=83=D1=82=D0=B5=D1=80=D1=8B=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=B8=20=D1=82=D0=B0=D1=81=D0=BA=D0=BE=D0=B2,=20=D0=B2=D0=BD?= =?UTF-8?q?=D1=91=D1=81=20=D0=B8=D1=85=20=D0=B2=20=D1=88=D0=B0=D0=B1=D0=BB?= =?UTF-8?q?=D0=BE=D0=BD=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Controller/ProjectController.php | 24 +++++++++++---- src/Controller/TaskController.php | 10 ++++++- src/Repository/ProjectRepository.php | 9 ------ src/Repository/TaskRepository.php | 25 ---------------- src/Type/TaskType.php | 2 ++ src/Voter/ProjectVoter.php | 45 ++++++++++++++++++++++++++++ src/Voter/TaskVoter.php | 10 ++++--- templates/projects/list.html.twig | 19 +++++++++--- templates/task/list.html.twig | 8 +++-- 9 files changed, 100 insertions(+), 52 deletions(-) create mode 100644 src/Voter/ProjectVoter.php diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index 5d3b02b..e577f58 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -24,11 +24,8 @@ public function list(Request $request): Response $user = $this->getUser(); $projectRepository = $this->getDoctrine() -> getManager() -> getRepository(Project::class); - if (in_array("ROLE_ADMIN", $user -> getRoles())) { - $projects = $projectRepository -> findAll(); - } else { - $projects = $projectRepository -> findBy(['owner' => $user]); - } + $projects = $projectRepository->findByUserRole($user->getId()); + return $this -> render("projects/list.html.twig", [ "projects" => $projects, ]); @@ -70,6 +67,13 @@ public function show($slug, Request $request): Response { $project = $this->getDoctrine()->getRepository(Project::class)->findOneBy(["projectKey" => $slug]); // dd($project); + + $this->denyAccessUnlessGranted('view', $project); + + if ($project === null) { + throw $this->createNotFoundException(sprintf("Project with key %s not found", $slug)); + } + $tasks = $project -> getTasks(); return $this->render('task/project_tasks.html.twig', [ @@ -88,6 +92,12 @@ public function edit($slug, Request $request): Response { $project = $this->getDoctrine()->getRepository(Project::class) -> findOneBy(["projectKey" => $slug]); + $this->denyAccessUnlessGranted('edit', $project); + + if ($project === null) { + throw $this->createNotFoundException(sprintf("Project with key %s not found", $slug)); + } + $form = $this->createForm(ProjectType::class, $project); $form -> handleRequest($request); @@ -118,8 +128,10 @@ public function delete($slug, Request $request): Response $project = $this->getDoctrine()->getRepository( Project::class) -> findOneBy(["projectKey" => $slug]); + $this->denyAccessUnlessGranted('delete', $project); + if ($project === null) { - throw $this->createNotFoundException(sprintf("Task with key %key not found", $slug)); + throw $this->createNotFoundException(sprintf("Project with key %s not found", $slug)); } $em -> remove($project); diff --git a/src/Controller/TaskController.php b/src/Controller/TaskController.php index b1c77ac..a9230e6 100644 --- a/src/Controller/TaskController.php +++ b/src/Controller/TaskController.php @@ -32,7 +32,8 @@ public function create(Request $request): Response $projectRepository = $this -> getDoctrine() -> getRepository(Project::class); $form = $this->createForm(TaskType::class, $task, [ - "projectsList" => $projectRepository -> findByUserRole($user -> getId()) + "projectsList" => $projectRepository -> findByUserRole($user -> getId()), +// 'userId' => 4, ]); $form->handleRequest($request); @@ -82,6 +83,7 @@ public function list(Request $request): Response $tasks = $userIsAdmin ? $taskRepository -> findBy([], ["dueDate" => "DESC"]) : $taskRepository -> findByProjectOwnerId($user -> getId()); +// dd($tasks); } return $this->render('task/list.html.twig', [ @@ -124,6 +126,12 @@ public function edit($id, Request $request): Response { $task = $this->getDoctrine()->getManager()->find(Task::class, $id); + $this->denyAccessUnlessGranted('edit', $task); + + if ($task === null) { + throw $this->createNotFoundException(sprintf("Task with id %s not found", $id)); + } + $user = $this -> getUser(); $projectRepository = $this -> getDoctrine() -> getRepository(Project::class); diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php index dfc8c71..120f46e 100644 --- a/src/Repository/ProjectRepository.php +++ b/src/Repository/ProjectRepository.php @@ -25,13 +25,4 @@ public function findByUserRole($id) : array return $this->findBy(["owner" => $id]); } } - - public function getProjectOwners() : array - { - return $this->createQueryBuilder("p") - ->select(["distinct u.email", "u.id"]) - ->innerJoin("p.owner", "u") - ->getQuery() - ->getResult(); - } } \ No newline at end of file diff --git a/src/Repository/TaskRepository.php b/src/Repository/TaskRepository.php index 0bcb46b..a6dcb04 100644 --- a/src/Repository/TaskRepository.php +++ b/src/Repository/TaskRepository.php @@ -42,35 +42,11 @@ public function getTasksAuthors() : array ->getArrayResult(); } - /** - * @throws Exception - */ - public function findTasksAuthorsByProjectOwnerId(int $id) : array - { - return $this->createQueryBuilder("t") - ->select(["distinct u.email", "u.id"]) - ->innerJoin("t.author", "u") - ->getQuery() - ->getArrayResult(); - -// $conn = $this -> getEntityManager() -> getConnection(); -// -// $sql = " -// SELECT DISTINCT u.email, u.id -// FROM task as t INNER JOIN user as u ON t.author_id = u.id -// WHERE project_id in (SELECT id FROM project WHERE owner_id = :id); -// "; -// $stmt = $conn -> prepare($sql); -// return $stmt ->executeQuery(["id" => $id]) -> fetchAllKeyValue(); - } - - /** * @throws Exception */ public function findByFilterData(array $data, ?int $user_id) : array { -// dd($data); $qb = $this->createQueryBuilder("t"); $qb->select("t"); $qb->join("App\Entity\User", "u", "WITH", "t.author = u.id"); @@ -92,7 +68,6 @@ public function findByFilterData(array $data, ?int $user_id) : array $qb->add("where", $qb->expr()->eq("p.owner", $data["owner"]->getId())); } -// $qb->orderBy() $qb->orderBy("t.dueDate", $data["sort-by-date"] ? "ASC" : "DESC"); $qb->orderBy("t.name", $data["sort-by-name"] ? "ASC" : "DESC"); diff --git a/src/Type/TaskType.php b/src/Type/TaskType.php index 5efe14e..7975d07 100644 --- a/src/Type/TaskType.php +++ b/src/Type/TaskType.php @@ -3,6 +3,8 @@ namespace App\Type; use App\Entity\Project; +use App\Entity\User; +use Doctrine\ORM\EntityRepository; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; diff --git a/src/Voter/ProjectVoter.php b/src/Voter/ProjectVoter.php new file mode 100644 index 0000000..3ec60ab --- /dev/null +++ b/src/Voter/ProjectVoter.php @@ -0,0 +1,45 @@ + getUser(); + + if (!$user instanceof User) { + return false; + } + + $isOwner = $subject->getOwner() === $user; + $isAdmin = $user->hasRole("ROLE_ADMIN"); + + return $isAdmin || $isOwner; + } + +} \ No newline at end of file diff --git a/src/Voter/TaskVoter.php b/src/Voter/TaskVoter.php index 1f84b73..6e1d5da 100644 --- a/src/Voter/TaskVoter.php +++ b/src/Voter/TaskVoter.php @@ -13,12 +13,13 @@ class TaskVoter extends Voter const COMPLETE = 'complete'; const DELETE = 'delete'; + const EDIT = "edit"; protected function supports($attribute, $subject) { - if (!in_array($attribute, [self::COMPLETE, self::DELETE])) { + if (!in_array($attribute, [self::COMPLETE, self::DELETE, self::EDIT])) { return false; } @@ -44,10 +45,11 @@ protected function voteOnAttribute($attribute, $subject, TokenInterface $token) return false; } - $isAdmin = in_array('ROLE_ADMIN', $user->getRoles()); - $isAuthor = ($subject->getAuthor() === $user); + $isAdmin = $user->hasRole("ROLE_ADMIN"); +// $isAuthor = ($subject->getAuthor() === $user); + $isProjectOwner = ($subject->getProject()->getOwner() === $user); - return $isAdmin || $isAuthor; + return $isAdmin || $isProjectOwner; } diff --git a/templates/projects/list.html.twig b/templates/projects/list.html.twig index 71a5dd7..f1b88e8 100644 --- a/templates/projects/list.html.twig +++ b/templates/projects/list.html.twig @@ -21,15 +21,26 @@ {% for project in projects %} - - {{ project.projectKey }} + + {% if (is_granted("view", project)) %} + + {{ project.projectKey }} + {% else %} + {{ project.projectKey }} + {% endif %} + {{ project.name }} {% if admin %} {{ project.owner }} {% endif %} -

Удалить

-

Edit

+ {% if (is_granted("delete", project)) %} + + Удалить
+ {% endif %} + {% if (is_granted("edit", project)) %} + Edit + {% endif %} {% endfor %} diff --git a/templates/task/list.html.twig b/templates/task/list.html.twig index a96f758..29c28b9 100644 --- a/templates/task/list.html.twig +++ b/templates/task/list.html.twig @@ -33,10 +33,12 @@ {{ task.author }} {{ task.isCompleted ? 'Выполнена' : 'Не выполнена' }} - {% if (not task.isCompleted) %} -

Сделано

+ {% if (is_granted("complete", task)) %} + Сделано
+ {% endif %} + {% if (is_granted("edit", task)) %} + Edit {% endif %} -

Edit

{% endfor %}