From 6ffc83e5807f01d75f783d381ff9f529ec23bf8d Mon Sep 17 00:00:00 2001
From: George <106768258+Temburdakov927@users.noreply.github.com>
Date: Fri, 3 Jun 2022 22:01:09 +0500
Subject: [PATCH] Complete all tasks
Artem Burdakov
---
config/packages/security.yaml | 1 +
src/Controller/ProjectController.php | 98 +++++++++++++++++++
src/Controller/TaskController.php | 36 ++++---
src/Entity/Project.php | 92 +++++++++++++++++
src/Entity/Task.php | 27 ++++-
src/Repository/ProjectRepository.php | 94 ++++++++++++++++++
src/Repository/TaskRepository.php | 56 +++++++++++
src/Type/ProjectType.php | 18 ++++
src/Type/TaskFilterType.php | 31 ++++++
src/Type/TaskType.php | 39 ++++++++
src/Voter/ProjectVoter.php | 35 +++++++
src/Voter/TaskVoter.php | 1 +
.../project/create_project_form.html.twig | 4 +
templates/project/index.html.twig | 44 +++++++++
templates/project/project.html.twig | 51 ++++++++++
templates/project/project_edit_form.html.twig | 4 +
templates/security/login.html.twig | 2 +-
17 files changed, 610 insertions(+), 23 deletions(-)
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 src/Voter/ProjectVoter.php
create mode 100644 templates/project/create_project_form.html.twig
create mode 100644 templates/project/index.html.twig
create mode 100644 templates/project/project.html.twig
create mode 100644 templates/project/project_edit_form.html.twig
diff --git a/config/packages/security.yaml b/config/packages/security.yaml
index 7b38924..1e7714e 100644
--- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -35,6 +35,7 @@ security:
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/task/*, roles: ROLE_USER }
+ - { path: ^/project/*, roles: ROLE_USER }
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php
new file mode 100644
index 0000000..59ee0c7
--- /dev/null
+++ b/src/Controller/ProjectController.php
@@ -0,0 +1,98 @@
+getDoctrine()
+ ->getManager()
+ ->getRepository(Project::class)
+ ->getAvailableProject($this->getUser()->getId(),$this->isGranted('ROLE_ADMIN'));
+
+ return $this->render('project/index.html.twig', [
+ 'projects' => $projects
+ ]);
+ }
+
+ /**
+ * @Route("/project/create/form", name="project_create_form")
+ */
+ public function createProjectForm(Request $request): Response
+ {
+ $project = new Project();
+ $form = $this->createForm(ProjectType::class, $project);
+ $form->handleRequest($request);
+
+ if($form->isSubmitted() && $form->isValid())
+ {
+ $project->setAuthor($this->getUser());
+ $project->setToken();
+ $this->getDoctrine()->getManager()->persist($project);
+ $this->getDoctrine()->getManager()->flush();
+ return $this->redirectToRoute("project_list");
+ }
+
+ return $this->render('project/create_project_form.html.twig', [
+ 'form' => $form->createView()
+ ]);
+ }
+
+ /**
+ * @Route("/project/{id}", name="project_byId")
+ */
+ public function projectById($id): Response
+ {
+ $project = $this->getDoctrine()->getManager()->find(Project::class, $id);
+ if($project === null)
+ throw $this->createNotFoundException("Project with %s not found", $id);
+
+ $this->denyAccessUnlessGranted('project_view', $project);
+
+
+ $tasks = $this->getDoctrine()->getRepository(Task::class)
+ ->findBy(['project' => $project]);
+ return $this->render('project/project.html.twig', [
+ 'project' => $project,
+ 'tasks' => $tasks
+ ]);
+ }
+
+ /**
+ * @Route("/project/{id}/edit", name="project_byId_edit")
+ */
+ public function projectEdit($id, Request $request): Response
+ {
+ $project = $this->getDoctrine()->getManager()->find(Project::class, $id);
+ if($project === null)
+ throw $this->createNotFoundException("Project with %s not found", $id);
+ $this->denyAccessUnlessGranted('project_edit', $project);
+
+ $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('project/project_edit_form.html.twig', [
+ 'form' => $form->createView()
+ ]);
+ }
+}
diff --git a/src/Controller/TaskController.php b/src/Controller/TaskController.php
index 0d9f9ad..2fc56d2 100644
--- a/src/Controller/TaskController.php
+++ b/src/Controller/TaskController.php
@@ -22,7 +22,8 @@ class TaskController extends AbstractController
public function create(Request $request): Response
{
$task = new Task();
- $form = $this->createForm(TaskType::class, $task);
+ $option = ['userId' => $this->getUser()->getId(), 'hasAdmin' => $this->isGranted('ROLE_ADMIN')];
+ $form = $this->createForm(TaskType::class, $task, $option);
$form->handleRequest($request);
@@ -53,24 +54,14 @@ public function list(Request $request): Response
$taskFilterForm->handleRequest($request);
if ($taskFilterForm->isSubmitted() && $taskFilterForm->isValid()) {
-
$filter = $taskFilterForm->getData();
- if ($filter['isCompleted'] === null) {
- unset($filter['isCompleted']);
- }
-
- $tasks = $this->getDoctrine()->getRepository(Task::class)
- ->findBy($filter, [
- 'dueDate' => 'DESC'
- ]);
-
+ $tasks = $this->getDoctrine()
+ ->getRepository(Task::class)
+ ->getAvaiableTaskWithFilter($this->getUser()->getId(), $filter, $this->isGranted('ROLE_ADMIN'));
} else {
- /** @var $tasks */
- $tasks = $this->getDoctrine()->getManager()
+ $tasks = $this->getDoctrine()
->getRepository(Task::class)
- ->findBy([], [
- 'dueDate' => 'DESC'
- ]);
+ ->getAvaiableTask($this->getUser()->getId(), $this->isGranted('ROLE_ADMIN'));
}
@@ -91,13 +82,20 @@ public function complete($id): Response
/** @var Task $task */
$task = $this->getDoctrine()->getManager()->find(Task::class, $id);
- $this->denyAccessUnlessGranted('complete', $task);
-
if ($task === null) {
throw $this->createNotFoundException(sprintf("Task with id %s not found", $id));
}
+ $this->denyAccessUnlessGranted('complete', $task);
+
+ if($task->isCompleted())
+ {
+ $task->setIsCompleted();
+ }
+ else
+ {
+ $task->setIsCompleted(true);
+ }
- $task->setIsCompleted(true);
$this->getDoctrine()->getManager()->persist($task);
$this->getDoctrine()->getManager()->flush();
diff --git a/src/Entity/Project.php b/src/Entity/Project.php
new file mode 100644
index 0000000..7f26e7d
--- /dev/null
+++ b/src/Entity/Project.php
@@ -0,0 +1,92 @@
+id;
+ }
+
+ public function getToken(): ?string
+ {
+ return $this->token;
+ }
+
+ public function setToken(string $token = null): self
+ {
+ if ($token == null)
+ {
+ $this->token = $this->generateToken();
+ return $this;
+ }
+ $this->token = $token;
+ return $this;
+ }
+
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+ public function setName(string $name): self
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * @return User
+ */
+ public function getAuthor(): user
+ {
+ return $this->author;
+ }
+
+ /**
+ * @param User $user
+ */
+ public function setAuthor(user $author): void
+ {
+ $this->author = $author;
+ }
+
+
+
+ private function generateToken($length = 5): ?string
+ {
+ return substr(str_shuffle(str_repeat($x='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length/strlen($x)) )),1,$length);
+ }
+}
diff --git a/src/Entity/Task.php b/src/Entity/Task.php
index 66a4b57..b27db00 100644
--- a/src/Entity/Task.php
+++ b/src/Entity/Task.php
@@ -44,7 +44,7 @@ class Task
/**
* @ORM\Column(type="boolean", nullable=false, options={"default" : 0})
- * @var boolean
+ * @var bool
*/
protected $isCompleted = false;
@@ -55,6 +55,12 @@ class Task
*/
protected $author;
+ /**
+ * @ORM\ManyToOne(targetEntity="App\Entity\Project", inversedBy="project")
+ * @var Project
+ */
+ protected $project;
+
/**
* Create empty task
*/
@@ -85,6 +91,22 @@ public function getAuthor()
return $this->author;
}
+ /**
+ * @return Project|null
+ */
+ public function getProject(): ?Project
+ {
+ return $this->project;
+ }
+
+ /**
+ * @param Project $project
+ */
+ public function setProject(Project $project = null)
+ {
+ $this->project = $project;
+ }
+
/**
* @return mixed
@@ -148,7 +170,7 @@ public function setDueDate($dueDate): void
*/
public function isCompleted() : bool
{
- return (boolean) $this->isCompleted;
+ return $this->isCompleted;
}
/**
@@ -163,5 +185,4 @@ public function setIsCompleted(bool $isCompleted = false)
-
}
\ No newline at end of file
diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php
new file mode 100644
index 0000000..aff51af
--- /dev/null
+++ b/src/Repository/ProjectRepository.php
@@ -0,0 +1,94 @@
+_em->persist($entity);
+ if ($flush) {
+ $this->_em->flush();
+ }
+ }
+
+ /**
+ * @throws ORMException
+ * @throws OptimisticLockException
+ */
+ public function remove(Project $entity, bool $flush = true): void
+ {
+ $this->_em->remove($entity);
+ if ($flush) {
+ $this->_em->flush();
+ }
+ }
+
+ public function getAvailableProject(int $id, Bool $hasAdmin = false): array
+ {
+ if($hasAdmin)
+ {
+ return $this->getEntityManager()->getRepository(Project::class)->findAll();
+ }
+ else
+ {
+ return $this->getEntityManager()->getRepository(Project::class)->findBy(['author' => $id]);
+ }
+ }
+
+ public function takeAvaiableId(int $id): array
+ {
+ return [];
+ }
+
+
+ // /**
+ // * @return Project[] Returns an array of Project objects
+ // */
+ /*
+ public function findByExampleField($value)
+ {
+ return $this->createQueryBuilder('p')
+ ->andWhere('p.exampleField = :val')
+ ->setParameter('val', $value)
+ ->orderBy('p.id', 'ASC')
+ ->setMaxResults(10)
+ ->getQuery()
+ ->getResult()
+ ;
+ }
+ */
+
+ /*
+ public function findOneBySomeField($value): ?Project
+ {
+ return $this->createQueryBuilder('p')
+ ->andWhere('p.exampleField = :val')
+ ->setParameter('val', $value)
+ ->getQuery()
+ ->getOneOrNullResult()
+ ;
+ }
+ */
+}
diff --git a/src/Repository/TaskRepository.php b/src/Repository/TaskRepository.php
index 43cc1ba..e7913a4 100644
--- a/src/Repository/TaskRepository.php
+++ b/src/Repository/TaskRepository.php
@@ -2,6 +2,7 @@
namespace App\Repository;
+use App\Entity\Project;
use App\Entity\Task;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\EntityRepository;
@@ -9,10 +10,65 @@
class TaskRepository extends ServiceEntityRepository
{
+ protected $id;
+
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Task::class);
}
+ public function getAvaiableTaskWithFilter(int $id, array $filter, Bool $hasAdmin = false): array
+ {
+
+ if($filter['due_date'] or $filter['due_date'] === null)
+ {
+ $duedate = array('dueDate' => 'desc');
+ }
+ else
+ {
+ $duedate = array('dueDate' => 'asc');
+ }
+
+ foreach ($filter as $key => $value)
+ {
+ if($key === null)
+ {
+ unset($filter[$key]);
+ }
+ }
+
+ unset($filter['due_date']);
+ if($hasAdmin)
+ {
+ return $this->getEntityManager()->getRepository(Task::class)->findBy($filter, $duedate);
+ }
+ else
+ {
+ $this->id = $id;
+ $tasks = $this->getEntityManager()->getRepository(Task::class)->findBy($filter, $duedate);
+
+ return array_filter($tasks, function ($task) {
+ return $this->id === $task->getAuthor()->getId() || $this->id === $task->getProject()->getAuthor()->getId();
+ });
+ }
+ }
+
+ public function getAvaiableTask(int $id, Bool $hasAdmin = false): array
+ {
+ if($hasAdmin)
+ {
+ return $this->getEntityManager()->getRepository(Task::class)->findAll();
+ }
+ else
+ {
+ $this->id = $id;
+ $tasks = $this->getEntityManager()->getRepository(Task::class)->findAll();
+
+ return array_filter($tasks, function ($task) {
+ return $this->id === $task->getAuthor()->getId() || $this->id === $task->getProject()->getAuthor()->getId();
+ });
+ }
+ }
+
}
\ No newline at end of file
diff --git a/src/Type/ProjectType.php b/src/Type/ProjectType.php
new file mode 100644
index 0000000..4c97913
--- /dev/null
+++ b/src/Type/ProjectType.php
@@ -0,0 +1,18 @@
+add('name', TextType::class)
+ ->add('save', SubmitType::class);
+ }
+}
\ No newline at end of file
diff --git a/src/Type/TaskFilterType.php b/src/Type/TaskFilterType.php
index 885d59a..79ec19a 100644
--- a/src/Type/TaskFilterType.php
+++ b/src/Type/TaskFilterType.php
@@ -2,6 +2,10 @@
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;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
@@ -22,6 +26,33 @@ public function buildForm(FormBuilderInterface $builder, array $options)
],
'label' => 'Выполнена'
])
+ ->add("due_date", ChoiceType::class, [
+ 'choices' => [
+ 'Newest' => true,
+ 'Oldest' => false,
+ 'Any' => null
+ ],
+ ])
+ ->add('project', EntityType::class, [
+ 'class' => Project::class,
+ 'query_builder' => function (EntityRepository $er) {
+ return $er->createQueryBuilder('p')
+ ->orderBy('p.id', 'ASC');
+ },
+ 'choice_label' => 'name',
+ 'required' => false,
+ 'label' => 'Выберите проект',
+ ])
+ ->add('author', EntityType::class, [
+ 'class' => User::class,
+ 'query_builder' => function (EntityRepository $er) {
+ return $er->createQueryBuilder('p')
+ ->orderBy('p.id', 'ASC');
+ },
+ 'choice_label' => 'email',
+ 'required' => false,
+ 'label' => 'Выберите создателя'
+ ])
->add('submit', SubmitType::class, [
'label' => 'Отфильтровать'
]);
diff --git a/src/Type/TaskType.php b/src/Type/TaskType.php
index 3b58193..77aa8eb 100644
--- a/src/Type/TaskType.php
+++ b/src/Type/TaskType.php
@@ -2,24 +2,63 @@
namespace App\Type;
+use App\Entity\Project;
+use Doctrine\ORM\EntityManager;
+use Doctrine\ORM\EntityRepository;
+use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
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 $hasAdmin;
+ private $id;
+ public function configureOptions(OptionsResolver $resolver): void
+ {
+ $resolver->setDefaults([
+ 'userId' => null,
+ 'hasAdmin' => false
+ ]);
+
+ $resolver->setAllowedTypes('userId', 'int');
+ $resolver->setAllowedTypes('hasAdmin', 'Bool');
+ }
+
public function buildForm(FormBuilderInterface $builder, array $options)
{
+ $this->hasAdmin = $options['hasAdmin'];
+ $this->id = $options['userId'];
$builder
->add('name', TextType::class)
->add('description', TextareaType::class)
->add('dueDate', DateType::class, [
'years' => range(2022,2023)
])
+ ->add('project', EntityType::class, [
+ 'class' => Project::class,
+ 'query_builder' => function (EntityRepository $er)
+ {
+ if($this->hasAdmin)
+ {
+ return $er->createQueryBuilder('t')
+ ->orderBy('t.id', 'ASC');
+ }
+ else
+ {
+ return $er->createQueryBuilder('t')
+ ->where(sprintf('t.author = %d', $this->id))
+ ->orderBy('t.id', 'ASC');
+ }
+ },
+ 'choice_label' => 'name',
+ ])
->add('save', SubmitType::class)
;
+
}
}
\ No newline at end of file
diff --git a/src/Voter/ProjectVoter.php b/src/Voter/ProjectVoter.php
new file mode 100644
index 0000000..a8f0d2e
--- /dev/null
+++ b/src/Voter/ProjectVoter.php
@@ -0,0 +1,35 @@
+getUser();
+
+ if (!$user instanceof User) {
+ return false;
+ }
+ $isAdmin = in_array('ROLE_ADMIN', $user->getRoles());
+ $isAuthor = ($subject->getAuthor() === $user);
+
+ return $isAdmin || $isAuthor;
+ }
+}
diff --git a/src/Voter/TaskVoter.php b/src/Voter/TaskVoter.php
index 1f84b73..e33b961 100644
--- a/src/Voter/TaskVoter.php
+++ b/src/Voter/TaskVoter.php
@@ -12,6 +12,7 @@ class TaskVoter extends Voter
{
const COMPLETE = 'complete';
+ const EDIT = 'edit';
const DELETE = 'delete';
diff --git a/templates/project/create_project_form.html.twig b/templates/project/create_project_form.html.twig
new file mode 100644
index 0000000..dd9b876
--- /dev/null
+++ b/templates/project/create_project_form.html.twig
@@ -0,0 +1,4 @@
+{% extends 'base.html.twig' %}
+{% block body %}
+ {{ form(form) }}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/project/index.html.twig b/templates/project/index.html.twig
new file mode 100644
index 0000000..e0a15cf
--- /dev/null
+++ b/templates/project/index.html.twig
@@ -0,0 +1,44 @@
+{% extends 'base.html.twig' %}
+
+{% block title %}Hello ProjectController!{% endblock %}
+
+{% block body %}
+
+
+
+
+
+ |
+ Name
+ |
+
+ Token
+ |
+
+ Author
+ |
+
+ View
+ |
+
+ Edit
+ |
+
+
+
+ {% for project in projects %}
+
+ | {{ project.name }} |
+ {{ project.token }} |
+ {{ project.author }} |
+ View |
+ Edit |
+
+ {% endfor %}
+
+
+ Create Project
+{% endblock %}
diff --git a/templates/project/project.html.twig b/templates/project/project.html.twig
new file mode 100644
index 0000000..9499dff
--- /dev/null
+++ b/templates/project/project.html.twig
@@ -0,0 +1,51 @@
+{% extends 'base.html.twig' %}
+{% block body %}
+
+ {{ project.name }} - {{ project.token }}
+
+
+
+
+
+ |
+ id
+ |
+
+ author
+ |
+
+ name
+ |
+
+ description
+ |
+
+ due date
+ |
+
+ Status
+ |
+
+ Complete
+ |
+
+
+
+ {% for task in tasks %}
+
+ | {{ task.id }} |
+ {{ task.author }} |
+ {{ task.name }} |
+ {{ task.description }} |
+ {{ task.dueDate|date("d.m.Y")}} |
+ {{ task.isCompleted? "ready" : "not ready" }} |
+
+ {% if (is_granted("complete", task)) %}
+ Complete
+ {% endif %}
+ |
+
+ {% endfor %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/project/project_edit_form.html.twig b/templates/project/project_edit_form.html.twig
new file mode 100644
index 0000000..dd9b876
--- /dev/null
+++ b/templates/project/project_edit_form.html.twig
@@ -0,0 +1,4 @@
+{% extends 'base.html.twig' %}
+{% block body %}
+ {{ form(form) }}
+{% endblock %}
\ No newline at end of file
diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig
index 7490fa3..e853479 100644
--- a/templates/security/login.html.twig
+++ b/templates/security/login.html.twig
@@ -16,7 +16,7 @@
Please sign in
-
+