Base du projet 'application ticketing'

This commit is contained in:
2020-02-17 22:28:57 +01:00
parent 2a72ac97ac
commit afa734f96d
64 changed files with 1798 additions and 685 deletions

View File

@ -27,3 +27,7 @@ APP_SECRET=bc3856916a3206bf0b23356f46c5cf7d
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7
###< doctrine/doctrine-bundle ###
###> nelmio/cors-bundle ###
CORS_ALLOW_ORIGIN=^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$
###< nelmio/cors-bundle ###

View File

@ -5,15 +5,19 @@
"php": "^7.1.3",
"ext-ctype": "*",
"ext-iconv": "*",
"nelmio/cors-bundle": "^2.0",
"sensio/framework-extra-bundle": "^5.5",
"symfony/console": "4.4.*",
"symfony/dotenv": "4.4.*",
"symfony/flex": "^1.3.1",
"symfony/framework-bundle": "4.4.*",
"symfony/inflector": "4.4.*",
"symfony/orm-pack": "^1.0",
"symfony/security-bundle": "4.4.*",
"symfony/yaml": "4.4.*"
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3",
"symfony/maker-bundle": "^1.14",
"symfony/web-server-bundle": "4.4.*"
},

268
backend/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "6421c1affbb7f7322a6499bde9164df1",
"content-hash": "b8a24c5421258bb04d461c7dd0498820",
"packages": [
{
"name": "doctrine/annotations",
@ -1369,6 +1369,63 @@
],
"time": "2020-01-07T22:58:31+00:00"
},
{
"name": "nelmio/cors-bundle",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/nelmio/NelmioCorsBundle.git",
"reference": "9683e6d30d000ef998919261329d825de7c53499"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/9683e6d30d000ef998919261329d825de7c53499",
"reference": "9683e6d30d000ef998919261329d825de7c53499",
"shasum": ""
},
"require": {
"symfony/framework-bundle": "^4.3 || ^5.0"
},
"require-dev": {
"mockery/mockery": "^1.2",
"symfony/phpunit-bridge": "^4.3 || ^5.0"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Nelmio\\CorsBundle\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nelmio",
"homepage": "http://nelm.io"
},
{
"name": "Symfony Community",
"homepage": "https://github.com/nelmio/NelmioCorsBundle/contributors"
}
],
"description": "Adds CORS (Cross-Origin Resource Sharing) headers support in your Symfony application",
"keywords": [
"api",
"cors",
"crossdomain"
],
"time": "2019-11-15T08:54:08+00:00"
},
{
"name": "ocramius/package-versions",
"version": "1.5.1",
@ -1639,6 +1696,84 @@
],
"time": "2019-11-01T11:05:21+00:00"
},
{
"name": "sensio/framework-extra-bundle",
"version": "v5.5.3",
"source": {
"type": "git",
"url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git",
"reference": "98f0807137b13d0acfdf3c255a731516e97015de"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/98f0807137b13d0acfdf3c255a731516e97015de",
"reference": "98f0807137b13d0acfdf3c255a731516e97015de",
"shasum": ""
},
"require": {
"doctrine/annotations": "^1.0",
"php": ">=7.1.3",
"symfony/config": "^4.3|^5.0",
"symfony/dependency-injection": "^4.3|^5.0",
"symfony/framework-bundle": "^4.3|^5.0",
"symfony/http-kernel": "^4.3|^5.0"
},
"conflict": {
"doctrine/doctrine-cache-bundle": "<1.3.1"
},
"require-dev": {
"doctrine/doctrine-bundle": "^1.11|^2.0",
"doctrine/orm": "^2.5",
"nyholm/psr7": "^1.1",
"symfony/browser-kit": "^4.3|^5.0",
"symfony/dom-crawler": "^4.3|^5.0",
"symfony/expression-language": "^4.3|^5.0",
"symfony/finder": "^4.3|^5.0",
"symfony/monolog-bridge": "^4.0|^5.0",
"symfony/monolog-bundle": "^3.2",
"symfony/phpunit-bridge": "^4.3.5|^5.0",
"symfony/psr-http-message-bridge": "^1.1",
"symfony/security-bundle": "^4.3|^5.0",
"symfony/twig-bundle": "^4.3|^5.0",
"symfony/yaml": "^4.3|^5.0",
"twig/twig": "^1.34|^2.4|^3.0"
},
"suggest": {
"symfony/expression-language": "",
"symfony/psr-http-message-bridge": "To use the PSR-7 converters",
"symfony/security-bundle": ""
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "5.5.x-dev"
}
},
"autoload": {
"psr-4": {
"Sensio\\Bundle\\FrameworkExtraBundle\\": "src/"
},
"exclude-from-classmap": [
"/tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "This bundle provides a way to configure your controllers with annotations",
"keywords": [
"annotations",
"controllers"
],
"time": "2019-12-27T08:57:19+00:00"
},
{
"name": "symfony/cache",
"version": "v4.4.4",
@ -4017,6 +4152,137 @@
}
],
"packages-dev": [
{
"name": "doctrine/data-fixtures",
"version": "1.4.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/data-fixtures.git",
"reference": "39e9777c9089351a468f780b01cffa3cb0a42907"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/39e9777c9089351a468f780b01cffa3cb0a42907",
"reference": "39e9777c9089351a468f780b01cffa3cb0a42907",
"shasum": ""
},
"require": {
"doctrine/common": "^2.11",
"doctrine/persistence": "^1.3.3",
"php": "^7.2"
},
"conflict": {
"doctrine/phpcr-odm": "<1.3.0"
},
"require-dev": {
"alcaeus/mongo-php-adapter": "^1.1",
"doctrine/coding-standard": "^6.0",
"doctrine/dbal": "^2.5.4",
"doctrine/mongodb-odm": "^1.3.0",
"doctrine/orm": "^2.7.0",
"phpunit/phpunit": "^7.0"
},
"suggest": {
"alcaeus/mongo-php-adapter": "For using MongoDB ODM with PHP 7",
"doctrine/mongodb-odm": "For loading MongoDB ODM fixtures",
"doctrine/orm": "For loading ORM fixtures",
"doctrine/phpcr-odm": "For loading PHPCR ODM fixtures"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Common\\DataFixtures\\": "lib/Doctrine/Common/DataFixtures"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
}
],
"description": "Data Fixtures for all Doctrine Object Managers",
"homepage": "http://www.doctrine-project.org",
"keywords": [
"database"
],
"time": "2020-01-17T11:11:28+00:00"
},
{
"name": "doctrine/doctrine-fixtures-bundle",
"version": "3.3.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/DoctrineFixturesBundle.git",
"reference": "8f07fcfdac7f3591f3c4bf13a50cbae05f65ed70"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/DoctrineFixturesBundle/zipball/8f07fcfdac7f3591f3c4bf13a50cbae05f65ed70",
"reference": "8f07fcfdac7f3591f3c4bf13a50cbae05f65ed70",
"shasum": ""
},
"require": {
"doctrine/data-fixtures": "^1.3",
"doctrine/doctrine-bundle": "^1.11|^2.0",
"doctrine/orm": "^2.6.0",
"php": "^7.1",
"symfony/config": "^3.4|^4.3|^5.0",
"symfony/console": "^3.4|^4.3|^5.0",
"symfony/dependency-injection": "^3.4|^4.3|^5.0",
"symfony/doctrine-bridge": "^3.4|^4.1|^5.0",
"symfony/http-kernel": "^3.4|^4.3|^5.0"
},
"require-dev": {
"doctrine/coding-standard": "^6.0",
"phpunit/phpunit": "^7.4",
"symfony/phpunit-bridge": "^4.1|^5.0"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-master": "3.3.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Bundle\\FixturesBundle\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Doctrine Project",
"homepage": "http://www.doctrine-project.org"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony DoctrineFixturesBundle",
"homepage": "http://www.doctrine-project.org",
"keywords": [
"Fixture",
"persistence"
],
"time": "2019-11-13T15:46:58+00:00"
},
{
"name": "nikic/php-parser",
"version": "v4.3.0",

View File

@ -7,4 +7,7 @@ return [
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true],
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
];

View File

@ -0,0 +1,10 @@
nelmio_cors:
defaults:
origin_regex: true
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
allow_headers: ['Content-Type', 'Authorization']
expose_headers: ['Link']
max_age: 3600
paths:
'^/': null

View File

@ -1,13 +1,26 @@
security:
encoders:
App\Entity\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
in_memory: { memory: null }
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
anonymous: ~
json_login:
check_path: /api/v1/login
logout:
path: /api/v1/logout
target: /api/v1
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
@ -18,5 +31,3 @@ security:
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }

View File

@ -0,0 +1,3 @@
sensio_framework_extra:
router:
annotations: false

View File

@ -2,29 +2,9 @@
# path: /
# controller: App\Controller\DefaultController::index
app_home:
path: /
controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction
defaults:
path: /api/v1
permanent: true
login:
path: /api/v1/login
controller: App\Controller\SecurityController::login
app_api_login:
path: /api/v1/login
methods: ['POST']
controller: App\Controller\ApiController::login
app_api_logout:
path: /api/v1/logout
methods: ['POST']
controller: App\Controller\ApiController::logout
app_api_show_info:
path: /api/v1
methods: ['GET']
controller: App\Controller\ApiController::showVersionInfo
app_api_list_users:
path: /api/v1/users
methods: ['GET']
controller: App\Controller\ApiController::listUsers
logout:
path: /api/v1/logout

View File

@ -1,33 +0,0 @@
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
class ApiController
{
public function login()
{
return new JsonResponse([
]);
}
public function logout()
{
return new JsonResponse([
]);
}
public function showVersionInfo()
{
return new JsonResponse([
'version' => '1',
]);
}
public function listUsers()
{
return new JsonResponse([]);
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Controller;
use App\Http\DataResponse;
use Symfony\Component\Routing\Annotation\Route;
class IndexController
{
/**
* @Route("/api/v1", name="api_v1_index")
*/
public function showAPIV1Index()
{
return new DataResponse([
'version' => '1',
]);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Controller;
use App\Http\DataResponse;
use Symfony\Component\Routing\Annotation\Route;
class RequestController
{
/**
* @Route("/api/v1/request", name="api_v1_list_requests")
*/
public function listRequests()
{
return new DataResponse([]);
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Controller;
use App\Http\DataResponse;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class SecurityController extends Controller
{
/**
* @Route("/api/v1/login", name="api_v1_login", methods={"POST"})
*/
public function login(Request $request)
{
$user = $this->getUser();
return new DataResponse([
'username' => $user->getUsername(),
'roles' => $user->getRoles(),
]);
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace App\Controller;
use App\Entity\User;
use App\Http\DataResponse;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
class UserController extends Controller
{
/**
* @Route("/api/v1/me", name="api_v1_users_me", methods={"GET"})
* @IsGranted("ROLE_USER")
*/
public function showCurrentUser()
{
/** @var User */
$user = $this->getUser();
return new DataResponse([
'username' => $user->getUsername(),
'roles' => $user->getRoles(),
]);
}
/**
* @Route("/api/v1/users", name="api_v1_list_users", methods={"GET"})
* @IsGranted("ROLE_DEVELOPER")
*/
public function listUsers()
{
/** @var array */
$users = $this->getDoctrine()
->getRepository(User::class)
->findAll()
;
$results = [];
foreach($users as $u) {
$results[] = [
'id' => $u->getId(),
'username' => $u->getUsername(),
];
}
return new DataResponse([
'users' => $results,
]);
}
/**
* @Route("/api/v1/users/{userId}", name="api_v1_get_user", methods={"GET"}, requirements={"userId"="\d+"})
* @IsGranted("ROLE_DEVELOPER")
*/
public function showUser($userId)
{
/** @var User */
$user = $this->getDoctrine()
->getRepository(User::class)
->find($userId)
;
return new DataResponse([
'user' => [
'id' => $user->getId(),
'username' => $user->getUsername(),
]
]);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
// $product = new Product();
// $manager->persist($product);
$manager->flush();
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\DataFixtures;
use App\Entity\RequestStatus;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
class RequestStatusFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
$statuses = [
'En attente',
'Pris en compte',
'En cours de traitement',
'Traité',
'Clos',
];
foreach($statuses as $statusLabel) {
$status = new RequestStatus();
$status->setLabel($statusLabel);
$manager->persist($status);
}
$manager->flush();
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace App\DataFixtures;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserFixtures extends Fixture
{
private $passwordEncoder;
public function __construct(UserPasswordEncoderInterface $passwordEncoder)
{
$this->passwordEncoder = $passwordEncoder;
}
public function load(ObjectManager $manager)
{
// On créait l'utilisateur client1 et client2
$client1 = new User();
$client1->setUsername('client1');
$client1->setPassword($this->passwordEncoder->encodePassword(
$client1,
'client1'
));
$client1->setRoles(['ROLE_CLIENT']);
$manager->persist($client1);
$client2 = new User();
$client2->setUsername('client2');
$client2->setPassword($this->passwordEncoder->encodePassword(
$client2,
'client2'
));
$client2->setRoles(['ROLE_CLIENT']);
$manager->persist($client2);
// On créait l'utilisateur dev1
$dev1 = new User();
$dev1->setUsername('dev1');
$dev1->setPassword($this->passwordEncoder->encodePassword(
$dev1,
'dev1'
));
$dev1->setRoles(['ROLE_DEVELOPER']);
$manager->persist($dev1);
$manager->flush();
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\CommentRepository")
*/
class Comment
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Request", inversedBy="comments")
* @ORM\JoinColumn(nullable=false)
*/
private $request;
/**
* @ORM\Column(type="datetime")
*/
private $createdAt;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="comments")
* @ORM\JoinColumn(nullable=false)
*/
private $author;
/**
* @ORM\Column(type="text")
*/
private $text;
public function getId(): ?int
{
return $this->id;
}
public function getRequest(): ?Request
{
return $this->request;
}
public function setRequest(?Request $request): self
{
$this->request = $request;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): self
{
$this->author = $author;
return $this;
}
public function getText(): ?string
{
return $this->text;
}
public function setText(string $text): self
{
$this->text = $text;
return $this;
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\ProjectRepository")
*/
class Project
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Request", mappedBy="project", orphanRemoval=true)
*/
private $request;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\User", inversedBy="projects")
*/
private $users;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
public function __construct()
{
$this->request = new ArrayCollection();
$this->users = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* @return Collection|Request[]
*/
public function getRequest(): Collection
{
return $this->request;
}
public function addRequest(Request $request): self
{
if (!$this->request->contains($request)) {
$this->request[] = $request;
$request->setProject($this);
}
return $this;
}
public function removeRequest(Request $request): self
{
if ($this->request->contains($request)) {
$this->request->removeElement($request);
// set the owning side to null (unless already changed)
if ($request->getProject() === $this) {
$request->setProject(null);
}
}
return $this;
}
/**
* @return Collection|User[]
*/
public function getUsers(): Collection
{
return $this->users;
}
public function addUser(User $user): self
{
if (!$this->users->contains($user)) {
$this->users[] = $user;
}
return $this;
}
public function removeUser(User $user): self
{
if ($this->users->contains($user)) {
$this->users->removeElement($user);
}
return $this;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
}

View File

@ -0,0 +1,154 @@
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\RequestRepository")
*/
class Request
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $title;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="requests")
* @ORM\JoinColumn(nullable=false)
*/
private $author;
/**
* @ORM\Column(type="datetime")
*/
private $createdAt;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Project", inversedBy="request")
* @ORM\JoinColumn(nullable=false)
*/
private $project;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="request", orphanRemoval=true)
*/
private $comments;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\RequestStatus", inversedBy="requests")
* @ORM\JoinColumn(nullable=true)
*/
private $status;
public function __construct()
{
$this->comments = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): ?string
{
return $this->title;
}
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
public function getAuthor(): ?User
{
return $this->author;
}
public function setAuthor(?User $author): self
{
$this->author = $author;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getProject(): ?Project
{
return $this->project;
}
public function setProject(?Project $project): self
{
$this->project = $project;
return $this;
}
/**
* @return Collection|Comment[]
*/
public function getComments(): Collection
{
return $this->comments;
}
public function addComment(Comment $comment): self
{
if (!$this->comments->contains($comment)) {
$this->comments[] = $comment;
$comment->setRequest($this);
}
return $this;
}
public function removeComment(Comment $comment): self
{
if ($this->comments->contains($comment)) {
$this->comments->removeElement($comment);
// set the owning side to null (unless already changed)
if ($comment->getRequest() === $this) {
$comment->setRequest(null);
}
}
return $this;
}
public function getStatus(): ?RequestStatus
{
return $this->status;
}
public function setStatus(?RequestStatus $status): self
{
$this->status = $status;
return $this;
}
}

View File

@ -0,0 +1,83 @@
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\RequestStatusRepository")
*/
class RequestStatus
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=64)
*/
private $label;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Request", mappedBy="status")
*/
private $requests;
public function __construct()
{
$this->requests = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getLabel(): ?string
{
return $this->label;
}
public function setLabel(string $label): self
{
$this->label = $label;
return $this;
}
/**
* @return Collection|Request[]
*/
public function getRequests(): Collection
{
return $this->requests;
}
public function addRequest(Request $request): self
{
if (!$this->requests->contains($request)) {
$this->requests[] = $request;
$request->setStatus($this);
}
return $this;
}
public function removeRequest(Request $request): self
{
if ($this->requests->contains($request)) {
$this->requests->removeElement($request);
// set the owning side to null (unless already changed)
if ($request->getStatus() === $this) {
$request->setStatus(null);
}
}
return $this;
}
}

222
backend/src/Entity/User.php Normal file
View File

@ -0,0 +1,222 @@
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=180, unique=true)
*/
private $username;
/**
* @ORM\Column(type="json")
*/
private $roles = [];
/**
* @var string The hashed password
* @ORM\Column(type="string")
*/
private $password;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Request", mappedBy="author", orphanRemoval=true)
*/
private $requests;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\Project", mappedBy="users")
*/
private $projects;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="author", orphanRemoval=true)
*/
private $comments;
public function __construct()
{
$this->requests = new ArrayCollection();
$this->projects = new ArrayCollection();
$this->comments = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUsername(): string
{
return (string) $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* @see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* @see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
/**
* @return Collection|Request[]
*/
public function getRequests(): Collection
{
return $this->requests;
}
public function addRequest(Request $request): self
{
if (!$this->requests->contains($request)) {
$this->requests[] = $request;
$request->setAuthor($this);
}
return $this;
}
public function removeRequest(Request $request): self
{
if ($this->requests->contains($request)) {
$this->requests->removeElement($request);
// set the owning side to null (unless already changed)
if ($request->getAuthor() === $this) {
$request->setAuthor(null);
}
}
return $this;
}
/**
* @return Collection|Project[]
*/
public function getProjects(): Collection
{
return $this->projects;
}
public function addProject(Project $project): self
{
if (!$this->projects->contains($project)) {
$this->projects[] = $project;
$project->addUser($this);
}
return $this;
}
public function removeProject(Project $project): self
{
if ($this->projects->contains($project)) {
$this->projects->removeElement($project);
$project->removeUser($this);
}
return $this;
}
/**
* @return Collection|Comment[]
*/
public function getComments(): Collection
{
return $this->comments;
}
public function addComment(Comment $comment): self
{
if (!$this->comments->contains($comment)) {
$this->comments[] = $comment;
$comment->setAuthor($this);
}
return $this;
}
public function removeComment(Comment $comment): self
{
if ($this->comments->contains($comment)) {
$this->comments->removeElement($comment);
// set the owning side to null (unless already changed)
if ($comment->getAuthor() === $this) {
$comment->setAuthor(null);
}
}
return $this;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Http;
use Symfony\Component\HttpFoundation\JsonResponse;
class DataResponse extends JsonResponse {
public function __construct($data = null, $status = 200, $headers = [], $json = false) {
parent::__construct(
['data' => $data],
$status,
$headers,
$json,
);
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Http;
use Symfony\Component\HttpFoundation\JsonResponse;
class ErrorResponse extends JsonResponse {
public function __construct($code, $message, $data = null, $status = 400, $headers = [], $json = false) {
parent::__construct(
[
'error' => [
'code' => $code,
'message' => $message,
'data' => $data,
],
],
$status,
$headers,
$json,
);
}
}

View File

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20200217203938 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, username VARCHAR(180) NOT NULL, roles LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', password VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_8D93D649F85E0677 (username), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE request_status (id INT AUTO_INCREMENT NOT NULL, label VARCHAR(64) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE comment (id INT AUTO_INCREMENT NOT NULL, request_id INT NOT NULL, author_id INT NOT NULL, created_at DATETIME NOT NULL, text LONGTEXT NOT NULL, INDEX IDX_9474526C427EB8A5 (request_id), INDEX IDX_9474526CF675F31B (author_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE project (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE project_user (project_id INT NOT NULL, user_id INT NOT NULL, INDEX IDX_B4021E51166D1F9C (project_id), INDEX IDX_B4021E51A76ED395 (user_id), PRIMARY KEY(project_id, user_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('CREATE TABLE request (id INT AUTO_INCREMENT NOT NULL, author_id INT NOT NULL, project_id INT NOT NULL, title VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL, INDEX IDX_3B978F9FF675F31B (author_id), INDEX IDX_3B978F9F166D1F9C (project_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
$this->addSql('ALTER TABLE comment ADD CONSTRAINT FK_9474526C427EB8A5 FOREIGN KEY (request_id) REFERENCES request (id)');
$this->addSql('ALTER TABLE comment ADD CONSTRAINT FK_9474526CF675F31B FOREIGN KEY (author_id) REFERENCES user (id)');
$this->addSql('ALTER TABLE project_user ADD CONSTRAINT FK_B4021E51166D1F9C FOREIGN KEY (project_id) REFERENCES project (id) ON DELETE CASCADE');
$this->addSql('ALTER TABLE project_user ADD CONSTRAINT FK_B4021E51A76ED395 FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE');
$this->addSql('ALTER TABLE request ADD CONSTRAINT FK_3B978F9FF675F31B FOREIGN KEY (author_id) REFERENCES user (id)');
$this->addSql('ALTER TABLE request ADD CONSTRAINT FK_3B978F9F166D1F9C FOREIGN KEY (project_id) REFERENCES project (id)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE comment DROP FOREIGN KEY FK_9474526CF675F31B');
$this->addSql('ALTER TABLE project_user DROP FOREIGN KEY FK_B4021E51A76ED395');
$this->addSql('ALTER TABLE request DROP FOREIGN KEY FK_3B978F9FF675F31B');
$this->addSql('ALTER TABLE project_user DROP FOREIGN KEY FK_B4021E51166D1F9C');
$this->addSql('ALTER TABLE request DROP FOREIGN KEY FK_3B978F9F166D1F9C');
$this->addSql('ALTER TABLE comment DROP FOREIGN KEY FK_9474526C427EB8A5');
$this->addSql('DROP TABLE user');
$this->addSql('DROP TABLE request_status');
$this->addSql('DROP TABLE comment');
$this->addSql('DROP TABLE project');
$this->addSql('DROP TABLE project_user');
$this->addSql('DROP TABLE request');
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20200217211954 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE request ADD status_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE request ADD CONSTRAINT FK_3B978F9F6BF700BD FOREIGN KEY (status_id) REFERENCES request_status (id)');
$this->addSql('CREATE INDEX IDX_3B978F9F6BF700BD ON request (status_id)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE request DROP FOREIGN KEY FK_3B978F9F6BF700BD');
$this->addSql('DROP INDEX IDX_3B978F9F6BF700BD ON request');
$this->addSql('ALTER TABLE request DROP status_id');
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Repository;
use App\Entity\Comment;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* @method Comment|null find($id, $lockMode = null, $lockVersion = null)
* @method Comment|null findOneBy(array $criteria, array $orderBy = null)
* @method Comment[] findAll()
* @method Comment[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class CommentRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Comment::class);
}
// /**
// * @return Comment[] Returns an array of Comment objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('c')
->andWhere('c.exampleField = :val')
->setParameter('val', $value)
->orderBy('c.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Comment
{
return $this->createQueryBuilder('c')
->andWhere('c.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Repository;
use App\Entity\Project;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* @method Project|null find($id, $lockMode = null, $lockVersion = null)
* @method Project|null findOneBy(array $criteria, array $orderBy = null)
* @method Project[] findAll()
* @method Project[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ProjectRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Project::class);
}
// /**
// * @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()
;
}
*/
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Repository;
use App\Entity\Request;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* @method Request|null find($id, $lockMode = null, $lockVersion = null)
* @method Request|null findOneBy(array $criteria, array $orderBy = null)
* @method Request[] findAll()
* @method Request[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class RequestRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Request::class);
}
// /**
// * @return Request[] Returns an array of Request objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('r')
->andWhere('r.exampleField = :val')
->setParameter('val', $value)
->orderBy('r.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Request
{
return $this->createQueryBuilder('r')
->andWhere('r.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Repository;
use App\Entity\RequestStatus;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* @method RequestStatus|null find($id, $lockMode = null, $lockVersion = null)
* @method RequestStatus|null findOneBy(array $criteria, array $orderBy = null)
* @method RequestStatus[] findAll()
* @method RequestStatus[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class RequestStatusRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, RequestStatus::class);
}
// /**
// * @return RequestStatus[] Returns an array of RequestStatus objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('r')
->andWhere('r.exampleField = :val')
->setParameter('val', $value)
->orderBy('r.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?RequestStatus
{
return $this->createQueryBuilder('r')
->andWhere('r.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @method User|null find($id, $lockMode = null, $lockVersion = null)
* @method User|null findOneBy(array $criteria, array $orderBy = null)
* @method User[] findAll()
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function upgradePassword(UserInterface $user, string $newEncodedPassword): void
{
if (!$user instanceof User) {
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', \get_class($user)));
}
$user->setPassword($newEncodedPassword);
$this->_em->persist($user);
$this->_em->flush();
}
}

View File

@ -20,6 +20,9 @@
"doctrine/common": {
"version": "2.12.0"
},
"doctrine/data-fixtures": {
"version": "1.4.2"
},
"doctrine/dbal": {
"version": "v2.10.1"
},
@ -38,6 +41,18 @@
"src/Repository/.gitignore"
]
},
"doctrine/doctrine-fixtures-bundle": {
"version": "3.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "3.0",
"ref": "fc52d86631a6dfd9fdf3381d0b7e3df2069e51b3"
},
"files": [
"src/DataFixtures/AppFixtures.php"
]
},
"doctrine/doctrine-migrations-bundle": {
"version": "1.2",
"recipe": {
@ -87,6 +102,18 @@
"laminas/laminas-zendframework-bridge": {
"version": "1.0.1"
},
"nelmio/cors-bundle": {
"version": "1.5",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "1.5",
"ref": "6388de23860284db9acce0a7a5d9d13153bcb571"
},
"files": [
"config/packages/nelmio_cors.yaml"
]
},
"nikic/php-parser": {
"version": "v4.3.0"
},
@ -108,6 +135,18 @@
"psr/log": {
"version": "1.1.2"
},
"sensio/framework-extra-bundle": {
"version": "5.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "5.2",
"ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b"
},
"files": [
"config/packages/sensio_framework_extra.yaml"
]
},
"symfony/cache": {
"version": "v4.4.4"
},