From f9eb6fbb6d0a10bce2b8c5b4ea257d3b4ff71475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20DAIM?= Date: Wed, 8 Nov 2023 17:30:06 +0100 Subject: [PATCH 01/27] new branch starting the team part in the MVC, plus a little change about the type Color --- Documentation/team.puml | 3 +++ public/index.php | 3 +++ sql/setup-tables.sql | 8 +++++++- src/Controller/FrontController.php | 8 ++++++++ src/Controller/TeamController.php | 33 ++++++++++++++++++++++++++++++ src/Data/Color.php | 21 +++++++++++++++---- src/Gateway/TeamGateway.php | 23 +++++++++++++++++++++ src/Model/TeamModel.php | 24 ++++++++++++++++++++++ 8 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 Documentation/team.puml create mode 100644 src/Controller/FrontController.php create mode 100644 src/Controller/TeamController.php create mode 100644 src/Gateway/TeamGateway.php create mode 100644 src/Model/TeamModel.php diff --git a/Documentation/team.puml b/Documentation/team.puml new file mode 100644 index 0000000..f775665 --- /dev/null +++ b/Documentation/team.puml @@ -0,0 +1,3 @@ +@startuml +/*todo*/ +@enduml \ No newline at end of file diff --git a/public/index.php b/public/index.php index 4c5290b..88d90fa 100644 --- a/public/index.php +++ b/public/index.php @@ -40,6 +40,9 @@ $router->map("POST", "/submit", fn() => $sampleFormController->submitForm($_POST $router->map("GET", "/twig", fn() => $sampleFormController->displayFormTwig()); $router->map("POST", "/submit-twig", fn() => $sampleFormController->submitFormTwig($_POST)); +$teamController = new \App\Controller\TeamController(new \App\Model\TeamModel(),$twig); +$router->map("GET","/team/new", fn()=>$teamController->submitTeam($_POST)); + $match = $router->match(); if ($match == null) { diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index 0c6fbe7..70723dc 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -1,8 +1,14 @@ - -- drop tables here DROP TABLE IF EXISTS FormEntries; CREATE TABLE FormEntries(name varchar, description varchar); +CREATE TABLE Team( + id numeric PRIMARY KEY AUTOINCREMENT, + name varchar, + picture varchar, + mainColor varchar, + secondColor varchar +); diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php new file mode 100644 index 0000000..c1a39bc --- /dev/null +++ b/src/Controller/FrontController.php @@ -0,0 +1,8 @@ +model = $model; + $this->twig = $twig; + } + + public function submitTeam(array $request){ + if(isset($request['name']) && isset($request["picture"]) && isset($request["mainColor"]) && isset($request["secondColor"])){ + $result= $this->$model->; + } + else{ + http_response_code(400); + echo "Champ(s) manquant(s)"; + } + } + +} + + diff --git a/src/Data/Color.php b/src/Data/Color.php index f841731..bc9043b 100755 --- a/src/Data/Color.php +++ b/src/Data/Color.php @@ -14,10 +14,7 @@ class Color { * @param int $value 6 bytes unsigned int that represents an RGB color * @throws \InvalidArgumentException if the value is negative or greater than 0xFFFFFF */ - public function __constructor(int $value) { - if ($value < 0 || $value > 0xFFFFFF) { - throw new InvalidArgumentException("int color value is invalid, must be positive and lower than 0xFFFFFF"); - } + private function __constructor(int $value) { $this->value = $value; } @@ -27,4 +24,20 @@ class Color { public function getValue(): int { return $this->value; } + + public static function from(int $value): Color { + $color = self::tryFrom($value); + if ($color == null) { + throw new InvalidArgumentException("int color value is invalid, must be positive and lower than 0xFFFFFF"); + } + return $color; + } + + public static function tryFrom(int $value): ?Color { + if ($value < 0 || $value > 0xFFFFFF) { + return null; + } + return new Color($value); + } + } \ No newline at end of file diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php new file mode 100644 index 0000000..2bd5faa --- /dev/null +++ b/src/Gateway/TeamGateway.php @@ -0,0 +1,23 @@ +con = $con; + } + + function insert(string $name, string $picture, string $mainColor, string $secondColor) { + $this->con->exec( /* todo */ + "INSERT INTO Team VALUES (:name, :picture)", + [ + ":name" => [$username, PDO::PARAM_STR], + "description" => [$description, PDO::PARAM_STR] + ] + ); + } +} \ No newline at end of file diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php new file mode 100644 index 0000000..b1d4e67 --- /dev/null +++ b/src/Model/TeamModel.php @@ -0,0 +1,24 @@ +gateway = $gateway; + } + + + public function createTeam(string $name, Color $mainColor, Color $secondColor ){ + + } +} \ No newline at end of file From c8df55669983ba7ffbc45490bea2c43f0e385f89 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Fri, 10 Nov 2023 15:23:21 +0100 Subject: [PATCH 02/27] made progression on the create team almost done --- src/Controller/FrontController.php | 8 ------ src/Controller/TeamController.php | 6 +++-- src/Gateway/TeamGateway.php | 20 +++++++++++---- src/Model/TeamModel.php | 39 +++++++++++++++++++++++++++--- src/Model/Validation.php | 10 ++++++++ 5 files changed, 65 insertions(+), 18 deletions(-) delete mode 100644 src/Controller/FrontController.php create mode 100644 src/Model/Validation.php diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php deleted file mode 100644 index c1a39bc..0000000 --- a/src/Controller/FrontController.php +++ /dev/null @@ -1,8 +0,0 @@ -$model->; + $errors = $this->model->createTeam($request['name'],$request['picture'],$request['mainColor'],$request["secondColor"]); + /*todo gestion des erreurs grace au tableau*/ } else{ http_response_code(400); - echo "Champ(s) manquant(s)"; + /*todo rappeler vue avec parametre pour signaler les pb */ } } diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php index 2bd5faa..56992d8 100644 --- a/src/Gateway/TeamGateway.php +++ b/src/Gateway/TeamGateway.php @@ -1,7 +1,10 @@ con = $con; } - function insert(string $name, string $picture, string $mainColor, string $secondColor) { - $this->con->exec( /* todo */ - "INSERT INTO Team VALUES (:name, :picture)", + public function insert(string $name, string $picture, Color $mainColor, Color $secondColor) { + $this->con->exec( + "INSERT INTO Team VALUES (:name, :picture, :mainColor, :secondColor)", [ - ":name" => [$username, PDO::PARAM_STR], - "description" => [$description, PDO::PARAM_STR] + ":name" => [$name, PDO::PARAM_STR], + ":picture" => [$picture, PDO::PARAM_STR], + ":mainColor" => [$mainColor, PDO::PARAM_STR], + ":secondColor" => [$secondColor, PDO::PARAM_STR] ] ); } + + public function listByName($name): array { + /*todo*/ + } + } \ No newline at end of file diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index b1d4e67..3158423 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -1,12 +1,22 @@ gateway = $gateway; } + public function createTeam(string $name,string $picture,int $mainColorValue, int $secondColorValue, array $errors) { - public function createTeam(string $name, Color $mainColor, Color $secondColor ){ + $mainColor = Color::tryFrom($mainColorValue); + $secondColor = Color::tryFrom($secondColorValue); + if( $mainColor == null || $secondColor == null ){ + $errors[] = self::ERROR_INVALID_COLOR; + } + + if(Validation::hasHTMLInjection($name)){ + $errors[] = self::ERROR_INVALID_NAME; + } + + if(filter_var($picture,FILTER_VALIDATE_URL)){ + $errors[] = self::ERROR_INVALID_PICTURE; + } + + if(empty($errors)){ + $this->gateway->insert($name,$picture,$mainColor,$secondColor); + } + + } + public function list(string $search,array $errors):array { + if(Validation::hasHTMLInjection($search)){ + $errors = self::ERROR_INVALID_SEARCH; + } } } \ No newline at end of file diff --git a/src/Model/Validation.php b/src/Model/Validation.php new file mode 100644 index 0000000..ac732d0 --- /dev/null +++ b/src/Model/Validation.php @@ -0,0 +1,10 @@ +]"); + } +} \ No newline at end of file From d08defaf65a6491f751018b8debea120d0f90df1 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Sun, 12 Nov 2023 23:50:58 +0100 Subject: [PATCH 03/27] Added a view for the creation of a team, started the list by name action --- src/Controller/TeamController.php | 18 ++++--- src/Gateway/TeamGateway.php | 7 ++- src/Model/TeamModel.php | 13 ++++- src/Views/insertTeam.html.twig | 81 +++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 src/Views/insertTeam.html.twig diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index 930fe21..65e3f25 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -20,16 +20,22 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ public function submitTeam(array $request){ $errors = []; - if(isset($request['name']) && isset($request["picture"]) && isset($request["mainColor"]) && isset($request["secondColor"])){ - $errors = $this->model->createTeam($request['name'],$request['picture'],$request['mainColor'],$request["secondColor"]); - /*todo gestion des erreurs grace au tableau*/ + $this->model->createTeam($request['name'],$request['picture'],$request['mainColor'],$request["secondColor"],$errors); + if(!empty($errors)){ + /*todo appelle vue avec param*/ + } + } + + public function listTeamByName(array $request){ + $errors = []; + $this->model->listByName($request['name'],$errors); + if(!empty($errors)){ + /*todo appelle vue avec param*/ } else{ - http_response_code(400); - /*todo rappeler vue avec parametre pour signaler les pb */ + /*todo appelle bonne vue*/ } } - } diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php index 56992d8..17462ea 100644 --- a/src/Gateway/TeamGateway.php +++ b/src/Gateway/TeamGateway.php @@ -27,7 +27,12 @@ class TeamGateway /* retourne exception par rapport à la validité du paramètr } public function listByName($name): array { - /*todo*/ + return $this->con->fetch( + "SELECT name,picture,mainColor,secondColor FROM Team WHERE name LIKE '% :thing %' ", + [ + ":thing" => [$name, PDO::PARAM_STR] + ] + ); } } \ No newline at end of file diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index 3158423..6815ad6 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -16,6 +16,7 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po public const ERROR_INVALID_NAME = 1; public const ERROR_INVALID_PICTURE = 2; public const ERROR_INVALID_SEARCH = 3; + public const ERROR_NO_DATA_FOUND = 4; private TeamGateway $gateway; @@ -49,9 +50,17 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po } - public function list(string $search,array $errors):array { - if(Validation::hasHTMLInjection($search)){ + public function listByName(string $name,array $errors):?array { + if(Validation::hasHTMLInjection($name)){ $errors = self::ERROR_INVALID_SEARCH; } + $results = $this->gateway->listByName($name); + if(empty($results)){ + $errors = self::ERROR_NO_DATA_FOUND; + } + if(!empty($errors)){ + return $results; + } + return null; } } \ No newline at end of file diff --git a/src/Views/insertTeam.html.twig b/src/Views/insertTeam.html.twig new file mode 100644 index 0000000..62f6553 --- /dev/null +++ b/src/Views/insertTeam.html.twig @@ -0,0 +1,81 @@ + + + + + Insertion view + + + + + + + +
+

Créer une équipe

+
+
+ + + + + + + + +
+
+ +
+
+
+ + + \ No newline at end of file From 6ff667e3b8bcecb381f8d5b6f430cda65fa14149 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Mon, 13 Nov 2023 22:12:51 +0100 Subject: [PATCH 04/27] WIP made a diagram class For team within MVC, players aren't incorporated yet --- Documentation/team.puml | 67 ++++++++++++++++++++++++++++++- public/index.php | 2 +- src/Controller/TeamController.php | 2 +- src/Gateway/TeamGateway.php | 2 +- src/Model/TeamModel.php | 2 +- src/Views/insertTeam.html.twig | 3 -- 6 files changed, 70 insertions(+), 8 deletions(-) diff --git a/Documentation/team.puml b/Documentation/team.puml index f775665..b8374cd 100644 --- a/Documentation/team.puml +++ b/Documentation/team.puml @@ -1,3 +1,68 @@ @startuml -/*todo*/ +class Team { + - name: String + - picture: Url + - members: array + + + getName(): String + + getPicture(): Url + + getMainColor(): Color + + getSecondColor(): Color + + listMembers(): array +} + +Team --> "- mainColor" Color +Team --> "- secondaryColor" Color + +class Color { + - value: int + + + getValue(): int +} + +class TeamGateway{ + -- + + __construct(con : Connexion) + + insert(name : string ,picture : string, mainColor : Color, secondColor : Color) + + listByName(name : string): array +} + +TeamGateway *--"- con" Connexion +TeamGateway ..> Color + +class TeamModel{ + + ERROR_INVALID_COLOR : int {readOnly} + + ERROR_INVALID_NAME : int {readOnly} + + ERROR_INVALID_PICTURE : int {readOnly} + + ERROR_INVALID_SEARCH : int {readOnly} + + ERROR_NO_DATA_FOUND : int {readOnly} + --- + + __construct(gateway : TeamGateway) + + createTeam(name : string,picture : string, mainColorValue : int, secondColorValue : int, errors : array) + + listByName(name : string ,errors : array) : ?array +} + +TeamModel *--"- gateway" TeamGateway +TeamModel ..> Team +TeamModel ..> Color + +class TeamController{ + - twig : Environement + -- + + __construct( model : TeamModel, twig : Environement) + + submitTeam(request : array) + + listTeamByName(request : array) +} + +TeamController *--"- model" TeamModel + +class Connexion{ + - pdo : PDO + -- + + __constructor(pdo : PDO) + + exec(query : string, args : array) + + fetch(query string, args array): array + +} + @enduml \ No newline at end of file diff --git a/public/index.php b/public/index.php index 88d90fa..816517d 100644 --- a/public/index.php +++ b/public/index.php @@ -40,7 +40,7 @@ $router->map("POST", "/submit", fn() => $sampleFormController->submitForm($_POST $router->map("GET", "/twig", fn() => $sampleFormController->displayFormTwig()); $router->map("POST", "/submit-twig", fn() => $sampleFormController->submitFormTwig($_POST)); -$teamController = new \App\Controller\TeamController(new \App\Model\TeamModel(),$twig); +$teamController = new \App\Controller\TeamController(new \App\Model\TeamModel(new \App\Gateway\TeamGateway($con)),$twig); $router->map("GET","/team/new", fn()=>$teamController->submitTeam($_POST)); $match = $router->match(); diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index 65e3f25..04d88e4 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -28,7 +28,7 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ public function listTeamByName(array $request){ $errors = []; - $this->model->listByName($request['name'],$errors); + $results = $this->model->listByName($request['name'],$errors); if(!empty($errors)){ /*todo appelle vue avec param*/ } diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php index 17462ea..f492411 100644 --- a/src/Gateway/TeamGateway.php +++ b/src/Gateway/TeamGateway.php @@ -26,7 +26,7 @@ class TeamGateway /* retourne exception par rapport à la validité du paramètr ); } - public function listByName($name): array { + public function listByName(string $name): array { return $this->con->fetch( "SELECT name,picture,mainColor,secondColor FROM Team WHERE name LIKE '% :thing %' ", [ diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index 6815ad6..d356672 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -10,7 +10,7 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po { /** - * Attributes used to + * Attributes used to handle errors */ public const ERROR_INVALID_COLOR = 0; public const ERROR_INVALID_NAME = 1; diff --git a/src/Views/insertTeam.html.twig b/src/Views/insertTeam.html.twig index 62f6553..0f68a9f 100644 --- a/src/Views/insertTeam.html.twig +++ b/src/Views/insertTeam.html.twig @@ -55,9 +55,6 @@ - - -

Créer une équipe

From a283c4b12627feb38ca5cb8c57ea84babed10e43 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Tue, 14 Nov 2023 17:27:03 +0100 Subject: [PATCH 05/27] WIP almost done, added errors handling and more, got some errors with the gateway, i will fix it tomorrow --- public/index.php | 3 +- sql/setup-tables.sql | 14 ++++++-- src/Controller/TeamController.php | 55 +++++++++++++++++++++++-------- src/Data/Team.php | 2 +- src/Gateway/TeamGateway.php | 16 ++++----- src/Model/TeamModel.php | 33 ++++++------------- src/Model/Validation.php | 10 ------ src/Validation/Validators.php | 19 +++++++++++ src/Views/insertTeam.html.twig | 7 ++++ 9 files changed, 100 insertions(+), 59 deletions(-) delete mode 100644 src/Model/Validation.php diff --git a/public/index.php b/public/index.php index 6bc77c4..ae49e19 100644 --- a/public/index.php +++ b/public/index.php @@ -40,7 +40,8 @@ $router->map("GET", "/tactic/new", fn() => $editorController->makeNew()); $router->map("GET", "/tactic/[i:id]/edit", fn(int $id) => $editorController->openEditorFor($id)); $teamController = new \App\Controller\TeamController(new \App\Model\TeamModel(new \App\Gateway\TeamGateway($con)),$twig); -$router->map("GET","/team/new", fn()=>$teamController->submitTeam($_POST)); +$router->map("GET","/team/new", fn()=>$teamController->displaySubmitTeam()); +$router->map("POST","/team/new", fn()=>$teamController->SubmitTeam($_POST)); $match = $router->match(); diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index be0301a..989f241 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -8,10 +8,20 @@ CREATE TABLE Team( id numeric PRIMARY KEY AUTOINCREMENT, name varchar, picture varchar, - mainColor varchar, - secondColor varchar + mainColor numeric, + secondColor numeric ); +CREATE TABLE Participate( + idTeam numeric FOREIGN KEY REFERENCES Team(id), + idMember numeric FOREIGN KEY REFERENCES Member(id), + role char CHECK (role IN ('C','P')) +); + +CREATE TABLE Member( + id numeric PRIMARY KEY AUTOINCREMENT, + email varchar, +); CREATE TABLE TacticInfo( id integer PRIMARY KEY AUTOINCREMENT, diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index 04d88e4..de98d09 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -1,7 +1,17 @@ model = $model; $this->twig = $twig; } - public function submitTeam(array $request){ - $errors = []; - $this->model->createTeam($request['name'],$request['picture'],$request['mainColor'],$request["secondColor"],$errors); - if(!empty($errors)){ - /*todo appelle vue avec param*/ + public function displaySubmitTeam() { + try { + $this->twig->display("insertTeam.html.twig", []); + } catch (LoaderError | RuntimeError | SyntaxError $e) { + echo " twig error : $e"; } } - public function listTeamByName(array $request){ + public function submitTeam(array $request): HttpResponse { + $errors = []; - $results = $this->model->listByName($request['name'],$errors); - if(!empty($errors)){ - /*todo appelle vue avec param*/ - } - else{ - /*todo appelle bonne vue*/ + $request = HttpRequest::from($request, $errors, [ + "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], + "mainColor" => [Validators::isInteger(), Validators::isIntInRange(0, 0xffffff)], + "secondColor" => [Validators::isInteger(), Validators::isIntInRange(0, 0xffffff)], + "picture" => [Validators::isURL()] + ]); + if (!empty($errors)) { + $badFields = []; + foreach ($errors as $e) { + if ($e instanceof FieldValidationFail) { + $badFields[] = $e->getFieldName(); + } + } + return ViewHttpResponse::twig('insertTeam.html.twig',['bad_fields'=> $badFields]); } + $this->model->createTeam($request['name'], $request['picture'], intval($request['mainColor']), intval($request['secondColor'])); + return ViewHttpResponse::twig('sample_form.html.twig',[]); + } + + public function listTeamByName(array $request): HttpResponse { + $errors = []; + + $results = $this->model->listByName($request['name'], $errors); + } } diff --git a/src/Data/Team.php b/src/Data/Team.php index 48643d9..bcdabac 100755 --- a/src/Data/Team.php +++ b/src/Data/Team.php @@ -22,7 +22,7 @@ class Team { * @param Color $secondColor * @param array $members */ - public function __construct(string $name, Url $picture, Color $mainColor, Color $secondColor, array $members) { + public function __construct(string $name, Url $picture, Color $mainColor, Color $secondColor, array $members =[]) { $this->name = $name; $this->picture = $picture; $this->mainColor = $mainColor; diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php index f492411..7c5883c 100644 --- a/src/Gateway/TeamGateway.php +++ b/src/Gateway/TeamGateway.php @@ -6,7 +6,7 @@ use App\Connexion; use App\Data\Color; use PDO; -class TeamGateway /* retourne exception par rapport à la validité du paramètre par ex. un int qui ne peut pas etre <0 doit etre verif etsoulever une exception */ +class TeamGateway { private Connexion $con; @@ -14,23 +14,23 @@ class TeamGateway /* retourne exception par rapport à la validité du paramètr $this->con = $con; } - public function insert(string $name, string $picture, Color $mainColor, Color $secondColor) { + public function insert(string $name, string $picture, int $mainColor, int $secondColor) { $this->con->exec( - "INSERT INTO Team VALUES (:name, :picture, :mainColor, :secondColor)", + "INSERT INTO Team VALUES (:teamName , :picture, :mainColor, :secondColor)", [ - ":name" => [$name, PDO::PARAM_STR], + ":teamName" => [$name, PDO::PARAM_STR], ":picture" => [$picture, PDO::PARAM_STR], - ":mainColor" => [$mainColor, PDO::PARAM_STR], - ":secondColor" => [$secondColor, PDO::PARAM_STR] + ":mainColor" => [$mainColor, PDO::PARAM_INT], + ":secondColor" => [$secondColor, PDO::PARAM_INT] ] ); } public function listByName(string $name): array { return $this->con->fetch( - "SELECT name,picture,mainColor,secondColor FROM Team WHERE name LIKE '% :thing %' ", + "SELECT name,picture,mainColor,secondColor FROM Team WHERE name LIKE '%:match%' ", [ - ":thing" => [$name, PDO::PARAM_STR] + ":match" => [$name, PDO::PARAM_STR] ] ); } diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index d356672..4bcd27e 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -2,6 +2,7 @@ namespace App\Model; use App\Data\Color; use App\Gateway\TeamGateway; +use App\Data\Team; /** * @@ -28,39 +29,25 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po $this->gateway = $gateway; } - public function createTeam(string $name,string $picture,int $mainColorValue, int $secondColorValue, array $errors) { - - $mainColor = Color::tryFrom($mainColorValue); - $secondColor = Color::tryFrom($secondColorValue); - if( $mainColor == null || $secondColor == null ){ - $errors[] = self::ERROR_INVALID_COLOR; - } - - if(Validation::hasHTMLInjection($name)){ - $errors[] = self::ERROR_INVALID_NAME; - } - - if(filter_var($picture,FILTER_VALIDATE_URL)){ - $errors[] = self::ERROR_INVALID_PICTURE; - } - - if(empty($errors)){ - $this->gateway->insert($name,$picture,$mainColor,$secondColor); - } + public function createTeam(string $name,string $picture,int $mainColor, int $secondColor) { + $this->gateway->insert($name,$picture,$mainColor,$secondColor); } - public function listByName(string $name,array $errors):?array { + public function listByName(string $name,array &$errors):array { + $teams=[]; if(Validation::hasHTMLInjection($name)){ $errors = self::ERROR_INVALID_SEARCH; } $results = $this->gateway->listByName($name); + if(empty($results)){ $errors = self::ERROR_NO_DATA_FOUND; } - if(!empty($errors)){ - return $results; + + foreach ($results as $row){ + $teams[] = new Team($row['name'],$row['picture'],$row['mainColor'],$row['secondColor']); } - return null; + return $results; } } \ No newline at end of file diff --git a/src/Model/Validation.php b/src/Model/Validation.php deleted file mode 100644 index ac732d0..0000000 --- a/src/Model/Validation.php +++ /dev/null @@ -1,10 +0,0 @@ -]"); - } -} \ No newline at end of file diff --git a/src/Validation/Validators.php b/src/Validation/Validators.php index ea9da46..2f69ca4 100644 --- a/src/Validation/Validators.php +++ b/src/Validation/Validators.php @@ -51,4 +51,23 @@ class Validators { } ); } + + public static function isInteger(): Validator { + return self::regex("/^[0-9]+$/"); + } + + public static function isIntInRange(int $min,int $max): Validator { + return new SimpleFunctionValidator( + fn(string $val) => intval($val) >= $min && intval($val) <= $max, + fn(string $name) => [new FieldValidationFail($name, "The value is not in the range $min to $max ")] + ); + } + + public static function isURL(): Validator { + return new SimpleFunctionValidator( + fn($val) => filter_var($val, FILTER_VALIDATE_URL) , + fn(string $name) => [new FieldValidationFail($name, "The value is not an URL")] + ); + } + } \ No newline at end of file diff --git a/src/Views/insertTeam.html.twig b/src/Views/insertTeam.html.twig index 0f68a9f..ba6ffac 100644 --- a/src/Views/insertTeam.html.twig +++ b/src/Views/insertTeam.html.twig @@ -31,6 +31,12 @@ margin-bottom: 5px; } + {% for item in bad_fields %} + #{{ item }}{ + border-color: red; + } + {% endfor %} + input[type="text"], input[type="password"] { width: 100%; padding: 10px; @@ -51,6 +57,7 @@ background-color: #0056b3; } + From 0859467e3fdda12a5cef0e62aba7e1a089b04277 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Tue, 14 Nov 2023 22:58:21 +0100 Subject: [PATCH 06/27] WIP just a few little adjusments and progress --- public/index.php | 3 + sql/setup-tables.sql | 4 +- src/Controller/TeamController.php | 28 +++++-- src/Model/TeamModel.php | 11 +-- ...rtTeam.html.twig => insert_team.html.twig} | 0 src/Views/list_team_by_name.html.twig | 77 +++++++++++++++++++ 6 files changed, 106 insertions(+), 17 deletions(-) rename src/Views/{insertTeam.html.twig => insert_team.html.twig} (100%) create mode 100644 src/Views/list_team_by_name.html.twig diff --git a/public/index.php b/public/index.php index ae49e19..bdb9000 100644 --- a/public/index.php +++ b/public/index.php @@ -42,6 +42,9 @@ $router->map("GET", "/tactic/[i:id]/edit", fn(int $id) => $editorController->ope $teamController = new \App\Controller\TeamController(new \App\Model\TeamModel(new \App\Gateway\TeamGateway($con)),$twig); $router->map("GET","/team/new", fn()=>$teamController->displaySubmitTeam()); $router->map("POST","/team/new", fn()=>$teamController->SubmitTeam($_POST)); +$router->map("GET","/team/list", fn()=>$teamController->displayListTeamByName()); +$router->map("POST","/team/list", fn()=>$teamController->ListTeamByName($_POST)); +/*todo $router->map("GET","/team/[i:id]", fn()=>$teamController->displayTeam);*/ $match = $router->match(); diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index 989f241..437649c 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -5,7 +5,7 @@ DROP TABLE IF EXISTS TacticInfo; CREATE TABLE FormEntries(name varchar, description varchar); CREATE TABLE Team( - id numeric PRIMARY KEY AUTOINCREMENT, + id integer PRIMARY KEY AUTOINCREMENT, name varchar, picture varchar, mainColor numeric, @@ -19,7 +19,7 @@ CREATE TABLE Participate( ); CREATE TABLE Member( - id numeric PRIMARY KEY AUTOINCREMENT, + id integer PRIMARY KEY AUTOINCREMENT, email varchar, ); diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index de98d09..8702b3e 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -29,7 +29,7 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ public function displaySubmitTeam() { try { - $this->twig->display("insertTeam.html.twig", []); + $this->twig->display("insert_team.html.twig", []); } catch (LoaderError | RuntimeError | SyntaxError $e) { echo " twig error : $e"; } @@ -51,17 +51,33 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ $badFields[] = $e->getFieldName(); } } - return ViewHttpResponse::twig('insertTeam.html.twig',['bad_fields'=> $badFields]); + return ViewHttpResponse::twig('insert_team.html.twig',['bad_fields'=> $badFields]); } $this->model->createTeam($request['name'], $request['picture'], intval($request['mainColor']), intval($request['secondColor'])); - return ViewHttpResponse::twig('sample_form.html.twig',[]); + return ViewHttpResponse::twig('sample_form.html.twig',[]); /*todo appeler une vue qui display la team au lieu de ça*/ } + public function displayListTeamByName(){ + try { + $this->twig->display("list_team_by_name.html.twig", []); + } catch (LoaderError | RuntimeError | SyntaxError $e) { + echo " twig error : $e"; + } + } public function listTeamByName(array $request): HttpResponse { $errors = []; - - $results = $this->model->listByName($request['name'], $errors); - + $request = HttpRequest::from($request, $errors, [ + "name" => [Validators::lenBetween(1,32),Validators::nameWithSpaces()] + ]); + if(!empty($errors) && $errors[0] instanceof FieldValidationFail){ + $badField = $errors[0]->getFieldName(); + return ViewHttpResponse::twig('list_team_by_name.html.twig',['bad_field' => $badField]) ; + } + $results = $this->model->listByName($request['name']); + if (empty($results)){ + /*todo appelle de la bonne vue qui va afficher un message qui dit que bah ca retourne rien, proposer de refaire une recherche*/ + } + return ViewHttpResponse::twig('display_teams.html.twig',['teams' => $results]); } } diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index 4bcd27e..02eef70 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -34,20 +34,13 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po } - public function listByName(string $name,array &$errors):array { + public function listByName(string $name):array { $teams=[]; - if(Validation::hasHTMLInjection($name)){ - $errors = self::ERROR_INVALID_SEARCH; - } $results = $this->gateway->listByName($name); - if(empty($results)){ - $errors = self::ERROR_NO_DATA_FOUND; - } - foreach ($results as $row){ $teams[] = new Team($row['name'],$row['picture'],$row['mainColor'],$row['secondColor']); } - return $results; + return $teams; } } \ No newline at end of file diff --git a/src/Views/insertTeam.html.twig b/src/Views/insert_team.html.twig similarity index 100% rename from src/Views/insertTeam.html.twig rename to src/Views/insert_team.html.twig diff --git a/src/Views/list_team_by_name.html.twig b/src/Views/list_team_by_name.html.twig new file mode 100644 index 0000000..7e0cbb3 --- /dev/null +++ b/src/Views/list_team_by_name.html.twig @@ -0,0 +1,77 @@ + + + + + Insertion view + + + + +
+

Créer une équipe

+ +
+ + +
+
+ +
+ +
+ + + \ No newline at end of file From 30611b9b21b0b9dad948fdfb5f33dabb12fdadfc Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Wed, 15 Nov 2023 15:21:17 +0100 Subject: [PATCH 07/27] WIP done with insertion, got a problem on list team and starting displayTeam --- public/index.php | 4 ++- src/Controller/TeamController.php | 35 ++++++++++++--------------- src/Data/Team.php | 11 ++++++++- src/Gateway/TeamGateway.php | 28 ++++++++++++++++----- src/Model/TeamModel.php | 20 ++++++--------- src/Views/display_team.html.twig | 35 +++++++++++++++++++++++++++ src/Views/display_teams.html.twig | 35 +++++++++++++++++++++++++++ src/Views/list_team_by_name.html.twig | 2 +- 8 files changed, 129 insertions(+), 41 deletions(-) create mode 100644 src/Views/display_team.html.twig create mode 100644 src/Views/display_teams.html.twig diff --git a/public/index.php b/public/index.php index bdb9000..cc63837 100644 --- a/public/index.php +++ b/public/index.php @@ -42,9 +42,11 @@ $router->map("GET", "/tactic/[i:id]/edit", fn(int $id) => $editorController->ope $teamController = new \App\Controller\TeamController(new \App\Model\TeamModel(new \App\Gateway\TeamGateway($con)),$twig); $router->map("GET","/team/new", fn()=>$teamController->displaySubmitTeam()); $router->map("POST","/team/new", fn()=>$teamController->SubmitTeam($_POST)); + $router->map("GET","/team/list", fn()=>$teamController->displayListTeamByName()); $router->map("POST","/team/list", fn()=>$teamController->ListTeamByName($_POST)); -/*todo $router->map("GET","/team/[i:id]", fn()=>$teamController->displayTeam);*/ + +$router->map("GET","/team/[i:id]", fn(int $id)=>$teamController->displayTeam($id)); $match = $router->match(); diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index 8702b3e..8228d6a 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -28,11 +28,7 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ } public function displaySubmitTeam() { - try { - $this->twig->display("insert_team.html.twig", []); - } catch (LoaderError | RuntimeError | SyntaxError $e) { - echo " twig error : $e"; - } + return ViewHttpResponse::twig("insert_team.html.twig", []); } public function submitTeam(array $request): HttpResponse { @@ -51,33 +47,34 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ $badFields[] = $e->getFieldName(); } } - return ViewHttpResponse::twig('insert_team.html.twig',['bad_fields'=> $badFields]); + return ViewHttpResponse::twig('insert_team.html.twig', ['bad_fields' => $badFields]); } $this->model->createTeam($request['name'], $request['picture'], intval($request['mainColor']), intval($request['secondColor'])); - return ViewHttpResponse::twig('sample_form.html.twig',[]); /*todo appeler une vue qui display la team au lieu de ça*/ + return ViewHttpResponse::twig('sample_form.html.twig', []); /*todo appeler une vue qui display la team au lieu de ça*/ } - public function displayListTeamByName(){ - try { - $this->twig->display("list_team_by_name.html.twig", []); - } catch (LoaderError | RuntimeError | SyntaxError $e) { - echo " twig error : $e"; - } + public function displayListTeamByName(): HttpResponse { + return ViewHttpResponse::twig("list_team_by_name.html.twig", []); } + public function listTeamByName(array $request): HttpResponse { $errors = []; $request = HttpRequest::from($request, $errors, [ - "name" => [Validators::lenBetween(1,32),Validators::nameWithSpaces()] + "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()] ]); - if(!empty($errors) && $errors[0] instanceof FieldValidationFail){ + if (!empty($errors) && $errors[0] instanceof FieldValidationFail) { $badField = $errors[0]->getFieldName(); - return ViewHttpResponse::twig('list_team_by_name.html.twig',['bad_field' => $badField]) ; + return ViewHttpResponse::twig('list_team_by_name.html.twig', ['bad_field' => $badField]); } $results = $this->model->listByName($request['name']); - if (empty($results)){ - /*todo appelle de la bonne vue qui va afficher un message qui dit que bah ca retourne rien, proposer de refaire une recherche*/ + if (empty($results)) { + return ViewHttpResponse::twig('display_teams.html.twig', []); } - return ViewHttpResponse::twig('display_teams.html.twig',['teams' => $results]); + return ViewHttpResponse::twig('display_teams.html.twig', ['teams' => $results]); + } + + public function displayTeam(int $id): HttpResponse{ + $results = $this->model->displayTeam($id); } } diff --git a/src/Data/Team.php b/src/Data/Team.php index bcdabac..321a5d9 100755 --- a/src/Data/Team.php +++ b/src/Data/Team.php @@ -5,6 +5,7 @@ namespace App\Data; use http\Url; class Team { + private int $id; private string $name; private Url $picture; private Color $mainColor; @@ -22,7 +23,8 @@ class Team { * @param Color $secondColor * @param array $members */ - public function __construct(string $name, Url $picture, Color $mainColor, Color $secondColor, array $members =[]) { + public function __construct(int $id,string $name, Url $picture, Color $mainColor, Color $secondColor, array $members =[]) { + $this->id = $id; $this->name = $name; $this->picture = $picture; $this->mainColor = $mainColor; @@ -30,6 +32,13 @@ class Team { $this->members = $members; } + /** + * @return int + */ + public function getId(): int { + return $this->id; + } + /** * @return string */ diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php index 7c5883c..7d9c014 100644 --- a/src/Gateway/TeamGateway.php +++ b/src/Gateway/TeamGateway.php @@ -3,11 +3,9 @@ namespace App\Gateway; use App\Connexion; -use App\Data\Color; use PDO; -class TeamGateway -{ +class TeamGateway { private Connexion $con; public function __construct(Connexion $con) { @@ -16,7 +14,7 @@ class TeamGateway public function insert(string $name, string $picture, int $mainColor, int $secondColor) { $this->con->exec( - "INSERT INTO Team VALUES (:teamName , :picture, :mainColor, :secondColor)", + "INSERT INTO Team(name, picture, mainColor, secondColor) VALUES (:teamName , :picture, :mainColor, :secondColor)", [ ":teamName" => [$name, PDO::PARAM_STR], ":picture" => [$picture, PDO::PARAM_STR], @@ -28,11 +26,29 @@ class TeamGateway public function listByName(string $name): array { return $this->con->fetch( - "SELECT name,picture,mainColor,secondColor FROM Team WHERE name LIKE '%:match%' ", + "SELECT id,name,picture,mainColor,secondColor FROM Team WHERE name LIKE '%' || :name || '%'", [ - ":match" => [$name, PDO::PARAM_STR] + ":name" => [$name, PDO::PARAM_STR] ] ); } + public function getTeamById(int $id): array{ + return $this->con->fetch( + "SELECT name,picture,mainColor,secondColor FROM Team WHERE id = :id", + [ + ":id" => [$id, PDO::PARAM_INT] + ] + ); + } + + public function getMembersById($id):array{ + return $this->con->fetch( + "SELECT p.role,m.email FROM Member m,Team t,Participate p WHERE t.id = :id AND p.idTeam = t.id AND p.idMember = m.id", + [ + ":id" => [$id, PDO::PARAM_INT] + ] + ); + } + } \ No newline at end of file diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index 02eef70..30c9fde 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -9,16 +9,6 @@ use App\Data\Team; */ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) pour le controller qui l'utilise, catch celle de la gw, */ { - - /** - * Attributes used to handle errors - */ - public const ERROR_INVALID_COLOR = 0; - public const ERROR_INVALID_NAME = 1; - public const ERROR_INVALID_PICTURE = 2; - public const ERROR_INVALID_SEARCH = 3; - public const ERROR_NO_DATA_FOUND = 4; - private TeamGateway $gateway; /** @@ -31,16 +21,20 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po public function createTeam(string $name,string $picture,int $mainColor, int $secondColor) { $this->gateway->insert($name,$picture,$mainColor,$secondColor); - } public function listByName(string $name):array { $teams=[]; $results = $this->gateway->listByName($name); - foreach ($results as $row){ - $teams[] = new Team($row['name'],$row['picture'],$row['mainColor'],$row['secondColor']); + $teams[] = new Team($row['id'],$row['name'],$row['picture'],$row['mainColor'],$row['secondColor']); } return $teams; } + + public function displayTeam(int $id): array{ + $resultTeam = $this->gateway->getTeamById($id); + $resultMembers = $this->gateway->getMembersById($id); + + } } \ No newline at end of file diff --git a/src/Views/display_team.html.twig b/src/Views/display_team.html.twig new file mode 100644 index 0000000..a1f88fc --- /dev/null +++ b/src/Views/display_team.html.twig @@ -0,0 +1,35 @@ + + + + + Twig view + + + +

Hello world

+ +{% if teams is empty %} +

Aucune équipe n'a été trouvée

+
+

Chercher une équipe

+
+
+ + +
+
+ +
+
+
+{% else %} + {% for t in teams %} +
+

Nom de l'équipe: {{ t.name }}

+

picture : {{ t.picture }}

+
+ {% endfor %} +{% endif %} + + + \ No newline at end of file diff --git a/src/Views/display_teams.html.twig b/src/Views/display_teams.html.twig new file mode 100644 index 0000000..a1f88fc --- /dev/null +++ b/src/Views/display_teams.html.twig @@ -0,0 +1,35 @@ + + + + + Twig view + + + +

Hello world

+ +{% if teams is empty %} +

Aucune équipe n'a été trouvée

+
+

Chercher une équipe

+
+
+ + +
+
+ +
+
+
+{% else %} + {% for t in teams %} +
+

Nom de l'équipe: {{ t.name }}

+

picture : {{ t.picture }}

+
+ {% endfor %} +{% endif %} + + + \ No newline at end of file diff --git a/src/Views/list_team_by_name.html.twig b/src/Views/list_team_by_name.html.twig index 7e0cbb3..1d9ddf3 100644 --- a/src/Views/list_team_by_name.html.twig +++ b/src/Views/list_team_by_name.html.twig @@ -61,7 +61,7 @@
-

Créer une équipe

+

Chercher une équipe

From 1acede41693ad7769df3247d7ba85ce00dfc38b9 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Thu, 16 Nov 2023 13:17:46 +0100 Subject: [PATCH 08/27] WIP did a few little changes --- src/Controller/TeamController.php | 3 ++- src/Data/Member.php | 13 ++++++++++- src/Gateway/TeamGateway.php | 2 +- src/Model/TeamModel.php | 37 +++++++++++++++++++++---------- src/Views/display_team.html.twig | 28 +++++------------------ src/Views/display_teams.html.twig | 2 -- 6 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index 8228d6a..ffbcb8e 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -74,7 +74,8 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ } public function displayTeam(int $id): HttpResponse{ - $results = $this->model->displayTeam($id); + $result = $this->model->displayTeam($id); + return ViewHttpResponse::twig('display_team.html.twig',['team' => $result]); } } diff --git a/src/Data/Member.php b/src/Data/Member.php index 91b09c4..9bf0b52 100755 --- a/src/Data/Member.php +++ b/src/Data/Member.php @@ -11,6 +11,7 @@ class Member { */ private int $userId; + private string $email; /** * @var MemberRole the member's role */ @@ -18,13 +19,16 @@ class Member { /** * @param int $userId + * @param string $email * @param MemberRole $role */ - public function __construct(int $userId, MemberRole $role) { + public function __construct(int $userId, string $email, MemberRole $role) { $this->userId = $userId; + $this->email = $email; $this->role = $role; } + /** * @return int */ @@ -39,4 +43,11 @@ class Member { return $this->role; } + /** + * @return string + */ + public function getEmail(): string { + return $this->email; + } + } \ No newline at end of file diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php index 7d9c014..1540c73 100644 --- a/src/Gateway/TeamGateway.php +++ b/src/Gateway/TeamGateway.php @@ -44,7 +44,7 @@ class TeamGateway { public function getMembersById($id):array{ return $this->con->fetch( - "SELECT p.role,m.email FROM Member m,Team t,Participate p WHERE t.id = :id AND p.idTeam = t.id AND p.idMember = m.id", + "SELECT p.role,m.email,m.id FROM Member m,Team t,Participate p WHERE t.id = :id AND p.idTeam = t.id AND p.idMember = m.id", [ ":id" => [$id, PDO::PARAM_INT] ] diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index 30c9fde..8c5c8f5 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -1,8 +1,11 @@ gateway = $gateway; } - public function createTeam(string $name,string $picture,int $mainColor, int $secondColor) { - $this->gateway->insert($name,$picture,$mainColor,$secondColor); + public function createTeam(string $name, string $picture, int $mainColor, int $secondColor) { + $this->gateway->insert($name, $picture, $mainColor, $secondColor); } - public function listByName(string $name):array { - $teams=[]; + public function listByName(string $name): array { + $teams = []; $results = $this->gateway->listByName($name); - foreach ($results as $row){ - $teams[] = new Team($row['id'],$row['name'],$row['picture'],$row['mainColor'],$row['secondColor']); + foreach ($results as $row) { + $teams[] = new Team($row['id'], $row['name'], $row['picture'], $row['mainColor'], $row['secondColor']); } return $teams; } - public function displayTeam(int $id): array{ - $resultTeam = $this->gateway->getTeamById($id); + public function displayTeam(int $id): Team { + $members = []; + $result = $this->gateway->getTeamById($id); $resultMembers = $this->gateway->getMembersById($id); - + foreach ($resultMembers as $row) { + if ($row['role'] == 'C') { + $role = 1; + } else { + $role = 2; + } + $members[] = new Member($row['id'], $row['email'], $role); + } + $team = new Team($result['id'], $result['name'], $result['picture'], $result['mainColor'], $result['secondColor'], $members); + return $team; } + } \ No newline at end of file diff --git a/src/Views/display_team.html.twig b/src/Views/display_team.html.twig index a1f88fc..d06322f 100644 --- a/src/Views/display_team.html.twig +++ b/src/Views/display_team.html.twig @@ -6,30 +6,12 @@ -

Hello world

+

{{ team.name }}

-{% if teams is empty %} -

Aucune équipe n'a été trouvée

-
-

Chercher une équipe

- -
- - -
-
- -
- -
-{% else %} - {% for t in teams %} -
-

Nom de l'équipe: {{ t.name }}

-

picture : {{ t.picture }}

-
- {% endfor %} -{% endif %} + +{% for m in team.members %} +

m.email

+{% endfor %} \ No newline at end of file diff --git a/src/Views/display_teams.html.twig b/src/Views/display_teams.html.twig index a1f88fc..d0c7c22 100644 --- a/src/Views/display_teams.html.twig +++ b/src/Views/display_teams.html.twig @@ -6,8 +6,6 @@ -

Hello world

- {% if teams is empty %}

Aucune équipe n'a été trouvée

From 9bc03c099b33a3b4849f6318f6620425e9d48eaf Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Sun, 19 Nov 2023 22:42:30 +0100 Subject: [PATCH 09/27] WIP did a few little changes, add colorpicker for the creation of the team, still have to make changes in regard to it --- sql/setup-tables.sql | 16 +++++++++------- src/Controller/TeamController.php | 15 +++++++++------ src/Data/Member.php | 13 +------------ src/Gateway/TeamGateway.php | 7 +++++++ src/Model/TeamModel.php | 12 +++++++----- src/Views/display_teams.html.twig | 4 ++-- src/Views/insert_team.html.twig | 4 ++-- 7 files changed, 37 insertions(+), 34 deletions(-) diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index 437649c..c2e8ec4 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -1,6 +1,9 @@ -- drop tables here DROP TABLE IF EXISTS FormEntries; DROP TABLE IF EXISTS TacticInfo; +DROP TABLE IF EXISTS Team; +DROP TABLE IF EXISTS Member; +DROP TABLE IF EXISTS Participate; CREATE TABLE FormEntries(name varchar, description varchar); @@ -12,15 +15,14 @@ CREATE TABLE Team( secondColor numeric ); -CREATE TABLE Participate( - idTeam numeric FOREIGN KEY REFERENCES Team(id), - idMember numeric FOREIGN KEY REFERENCES Member(id), - role char CHECK (role IN ('C','P')) +CREATE TABLE Member( + id integer PRIMARY KEY AUTOINCREMENT ); -CREATE TABLE Member( - id integer PRIMARY KEY AUTOINCREMENT, - email varchar, +CREATE TABLE Participate( + idTeam integer FOREIGN KEY REFERENCES Team(id), + idMember integer FOREIGN KEY REFERENCES Member(id), + role char CHECK (role IN ('C','P')) ); CREATE TABLE TacticInfo( diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index ffbcb8e..e2f0b89 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -9,9 +9,6 @@ use App\Model\TeamModel; use App\Validation\FieldValidationFail; use App\Validation\Validators; use \Twig\Environment; -use Twig\Error\LoaderError; -use Twig\Error\RuntimeError; -use Twig\Error\SyntaxError; class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ { @@ -32,7 +29,9 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ } public function submitTeam(array $request): HttpResponse { - + $request['mainColor'] = intval($request['mainColor']); + $request['secondColor'] = intval($request['secondColor']); + var_dump($request['secondColor']); $errors = []; $request = HttpRequest::from($request, $errors, [ "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], @@ -49,8 +48,8 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ } return ViewHttpResponse::twig('insert_team.html.twig', ['bad_fields' => $badFields]); } - $this->model->createTeam($request['name'], $request['picture'], intval($request['mainColor']), intval($request['secondColor'])); - return ViewHttpResponse::twig('sample_form.html.twig', []); /*todo appeler une vue qui display la team au lieu de ça*/ + $id = $this->model->createTeam($request['name'], $request['picture'], $request['mainColor'], $request['secondColor']); + return $this->displayTeam($id); } public function displayListTeamByName(): HttpResponse { @@ -62,14 +61,18 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ $request = HttpRequest::from($request, $errors, [ "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()] ]); + if (!empty($errors) && $errors[0] instanceof FieldValidationFail) { $badField = $errors[0]->getFieldName(); return ViewHttpResponse::twig('list_team_by_name.html.twig', ['bad_field' => $badField]); } + $results = $this->model->listByName($request['name']); + if (empty($results)) { return ViewHttpResponse::twig('display_teams.html.twig', []); } + return ViewHttpResponse::twig('display_teams.html.twig', ['teams' => $results]); } diff --git a/src/Data/Member.php b/src/Data/Member.php index 9bf0b52..bfdb7a8 100755 --- a/src/Data/Member.php +++ b/src/Data/Member.php @@ -11,7 +11,6 @@ class Member { */ private int $userId; - private string $email; /** * @var MemberRole the member's role */ @@ -19,12 +18,10 @@ class Member { /** * @param int $userId - * @param string $email * @param MemberRole $role */ - public function __construct(int $userId, string $email, MemberRole $role) { + public function __construct(int $userId, MemberRole $role) { $this->userId = $userId; - $this->email = $email; $this->role = $role; } @@ -42,12 +39,4 @@ class Member { public function getRole(): MemberRole { return $this->role; } - - /** - * @return string - */ - public function getEmail(): string { - return $this->email; - } - } \ No newline at end of file diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php index 1540c73..0f8fa74 100644 --- a/src/Gateway/TeamGateway.php +++ b/src/Gateway/TeamGateway.php @@ -24,6 +24,13 @@ class TeamGateway { ); } + public function getIdOfLastInsertion(): array{ + return $this->con->fetch( + "SELECT last_insert_rowid() as id", + [] + ); + } + public function listByName(string $name): array { return $this->con->fetch( "SELECT id,name,picture,mainColor,secondColor FROM Team WHERE name LIKE '%' || :name || '%'", diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index 8c5c8f5..17b02dd 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -6,6 +6,7 @@ use App\Gateway\TeamGateway; use App\Data\Team; use App\Data\Member; use App\Data\MemberRole; +use http\Url; /** * @@ -21,15 +22,17 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po $this->gateway = $gateway; } - public function createTeam(string $name, string $picture, int $mainColor, int $secondColor) { + public function createTeam(string $name, string $picture, int $mainColor, int $secondColor): int{ $this->gateway->insert($name, $picture, $mainColor, $secondColor); + return (int) $this->gateway->getIdOfLastInsertion()['id']; } public function listByName(string $name): array { $teams = []; $results = $this->gateway->listByName($name); foreach ($results as $row) { - $teams[] = new Team($row['id'], $row['name'], $row['picture'], $row['mainColor'], $row['secondColor']); + $url = new Url($row['picture']); + $teams[] = new Team($row['id'], $row['name'], $url, $row['mainColor'], $row['secondColor']); } return $teams; } @@ -44,10 +47,9 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po } else { $role = 2; } - $members[] = new Member($row['id'], $row['email'], $role); + $members[] = new Member($row['id'], $role); } - $team = new Team($result['id'], $result['name'], $result['picture'], $result['mainColor'], $result['secondColor'], $members); - return $team; + return new Team($result['id'], $result['name'], $result['picture'], $result['mainColor'], $result['secondColor'], $members); } } \ No newline at end of file diff --git a/src/Views/display_teams.html.twig b/src/Views/display_teams.html.twig index d0c7c22..e4941e4 100644 --- a/src/Views/display_teams.html.twig +++ b/src/Views/display_teams.html.twig @@ -22,8 +22,8 @@
{% else %} {% for t in teams %} -
-

Nom de l'équipe: {{ t.name }}

+
+

Nom de l'équipe : {{ t.name }}

picture : {{ t.picture }}

{% endfor %} diff --git a/src/Views/insert_team.html.twig b/src/Views/insert_team.html.twig index ba6ffac..0cc85dd 100644 --- a/src/Views/insert_team.html.twig +++ b/src/Views/insert_team.html.twig @@ -71,9 +71,9 @@ - + - +
From 46862bb7cd91b3e3e060cfb1f5f06d0749dd721b Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Mon, 20 Nov 2023 09:53:53 +0100 Subject: [PATCH 10/27] WIP fixed some bugs, still got two regarding /team/id and /team/list --- sql/setup-tables.sql | 8 +++++--- src/Controller/TeamController.php | 14 +++++++------- src/Data/MemberRole.php | 13 +++++++++++-- src/Gateway/TeamGateway.php | 16 +++++++++------- src/Model/TeamModel.php | 7 ++++--- 5 files changed, 36 insertions(+), 22 deletions(-) diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index c2e8ec4..1366a87 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -20,9 +20,11 @@ CREATE TABLE Member( ); CREATE TABLE Participate( - idTeam integer FOREIGN KEY REFERENCES Team(id), - idMember integer FOREIGN KEY REFERENCES Member(id), - role char CHECK (role IN ('C','P')) + idTeam integer, + idMember integer, + role char(1) CHECK (role IN ('C','P')), + FOREIGN KEY (idTeam) REFERENCES Team(id), + FOREIGN KEY (idMember) REFERENCES Member(id) ); CREATE TABLE TacticInfo( diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index e2f0b89..30a6ab6 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -29,10 +29,11 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ } public function submitTeam(array $request): HttpResponse { - $request['mainColor'] = intval($request['mainColor']); - $request['secondColor'] = intval($request['secondColor']); - var_dump($request['secondColor']); + $errors = []; + $request['mainColor'] = hexdec($request['mainColor']); + $request['secondColor'] = hexdec($request['secondColor']); + $request = HttpRequest::from($request, $errors, [ "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], "mainColor" => [Validators::isInteger(), Validators::isIntInRange(0, 0xffffff)], @@ -48,8 +49,7 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ } return ViewHttpResponse::twig('insert_team.html.twig', ['bad_fields' => $badFields]); } - $id = $this->model->createTeam($request['name'], $request['picture'], $request['mainColor'], $request['secondColor']); - return $this->displayTeam($id); + return $this->displayTeam($this->model->createTeam($request['name'], $request['picture'], $request['mainColor'], $request['secondColor'])); } public function displayListTeamByName(): HttpResponse { @@ -76,9 +76,9 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ return ViewHttpResponse::twig('display_teams.html.twig', ['teams' => $results]); } - public function displayTeam(int $id): HttpResponse{ + public function displayTeam(int $id): HttpResponse { $result = $this->model->displayTeam($id); - return ViewHttpResponse::twig('display_team.html.twig',['team' => $result]); + return ViewHttpResponse::twig('display_team.html.twig', ['team' => $result]); } } diff --git a/src/Data/MemberRole.php b/src/Data/MemberRole.php index 05d746d..bd8add3 100755 --- a/src/Data/MemberRole.php +++ b/src/Data/MemberRole.php @@ -11,8 +11,9 @@ use http\Exception\InvalidArgumentException; * encapsulates an integer value and use it as an enumeration discriminant */ final class MemberRole { - private const ROLE_PLAYER = 0; - private const ROLE_COACH = 1; + + public const ROLE_PLAYER = 0; + public const ROLE_COACH = 1; private const MIN = self::ROLE_PLAYER; private const MAX = self::ROLE_COACH; @@ -25,6 +26,14 @@ final class MemberRole { $this->value = $val; } + public static function player(){ + return new MemberRole(MemberRole::ROLE_PLAYER); + } + + public static function coach(){ + return new MemberRole(MemberRole::ROLE_COACH); + } + private function isValid(int $val): bool { return ($val <= self::MAX and $val >= self::MIN); } diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php index 0f8fa74..4bb1518 100644 --- a/src/Gateway/TeamGateway.php +++ b/src/Gateway/TeamGateway.php @@ -24,13 +24,6 @@ class TeamGateway { ); } - public function getIdOfLastInsertion(): array{ - return $this->con->fetch( - "SELECT last_insert_rowid() as id", - [] - ); - } - public function listByName(string $name): array { return $this->con->fetch( "SELECT id,name,picture,mainColor,secondColor FROM Team WHERE name LIKE '%' || :name || '%'", @@ -49,6 +42,15 @@ class TeamGateway { ); } + public function getIdTeamByName(string $name): array{ + return $this->con->fetch( + "SELECT id FROM Team WHERE name = :name", + [ + ":name" => [$name, PDO::PARAM_STR] + ] + ); + } + public function getMembersById($id):array{ return $this->con->fetch( "SELECT p.role,m.email,m.id FROM Member m,Team t,Participate p WHERE t.id = :id AND p.idTeam = t.id AND p.idMember = m.id", diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index 17b02dd..1dd57a0 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -24,7 +24,8 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po public function createTeam(string $name, string $picture, int $mainColor, int $secondColor): int{ $this->gateway->insert($name, $picture, $mainColor, $secondColor); - return (int) $this->gateway->getIdOfLastInsertion()['id']; + $result = $this->gateway->getIdTeamByName($name); + return intval($result[0]['id']); } public function listByName(string $name): array { @@ -43,9 +44,9 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po $resultMembers = $this->gateway->getMembersById($id); foreach ($resultMembers as $row) { if ($row['role'] == 'C') { - $role = 1; + $role = MemberRole::coach(); } else { - $role = 2; + $role = MemberRole::player(); } $members[] = new Member($row['id'], $role); } From d824f17ea75f277d3a70b90c21eaf839c8dc861d Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Tue, 21 Nov 2023 11:36:25 +0100 Subject: [PATCH 11/27] every bugs has been fixed --- .gitignore | 2 +- sql/setup-tables.sql | 12 ++++++------ src/Controller/TeamController.php | 6 ++---- src/Data/Color.php | 22 +++++++++++----------- src/Data/Team.php | 12 +++++------- src/Gateway/TeamGateway.php | 10 +++++----- src/Model/TeamModel.php | 17 ++++++----------- src/Validation/Validators.php | 1 - src/Views/display_team.html.twig | 19 +++++++++++++++++-- src/Views/display_teams.html.twig | 2 +- 10 files changed, 54 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index 9124809..b116c4f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ .vite vendor - +.nfs* composer.lock *.phar /dist diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index 1366a87..4b9f10b 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -2,8 +2,8 @@ DROP TABLE IF EXISTS FormEntries; DROP TABLE IF EXISTS TacticInfo; DROP TABLE IF EXISTS Team; +DROP TABLE IF EXISTS User; DROP TABLE IF EXISTS Member; -DROP TABLE IF EXISTS Participate; CREATE TABLE FormEntries(name varchar, description varchar); @@ -11,20 +11,20 @@ CREATE TABLE Team( id integer PRIMARY KEY AUTOINCREMENT, name varchar, picture varchar, - mainColor numeric, - secondColor numeric + mainColor varchar, + secondColor varchar ); -CREATE TABLE Member( +CREATE TABLE User( id integer PRIMARY KEY AUTOINCREMENT ); -CREATE TABLE Participate( +CREATE TABLE Member( idTeam integer, idMember integer, role char(1) CHECK (role IN ('C','P')), FOREIGN KEY (idTeam) REFERENCES Team(id), - FOREIGN KEY (idMember) REFERENCES Member(id) + FOREIGN KEY (idMember) REFERENCES User(id) ); CREATE TABLE TacticInfo( diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index 30a6ab6..fa418c5 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -31,13 +31,11 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ public function submitTeam(array $request): HttpResponse { $errors = []; - $request['mainColor'] = hexdec($request['mainColor']); - $request['secondColor'] = hexdec($request['secondColor']); $request = HttpRequest::from($request, $errors, [ "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], - "mainColor" => [Validators::isInteger(), Validators::isIntInRange(0, 0xffffff)], - "secondColor" => [Validators::isInteger(), Validators::isIntInRange(0, 0xffffff)], + "mainColor" => [Validators::regex('/#(?:[0-9a-fA-F]{6})/')], + "secondColor" => [Validators::regex('/#(?:[0-9a-fA-F]{6})/')], "picture" => [Validators::isURL()] ]); if (!empty($errors)) { diff --git a/src/Data/Color.php b/src/Data/Color.php index bc9043b..2e934c0 100755 --- a/src/Data/Color.php +++ b/src/Data/Color.php @@ -2,39 +2,39 @@ namespace App\Data; -use http\Exception\InvalidArgumentException; +use \InvalidArgumentException; class Color { /** - * @var int 6 bytes unsigned int that represents an RGB color + * @var string 6 bytes unsigned int that represents an RGB color */ - private int $value; + private string $hex; /** - * @param int $value 6 bytes unsigned int that represents an RGB color + * @param string $value 6 bytes unsigned int that represents an RGB color * @throws \InvalidArgumentException if the value is negative or greater than 0xFFFFFF */ - private function __constructor(int $value) { + private function __construct(string $value) { $this->value = $value; } /** - * @return int + * @return string */ - public function getValue(): int { + public function getValue(): string { return $this->value; } - public static function from(int $value): Color { + public static function from(string $value): Color { $color = self::tryFrom($value); if ($color == null) { - throw new InvalidArgumentException("int color value is invalid, must be positive and lower than 0xFFFFFF"); + throw new InvalidArgumentException("The string is not an hexadecimal code"); } return $color; } - public static function tryFrom(int $value): ?Color { - if ($value < 0 || $value > 0xFFFFFF) { + public static function tryFrom(string $value): ?Color { + if (!preg_match('/#(?:[0-9a-fA-F]{6})/',$value)) { return null; } return new Color($value); diff --git a/src/Data/Team.php b/src/Data/Team.php index 321a5d9..4c80dc6 100755 --- a/src/Data/Team.php +++ b/src/Data/Team.php @@ -2,12 +2,10 @@ namespace App\Data; -use http\Url; - class Team { private int $id; private string $name; - private Url $picture; + private string $picture; private Color $mainColor; private Color $secondColor; @@ -18,12 +16,12 @@ class Team { /** * @param string $name - * @param Url $picture + * @param string $picture * @param Color $mainColor * @param Color $secondColor * @param array $members */ - public function __construct(int $id,string $name, Url $picture, Color $mainColor, Color $secondColor, array $members =[]) { + public function __construct(int $id,string $name, string $picture, Color $mainColor, Color $secondColor, array $members =[]) { $this->id = $id; $this->name = $name; $this->picture = $picture; @@ -47,9 +45,9 @@ class Team { } /** - * @return Url + * @return string */ - public function getPicture(): Url { + public function getPicture(): string { return $this->picture; } diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php index 4bb1518..1cecbe1 100644 --- a/src/Gateway/TeamGateway.php +++ b/src/Gateway/TeamGateway.php @@ -12,14 +12,14 @@ class TeamGateway { $this->con = $con; } - public function insert(string $name, string $picture, int $mainColor, int $secondColor) { + public function insert(string $name, string $picture, string $mainColor, string $secondColor) { $this->con->exec( "INSERT INTO Team(name, picture, mainColor, secondColor) VALUES (:teamName , :picture, :mainColor, :secondColor)", [ ":teamName" => [$name, PDO::PARAM_STR], ":picture" => [$picture, PDO::PARAM_STR], - ":mainColor" => [$mainColor, PDO::PARAM_INT], - ":secondColor" => [$secondColor, PDO::PARAM_INT] + ":mainColor" => [$mainColor, PDO::PARAM_STR], + ":secondColor" => [$secondColor, PDO::PARAM_STR] ] ); } @@ -35,7 +35,7 @@ class TeamGateway { public function getTeamById(int $id): array{ return $this->con->fetch( - "SELECT name,picture,mainColor,secondColor FROM Team WHERE id = :id", + "SELECT id,name,picture,mainColor,secondColor FROM Team WHERE id = :id", [ ":id" => [$id, PDO::PARAM_INT] ] @@ -53,7 +53,7 @@ class TeamGateway { public function getMembersById($id):array{ return $this->con->fetch( - "SELECT p.role,m.email,m.id FROM Member m,Team t,Participate p WHERE t.id = :id AND p.idTeam = t.id AND p.idMember = m.id", + "SELECT m.role,u.id FROM User u,Team t,Member m WHERE t.id = :id AND m.idTeam = t.id AND m.idMember = u.id", [ ":id" => [$id, PDO::PARAM_INT] ] diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index 1dd57a0..c5521eb 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -6,12 +6,9 @@ use App\Gateway\TeamGateway; use App\Data\Team; use App\Data\Member; use App\Data\MemberRole; -use http\Url; +use App\Data\Color; -/** - * - */ -class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) pour le controller qui l'utilise, catch celle de la gw, */ +class TeamModel { private TeamGateway $gateway; @@ -22,7 +19,7 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po $this->gateway = $gateway; } - public function createTeam(string $name, string $picture, int $mainColor, int $secondColor): int{ + public function createTeam(string $name, string $picture, string $mainColor, string $secondColor): int { $this->gateway->insert($name, $picture, $mainColor, $secondColor); $result = $this->gateway->getIdTeamByName($name); return intval($result[0]['id']); @@ -32,15 +29,14 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po $teams = []; $results = $this->gateway->listByName($name); foreach ($results as $row) { - $url = new Url($row['picture']); - $teams[] = new Team($row['id'], $row['name'], $url, $row['mainColor'], $row['secondColor']); + $teams[] = new Team($row['id'], $row['name'], $row['picture'], Color::from($row['mainColor']), Color::from($row['secondColor'])); } return $teams; } public function displayTeam(int $id): Team { $members = []; - $result = $this->gateway->getTeamById($id); + $result = $this->gateway->getTeamById($id)[0]; $resultMembers = $this->gateway->getMembersById($id); foreach ($resultMembers as $row) { if ($row['role'] == 'C') { @@ -50,7 +46,6 @@ class TeamModel /* throw des exceptions(ex validation des champs, filtre etc) po } $members[] = new Member($row['id'], $role); } - return new Team($result['id'], $result['name'], $result['picture'], $result['mainColor'], $result['secondColor'], $members); + return new Team(intval($result['id']), $result['name'], $result['picture'], Color::from($result['mainColor']), Color::from($result['secondColor']), $members); } - } \ No newline at end of file diff --git a/src/Validation/Validators.php b/src/Validation/Validators.php index 2f69ca4..a36bbef 100644 --- a/src/Validation/Validators.php +++ b/src/Validation/Validators.php @@ -69,5 +69,4 @@ class Validators { fn(string $name) => [new FieldValidationFail($name, "The value is not an URL")] ); } - } \ No newline at end of file diff --git a/src/Views/display_team.html.twig b/src/Views/display_team.html.twig index d06322f..e3b0fe0 100644 --- a/src/Views/display_team.html.twig +++ b/src/Views/display_team.html.twig @@ -3,14 +3,29 @@ Twig view +

{{ team.name }}

- +Logo d'équipe +
+
{% for m in team.members %} -

m.email

+

m.id

{% endfor %} diff --git a/src/Views/display_teams.html.twig b/src/Views/display_teams.html.twig index e4941e4..c0ac185 100644 --- a/src/Views/display_teams.html.twig +++ b/src/Views/display_teams.html.twig @@ -24,7 +24,7 @@ {% for t in teams %}

Nom de l'équipe : {{ t.name }}

-

picture : {{ t.picture }}

+ logo de l'équipe
{% endfor %} {% endif %} From 53584a518e44200be3f47bfc99cfc6191fab300f Mon Sep 17 00:00:00 2001 From: Override-6 Date: Tue, 21 Nov 2023 22:57:36 +0100 Subject: [PATCH 12/27] add salva-like routes and controller architecture --- src/Controller/ErrorController.php | 26 -------- src/Controller/FrontController.php | 2 +- src/Controller/SampleFormController.php | 64 ------------------- src/Controller/{ => Sub}/AuthController.php | 4 +- src/Controller/{ => Sub}/EditorController.php | 4 +- .../{ => Sub}/VisualizerController.php | 5 +- src/Controller/UserController.php | 36 ++++++++++- src/Model/AuthModel.php | 1 - 8 files changed, 40 insertions(+), 102 deletions(-) delete mode 100644 src/Controller/ErrorController.php delete mode 100644 src/Controller/SampleFormController.php rename src/Controller/{ => Sub}/AuthController.php (97%) rename src/Controller/{ => Sub}/EditorController.php (93%) rename src/Controller/{ => Sub}/VisualizerController.php (88%) diff --git a/src/Controller/ErrorController.php b/src/Controller/ErrorController.php deleted file mode 100644 index 7fc5239..0000000 --- a/src/Controller/ErrorController.php +++ /dev/null @@ -1,26 +0,0 @@ -display("error.html.twig", ['failures' => $failures]); - } catch (LoaderError|RuntimeError|SyntaxError $e) { - echo "Twig error: $e"; - } - } -} diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php index 66d5d4f..0600a01 100644 --- a/src/Controller/FrontController.php +++ b/src/Controller/FrontController.php @@ -56,7 +56,7 @@ class FrontController { private function initializeRouterMap(): void { $this->router->map("GET", "/", "UserController"); $this->router->map("GET", "/[a:action]?", "UserController"); - $this->router->map("GET", "/tactic/[a:action]/[i:idTactic]?", "EditorController"); + $this->router->map("GET", "/tactic/[a:action]/[i:idTactic]?", "UserController"); } /** diff --git a/src/Controller/SampleFormController.php b/src/Controller/SampleFormController.php deleted file mode 100644 index 773a4db..0000000 --- a/src/Controller/SampleFormController.php +++ /dev/null @@ -1,64 +0,0 @@ -gateway = $gateway; - } - - - public function displayFormReact(): HttpResponse { - return ViewHttpResponse::react("views/SampleForm.tsx", []); - } - - public function displayFormTwig(): HttpResponse { - return ViewHttpResponse::twig('sample_form.html.twig', []); - } - - /** - * @param array $form - * @param callable(array>): ViewHttpResponse $response - * @return HttpResponse - */ - private function submitForm(array $form, callable $response): HttpResponse { - return Control::runCheckedFrom($form, [ - "name" => [Validators::lenBetween(0, 32), Validators::name("Le nom ne peut contenir que des lettres, des chiffres et des accents")], - "description" => [Validators::lenBetween(0, 512)], - ], function (HttpRequest $req) use ($response) { - $description = htmlspecialchars($req["description"]); - $this->gateway->insert($req["name"], $description); - $results = ["results" => $this->gateway->listResults()]; - return call_user_func_array($response, [$results]); - }, false); - } - - /** - * @param array $form - * @return HttpResponse - */ - public function submitFormTwig(array $form): HttpResponse { - return $this->submitForm($form, fn(array $results) => ViewHttpResponse::twig('display_results.html.twig', $results)); - } - - /** - * @param array $form - * @return HttpResponse - */ - public function submitFormReact(array $form): HttpResponse { - return $this->submitForm($form, fn(array $results) => ViewHttpResponse::react('views/DisplayResults.tsx', $results)); - } -} diff --git a/src/Controller/AuthController.php b/src/Controller/Sub/AuthController.php similarity index 97% rename from src/Controller/AuthController.php rename to src/Controller/Sub/AuthController.php index e42c27d..a78b02e 100644 --- a/src/Controller/AuthController.php +++ b/src/Controller/Sub/AuthController.php @@ -1,8 +1,7 @@ $tactic->getName(), "id" => $tactic->getId()]); } - public function create(): HttpResponse { + public function createNew(): HttpResponse { $tactic = $this->model->makeNewDefault(); return $this->openEditor($tactic); } diff --git a/src/Controller/VisualizerController.php b/src/Controller/Sub/VisualizerController.php similarity index 88% rename from src/Controller/VisualizerController.php rename to src/Controller/Sub/VisualizerController.php index 3c8b55e..e3b5663 100644 --- a/src/Controller/VisualizerController.php +++ b/src/Controller/Sub/VisualizerController.php @@ -1,6 +1,6 @@ tacticModel = $tacticModel; } - public function openVisualizer(int $id): HttpResponse { + public function visualize(int $id): HttpResponse { $tactic = $this->tacticModel->get($id); if ($tactic == null) { @@ -27,6 +27,5 @@ class VisualizerController { } return ViewHttpResponse::react("views/Visualizer.tsx", ["name" => $tactic->getName()]); - } } diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 930cd67..2a95bcc 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -2,15 +2,47 @@ namespace App\Controller; +use App\Connexion; +use App\Gateway\AuthGateway; +use App\Gateway\TacticInfoGateway; use App\Http\HttpResponse; use App\Http\ViewHttpResponse; +use App\Model\AuthModel; +use App\Model\TacticModel; class UserController { public function home(): HttpResponse { return ViewHttpResponse::twig("home.twig", []); } - public function default(): HttpResponse { - return self::home(); + public function register(): HttpResponse { + $model = new AuthModel(new AuthGateway(new Connexion(get_database()))); + if ($_SERVER['REQUEST_METHOD'] === 'GET') { + return (new Sub\AuthController($model))->displayRegister(); + } + return (new Sub\AuthController($model))->confirmRegister($_POST); + } + + public function login(): HttpResponse { + $model = new AuthModel(new AuthGateway(new Connexion(get_database()))); + if ($_SERVER['REQUEST_METHOD'] === 'GET') { + return (new Sub\AuthController($model))->displayLogin(); + } + return (new Sub\AuthController($model))->confirmLogin($_POST); + } + + public function open(int $id): HttpResponse { + $model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); + return (new Sub\VisualizerController($model))->visualize($id); + } + + public function edit(int $id): HttpResponse { + $model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); + return (new Sub\EditorController($model))->edit($id); + } + + public function create(): HttpResponse { + $model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); + return (new Sub\EditorController($model))->createNew(); } } diff --git a/src/Model/AuthModel.php b/src/Model/AuthModel.php index 45b63e4..312cca8 100644 --- a/src/Model/AuthModel.php +++ b/src/Model/AuthModel.php @@ -2,7 +2,6 @@ namespace App\Model; -use App\Controller\AuthController; use App\Gateway\AuthGateway; use App\Validation\FieldValidationFail; use App\Validation\ValidationFail; From 14612226cd1f9680776ad2de295a6e0afbc88b8f Mon Sep 17 00:00:00 2001 From: "vivien.dufour" Date: Wed, 22 Nov 2023 08:38:58 +0100 Subject: [PATCH 13/27] fix on routes --- src/Controller/FrontController.php | 49 ++++++++++++++----------- src/Controller/Sub/EditorController.php | 4 +- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php index 0600a01..58e5d9d 100644 --- a/src/Controller/FrontController.php +++ b/src/Controller/FrontController.php @@ -14,10 +14,12 @@ use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; use Twig\Loader\FilesystemLoader; -class FrontController { +class FrontController +{ private AltoRouter $router; - public function __construct(string $basePath) { + public function __construct(string $basePath) + { $this->router = $this->createRouter($basePath); $this->initializeRouterMap(); } @@ -27,7 +29,8 @@ class FrontController { * * @return void */ - public function run(): void { + public function run(): void + { $match = $this->router->match(); if ($match != null) { $this->handleMatch($match); @@ -42,7 +45,8 @@ class FrontController { * @param string $basePath * @return AltoRouter */ - public function createRouter(string $basePath): AltoRouter { + public function createRouter(string $basePath): AltoRouter + { $router = new AltoRouter(); $router->setBasePath($basePath); return $router; @@ -53,17 +57,19 @@ class FrontController { * * @return void */ - private function initializeRouterMap(): void { + private function initializeRouterMap(): void + { $this->router->map("GET", "/", "UserController"); - $this->router->map("GET", "/[a:action]?", "UserController"); - $this->router->map("GET", "/tactic/[a:action]/[i:idTactic]?", "UserController"); + $this->router->map("GET|POST", "/[a:action]?", "UserController"); + $this->router->map("GET|POST", "/tactic/[a:action]/[i:idTactic]?", "UserController"); } /** * @param array $match * @return void */ - private function handleMatch(array $match): void { + private function handleMatch(array $match): void + { $tag = $match['target']; $action = $this->getAction($match); @@ -78,15 +84,12 @@ class FrontController { * @param array $params * @return HttpResponse */ - private function tryToCall(string $controller, string $action, array $params): HttpResponse { + private function tryToCall(string $controller, string $action, array $params): HttpResponse + { $controller = $this->getController($controller); - try { - if (is_callable([$controller, $action])) { - return call_user_func_array([$controller, $action], $params); - } else { - return ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND); - } - } catch (Exception $e) { + if (is_callable([$controller, $action])) { + return call_user_func_array([$controller, $action], $params); + } else { return ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND); } } @@ -97,7 +100,8 @@ class FrontController { * @param array $match * @return string */ - private function getAction(array $match): string { + private function getAction(array $match): string + { if (isset($match["params"]["action"])) { return $match["params"]["action"]; } @@ -110,7 +114,8 @@ class FrontController { * @param string $controller * @return mixed */ - private function getController(string $controller) { + private function getController(string $controller) + { $namespace = "\\App\\Controller\\"; $controller = $namespace . $controller; return new $controller(); @@ -122,7 +127,8 @@ class FrontController { * @param HttpResponse $response * @return void */ - private function handleResponseByType(HttpResponse $response): void { + private function handleResponseByType(HttpResponse $response): void + { http_response_code($response->getCode()); if ($response instanceof ViewHttpResponse) { $this->displayViewByKind($response); @@ -138,7 +144,8 @@ class FrontController { * @param ViewHttpResponse $response * @return void */ - private function displayViewByKind(ViewHttpResponse $response): void { + private function displayViewByKind(ViewHttpResponse $response): void + { $file = $response->getFile(); $args = $response->getArguments(); @@ -151,7 +158,7 @@ class FrontController { $loader = new FilesystemLoader('../src/Views/'); $twig = new Environment($loader); $twig->display($file, $args); - } catch (RuntimeError|SyntaxError|LoaderError $e) { + } catch (RuntimeError | SyntaxError | LoaderError $e) { http_response_code(500); echo "There was an error rendering your view, please refer to an administrator.\nlogs date: " . date("YYYD, d M Y H:i:s"); throw $e; diff --git a/src/Controller/Sub/EditorController.php b/src/Controller/Sub/EditorController.php index def6ac9..ce7a0a3 100644 --- a/src/Controller/Sub/EditorController.php +++ b/src/Controller/Sub/EditorController.php @@ -14,8 +14,8 @@ use App\Model\TacticModel; class EditorController { private TacticModel $model; - public function __construct() { - $this->model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); + public function __construct(TacticModel $model) { + $this->model = $model; } private function openEditor(TacticInfo $tactic): HttpResponse { From 3d7eb7bbb10eb1a0012ed531dc19f760addf7af0 Mon Sep 17 00:00:00 2001 From: Override-6 Date: Tue, 21 Nov 2023 19:40:30 +0100 Subject: [PATCH 14/27] add session handle --- Documentation/models.puml | 8 +-- public/index.php | 5 +- sql/setup-tables.sql | 24 +++++---- src/Controller/FrontController.php | 52 +++++++++---------- src/Controller/Sub/AuthController.php | 24 ++++----- src/Controller/UserController.php | 6 +-- src/Data/Account.php | 73 ++++----------------------- src/Gateway/AccountGateway.php | 56 ++++++++++++++++++++ src/Gateway/AuthGateway.php | 47 ----------------- src/Model/AuthModel.php | 66 ++++++++++++------------ src/Session/MutableSessionHandle.php | 9 ++++ src/Session/PhpSessionHandle.php | 24 +++++++++ src/Session/SessionHandle.php | 11 ++++ 13 files changed, 206 insertions(+), 199 deletions(-) create mode 100644 src/Gateway/AccountGateway.php delete mode 100644 src/Gateway/AuthGateway.php create mode 100644 src/Session/MutableSessionHandle.php create mode 100644 src/Session/PhpSessionHandle.php create mode 100644 src/Session/SessionHandle.php diff --git a/Documentation/models.puml b/Documentation/models.puml index d95343c..82037bc 100755 --- a/Documentation/models.puml +++ b/Documentation/models.puml @@ -81,7 +81,7 @@ AuthController --> "- model" AuthModel class AuthModel{ + register(username : string, password : string, confirmPassword : string, email : string): array - + getUserFields(email : string):array + + getAccount(email : string):array + login(email : string, password : string) } AuthModel --> "- gateway" AuthGateway @@ -89,9 +89,9 @@ AuthModel --> "- gateway" AuthGateway class AuthGateway{ -con : Connection - + mailExist(email : string) : bool + + mailExists(email : string) : bool + insertAccount(username : string, hash : string, email : string) - + getUserHash(email : string):string - + getUserFields (email : string): array + + getHash(email : string):string + + getAccount (email : string): array } @enduml \ No newline at end of file diff --git a/public/index.php b/public/index.php index f898d7f..ce1df33 100644 --- a/public/index.php +++ b/public/index.php @@ -1,5 +1,6 @@ run(); + +$frontController->run(PhpSessionHandle::init()); diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index 108b62a..a289336 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -1,18 +1,24 @@ - -- drop tables here DROP TABLE IF EXISTS FormEntries; -DROP TABLE IF EXISTS AccountUser; +DROP TABLE IF EXISTS Account; DROP TABLE IF EXISTS TacticInfo; -CREATE TABLE FormEntries(name varchar, description varchar); -CREATE TABLE AccountUser( +CREATE TABLE FormEntries +( + name varchar, + description varchar +); +CREATE TABLE Account +( username varchar, - hash varchar, - email varchar unique + hash varchar, + email varchar unique, + token varchar(256) NOT NULL UNIQUE ); -CREATE TABLE TacticInfo( - id integer PRIMARY KEY AUTOINCREMENT, - name varchar, +CREATE TABLE TacticInfo +( + id integer PRIMARY KEY AUTOINCREMENT, + name varchar, creation_date timestamp DEFAULT CURRENT_TIMESTAMP ); \ No newline at end of file diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php index 58e5d9d..d5c6417 100644 --- a/src/Controller/FrontController.php +++ b/src/Controller/FrontController.php @@ -7,6 +7,7 @@ use App\Http\HttpCodes; use App\Http\HttpResponse; use App\Http\JsonHttpResponse; use App\Http\ViewHttpResponse; +use App\Session\MutableSessionHandle; use Exception; use Twig\Environment; use Twig\Error\LoaderError; @@ -14,12 +15,10 @@ use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; use Twig\Loader\FilesystemLoader; -class FrontController -{ +class FrontController { private AltoRouter $router; - public function __construct(string $basePath) - { + public function __construct(string $basePath) { $this->router = $this->createRouter($basePath); $this->initializeRouterMap(); } @@ -29,11 +28,10 @@ class FrontController * * @return void */ - public function run(): void - { + public function run(MutableSessionHandle $session): void { $match = $this->router->match(); if ($match != null) { - $this->handleMatch($match); + $this->handleMatch($match, $session); } else { $this->displayViewByKind(ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND)); } @@ -45,8 +43,7 @@ class FrontController * @param string $basePath * @return AltoRouter */ - public function createRouter(string $basePath): AltoRouter - { + public function createRouter(string $basePath): AltoRouter { $router = new AltoRouter(); $router->setBasePath($basePath); return $router; @@ -57,8 +54,7 @@ class FrontController * * @return void */ - private function initializeRouterMap(): void - { + private function initializeRouterMap(): void { $this->router->map("GET", "/", "UserController"); $this->router->map("GET|POST", "/[a:action]?", "UserController"); $this->router->map("GET|POST", "/tactic/[a:action]/[i:idTactic]?", "UserController"); @@ -68,28 +64,34 @@ class FrontController * @param array $match * @return void */ - private function handleMatch(array $match): void - { + private function handleMatch(array $match, MutableSessionHandle $session): void { $tag = $match['target']; $action = $this->getAction($match); $params = $match["params"]; unset($params['action']); - $this->handleResponseByType($this->tryToCall($tag, $action, array_values($params))); + $this->handleResponseByType($this->tryToCall($tag, $action, array_values($params), $session)); } /** * @param string $controller * @param string $action * @param array $params + * @param MutableSessionHandle $session * @return HttpResponse */ - private function tryToCall(string $controller, string $action, array $params): HttpResponse - { + + private function tryToCall(string $controller, string $action, array $params, MutableSessionHandle $session): HttpResponse { $controller = $this->getController($controller); - if (is_callable([$controller, $action])) { - return call_user_func_array([$controller, $action], $params); - } else { + try { + if (is_callable([$controller, $action])) { + // append the session as the last parameter of a controller function + $params[] = $session; + return call_user_func_array([$controller, $action], $params); + } else { + return ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND); + } + } catch (Exception $e) { return ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND); } } @@ -100,8 +102,7 @@ class FrontController * @param array $match * @return string */ - private function getAction(array $match): string - { + private function getAction(array $match): string { if (isset($match["params"]["action"])) { return $match["params"]["action"]; } @@ -114,8 +115,7 @@ class FrontController * @param string $controller * @return mixed */ - private function getController(string $controller) - { + private function getController(string $controller) { $namespace = "\\App\\Controller\\"; $controller = $namespace . $controller; return new $controller(); @@ -127,8 +127,7 @@ class FrontController * @param HttpResponse $response * @return void */ - private function handleResponseByType(HttpResponse $response): void - { + private function handleResponseByType(HttpResponse $response): void { http_response_code($response->getCode()); if ($response instanceof ViewHttpResponse) { $this->displayViewByKind($response); @@ -144,8 +143,7 @@ class FrontController * @param ViewHttpResponse $response * @return void */ - private function displayViewByKind(ViewHttpResponse $response): void - { + private function displayViewByKind(ViewHttpResponse $response): void { $file = $response->getFile(); $args = $response->getArguments(); diff --git a/src/Controller/Sub/AuthController.php b/src/Controller/Sub/AuthController.php index a78b02e..7a537f2 100644 --- a/src/Controller/Sub/AuthController.php +++ b/src/Controller/Sub/AuthController.php @@ -2,6 +2,7 @@ namespace App\Controller\Sub; +use App\Gateway\AccountGateway; use App\Http\HttpRequest; use App\Http\HttpResponse; use App\Http\ViewHttpResponse; @@ -49,17 +50,16 @@ class AuthController { "username" => [Validators::name(), Validators::lenBetween(2, 32)], "password" => [Validators::lenBetween(6, 256)], "confirmpassword" => [Validators::lenBetween(6, 256)], - "email" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/"),Validators::lenBetween(5, 256)], + "email" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/"), Validators::lenBetween(5, 256)], ]); if (!empty($fails)) { return $this->displayBadFields("display_register.html.twig", $fails); } - $fails = $this->model->register($request['username'], $request["password"], $request['confirmpassword'], $request['email']); - if (empty($fails)) { - $results = $this->model->getUserFields($request['email']); - return ViewHttpResponse::twig("display_auth_confirm.html.twig", ['username' => $results['username'], 'email' => $results['email']]); + $account = $this->model->register($request['username'], $request["password"], $request['confirmpassword'], $request['email'], $fails); + if (!empty($fails)) { + return $this->displayBadFields("display_register.html.twig", $fails); } - return $this->displayBadFields("display_register.html.twig", $fails); + return ViewHttpResponse::twig("display_auth_confirm.html.twig", ['username' => $account->getName(), 'email' => $account->getEmail()]); } @@ -75,18 +75,18 @@ class AuthController { $fails = []; $request = HttpRequest::from($request, $fails, [ "password" => [Validators::lenBetween(6, 256)], - "email" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/"),Validators::lenBetween(5, 256)], + "email" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/"), Validators::lenBetween(5, 256)], ]); if (!empty($fails)) { return $this->displayBadFields("display_login.html.twig", $fails); } - $fails = $this->model->login($request['email'], $request['password']); - if (empty($fails)) { - $results = $this->model->getUserFields($request['email']); - return ViewHttpResponse::twig("display_auth_confirm.html.twig", ['username' => $results['username'], 'email' => $results['email']]); + $account = $this->model->login($request['email'], $request['password'], $fails); + if (!empty($fails)) { + return $this->displayBadFields("display_login.html.twig", $fails); } - return $this->displayBadFields("display_login.html.twig", $fails); + + return ViewHttpResponse::twig("display_auth_confirm.html.twig", ['username' => $account->getName(), 'email' => $account->getEmail()]); } } diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 2a95bcc..360c9a3 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -3,7 +3,7 @@ namespace App\Controller; use App\Connexion; -use App\Gateway\AuthGateway; +use App\Gateway\AccountGateway; use App\Gateway\TacticInfoGateway; use App\Http\HttpResponse; use App\Http\ViewHttpResponse; @@ -16,7 +16,7 @@ class UserController { } public function register(): HttpResponse { - $model = new AuthModel(new AuthGateway(new Connexion(get_database()))); + $model = new AuthModel(new AccountGateway(new Connexion(get_database()))); if ($_SERVER['REQUEST_METHOD'] === 'GET') { return (new Sub\AuthController($model))->displayRegister(); } @@ -24,7 +24,7 @@ class UserController { } public function login(): HttpResponse { - $model = new AuthModel(new AuthGateway(new Connexion(get_database()))); + $model = new AuthModel(new AccountGateway(new Connexion(get_database()))); if ($_SERVER['REQUEST_METHOD'] === 'GET') { return (new Sub\AuthController($model))->displayLogin(); } diff --git a/src/Data/Account.php b/src/Data/Account.php index 2a21bf1..8b58e1c 100755 --- a/src/Data/Account.php +++ b/src/Data/Account.php @@ -4,8 +4,6 @@ namespace App\Data; use http\Exception\InvalidArgumentException; -const PHONE_NUMBER_REGEXP = "/^\\+[0-9]+$/"; - /** * Base class of a user account. * Contains the private information that we don't want @@ -16,27 +14,16 @@ class Account { * @var string $email account's mail address */ private string $email; - /** - * @var string account's phone number. - * its format is specified by the {@link PHONE_NUMBER_REGEXP} constant - * - */ - private string $phoneNumber; /** - * @var AccountUser account's public and shared information + * @var string string token */ - private AccountUser $user; + private string $token; /** - * @var Team[] account's teams + * @var string the account's username */ - private array $teams; - - /** - * @var int account's unique identifier - */ - private int $id; + private string $name; /** @@ -46,60 +33,22 @@ class Account { * @param Team[] $teams * @param int $id */ - public function __construct(string $email, string $phoneNumber, AccountUser $user, array $teams, int $id) { + public function __construct(string $email, string $name, string $token) { $this->email = $email; - $this->phoneNumber = $phoneNumber; - $this->user = $user; - $this->teams = $teams; - $this->id = $id; + $this->name = $name; + $this->token = $token; } - /** - * @return string - */ public function getEmail(): string { return $this->email; } - /** - * @param string $email - */ - public function setEmail(string $email): void { - if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { - throw new InvalidArgumentException("Invalid mail address"); - } - $this->email = $email; - } - - /** - * @return string - */ - public function getPhoneNumber(): string { - return $this->phoneNumber; + public function getToken(): string { + return $this->token; } - /** - * @param string $phoneNumber - */ - public function setPhoneNumber(string $phoneNumber): void { - if (!preg_match(PHONE_NUMBER_REGEXP, $phoneNumber)) { - throw new InvalidArgumentException("Invalid phone number"); - } - $this->phoneNumber = $phoneNumber; + public function getName(): string { + return $this->name; } - public function getId(): int { - return $this->id; - } - - /** - * @return Team[] - */ - public function getTeams(): array { - return $this->teams; - } - - public function getUser(): AccountUser { - return $this->user; - } } diff --git a/src/Gateway/AccountGateway.php b/src/Gateway/AccountGateway.php new file mode 100644 index 0000000..a4686d6 --- /dev/null +++ b/src/Gateway/AccountGateway.php @@ -0,0 +1,56 @@ +con = $con; + } + + + public function exists(string $email): bool { + return $this->getAccount($email) != null; + } + + + public function insertAccount(Account $account, string $hash): void { + $this->con->exec("INSERT INTO Account VALUES (:username,:hash,:email)", [ + ':username' => [$account->getName(), PDO::PARAM_STR], + ':hash' => [$hash, PDO::PARAM_STR], + ':email' => [$account->getEmail(), PDO::PARAM_STR], + ':token' => [$account->getToken(), PDO::PARAM_STR] + ]); + } + + public function getHash(string $email): string { + $results = $this->con->fetch("SELECT hash FROM Account WHERE email = :email", [ + ':email' => [$email, PDO::PARAM_STR] + ]); + return $results[0]['hash']; + } + + + /** + * @param string $email + * @return Account|null + */ + public function getAccount(string $email): ?Account { + $results = $this->con->fetch("SELECT username,email,token FROM Account WHERE email = :email", [':email' => [$email, PDO::PARAM_STR]]); + if (empty($results)) + return null; + + $acc = $results[0]; + return new Account($acc["email"], $acc["name"], $acc["token"]); + } + + +} diff --git a/src/Gateway/AuthGateway.php b/src/Gateway/AuthGateway.php deleted file mode 100644 index 5acc01c..0000000 --- a/src/Gateway/AuthGateway.php +++ /dev/null @@ -1,47 +0,0 @@ -con = $con; - } - - - public function mailExist(string $email): bool { - return $this->getUserFields($email) != null; - } - - - public function insertAccount(string $username, string $hash, string $email): void { - $this->con->exec("INSERT INTO AccountUser VALUES (:username,:hash,:email)", [':username' => [$username, PDO::PARAM_STR],':hash' => [$hash, PDO::PARAM_STR],':email' => [$email, PDO::PARAM_STR]]); - } - - public function getUserHash(string $email): string { - $results = $this->con->fetch("SELECT hash FROM AccountUser WHERE email = :email", [':email' => [$email, PDO::PARAM_STR]]); - return $results[0]['hash']; - } - - - /** - * @param string $email - * @return array|null - */ - public function getUserFields(string $email): ?array { - $results = $this->con->fetch("SELECT username,email FROM AccountUser WHERE email = :email", [':email' => [$email, PDO::PARAM_STR]]); - $firstRow = $results[0] ?? null; - return $firstRow; - } - - - - -} diff --git a/src/Model/AuthModel.php b/src/Model/AuthModel.php index 312cca8..b786c2e 100644 --- a/src/Model/AuthModel.php +++ b/src/Model/AuthModel.php @@ -2,16 +2,19 @@ namespace App\Model; -use App\Gateway\AuthGateway; +use App\Controller\AuthController; +use App\Data\Account; +use App\Gateway\AccountGateway; use App\Validation\FieldValidationFail; use App\Validation\ValidationFail; class AuthModel { - private AuthGateway $gateway; + private AccountGateway $gateway; + /** - * @param AuthGateway $gateway + * @param AccountGateway $gateway */ - public function __construct(AuthGateway $gateway) { + public function __construct(AccountGateway $gateway) { $this->gateway = $gateway; } @@ -21,59 +24,54 @@ class AuthModel { * @param string $password * @param string $confirmPassword * @param string $email - * @return ValidationFail[] + * @param ValidationFail[] $failures + * @return Account|null the registered account or null if failures occurred */ - public function register(string $username, string $password, string $confirmPassword, string $email): array { - $errors = []; + public function register(string $username, string $password, string $confirmPassword, string $email, array &$failures): ?Account { if ($password != $confirmPassword) { - $errors[] = new FieldValidationFail("confirmpassword", "password and password confirmation are not equals"); + $failures[] = new FieldValidationFail("confirmpassword", "password and password confirmation are not equals"); } - if ($this->gateway->mailExist($email)) { - $errors[] = new FieldValidationFail("email", "email already exist"); + if ($this->gateway->exists($email)) { + $failures[] = new FieldValidationFail("email", "email already exist"); } - if(empty($errors)) { - $hash = password_hash($password, PASSWORD_DEFAULT); - $this->gateway->insertAccount($username, $hash, $email); + if (!empty($errors)) { + return null; } - return $errors; - } + $hash = password_hash($password, PASSWORD_DEFAULT); - /** - * @param string $email - * @return array|null - */ - public function getUserFields(string $email): ?array { - return $this->gateway->getUserFields($email); + $account = new Account($email, $username, $this->generateToken()); + $this->gateway->insertAccount($account, $hash); + return $account; } + private function generateToken(): string { + return base64_encode(random_bytes(64)); + } /** * @param string $email * @param string $password - * @return ValidationFail[] $errors + * @param ValidationFail[] $failures + * @return Account|null the authenticated account or null if failures occurred */ - public function login(string $email, string $password): array { - $errors = []; - - if (!$this->gateway->mailExist($email)) { - $errors[] = new FieldValidationFail("email", "email doesnt exists"); - return $errors; + public function login(string $email, string $password, array &$failures): ?Account { + if (!$this->gateway->exists($email)) { + $failures = new FieldValidationFail("email", "email doesnt exists"); + return null; } - $hash = $this->gateway->getUserHash($email); + $hash = $this->gateway->getHash($email); if (!password_verify($password, $hash)) { - $errors[] = new FieldValidationFail("password", "invalid password"); + $failures = new FieldValidationFail("password", "invalid password"); + return null; } - return $errors; + return $this->gateway->getAccount($email); } - - - } diff --git a/src/Session/MutableSessionHandle.php b/src/Session/MutableSessionHandle.php new file mode 100644 index 0000000..a7822a3 --- /dev/null +++ b/src/Session/MutableSessionHandle.php @@ -0,0 +1,9 @@ + Date: Tue, 21 Nov 2023 23:24:20 +0100 Subject: [PATCH 15/27] Updated documentation + started to add some documentation --- Documentation/team.puml | 24 ++++----- src/Controller/TeamController.php | 4 +- src/Views/display_team.html.twig | 84 ++++++++++++++++++++----------- 3 files changed, 69 insertions(+), 43 deletions(-) diff --git a/Documentation/team.puml b/Documentation/team.puml index b8374cd..a291b1d 100644 --- a/Documentation/team.puml +++ b/Documentation/team.puml @@ -12,12 +12,14 @@ class Team { } Team --> "- mainColor" Color -Team --> "- secondaryColor" Color +Team --> "- secondColor" Color class Color { - - value: int - - + getValue(): int + - value: string + - __construct(value : string) + + getValue(): string + + from(value: string): Color + + tryFrom(value : string) : ?Color } class TeamGateway{ @@ -31,15 +33,11 @@ TeamGateway *--"- con" Connexion TeamGateway ..> Color class TeamModel{ - + ERROR_INVALID_COLOR : int {readOnly} - + ERROR_INVALID_NAME : int {readOnly} - + ERROR_INVALID_PICTURE : int {readOnly} - + ERROR_INVALID_SEARCH : int {readOnly} - + ERROR_NO_DATA_FOUND : int {readOnly} --- + __construct(gateway : TeamGateway) + createTeam(name : string,picture : string, mainColorValue : int, secondColorValue : int, errors : array) + listByName(name : string ,errors : array) : ?array + + displayTeam(id : int): Team } TeamModel *--"- gateway" TeamGateway @@ -50,8 +48,11 @@ class TeamController{ - twig : Environement -- + __construct( model : TeamModel, twig : Environement) - + submitTeam(request : array) - + listTeamByName(request : array) + + displaySubmitTeam() : HttpResponse + + submitTeam(request : array) : HttpResponse + + displayListTeamByName(): HttpResponse + + listTeamByName(request : array) : HttpResponse + + displayTeam(id : int): HttpResponse } TeamController *--"- model" TeamModel @@ -62,7 +63,6 @@ class Connexion{ + __constructor(pdo : PDO) + exec(query : string, args : array) + fetch(query string, args array): array - } @enduml \ No newline at end of file diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index fa418c5..288e733 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -10,7 +10,7 @@ use App\Validation\FieldValidationFail; use App\Validation\Validators; use \Twig\Environment; -class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ +class TeamController { private TeamModel $model; private Environment $twig; @@ -24,7 +24,7 @@ class TeamController /* verif si les camp sont assignés, sinon erreur 400*/ $this->twig = $twig; } - public function displaySubmitTeam() { + public function displaySubmitTeam() : HttpResponse { return ViewHttpResponse::twig("insert_team.html.twig", []); } diff --git a/src/Views/display_team.html.twig b/src/Views/display_team.html.twig index e3b0fe0..f7bb02b 100644 --- a/src/Views/display_team.html.twig +++ b/src/Views/display_team.html.twig @@ -1,32 +1,58 @@ - - - Twig view - - - - -

{{ team.name }}

-Logo d'équipe -
-
- -{% for m in team.members %} -

m.id

-{% endfor %} - - + + + Twig view + + + +
+

IQBall

+
+ +
+ +
+

{{ team.name }}

+ Logo d'équipe +
+
+ + {% for m in team.members %} +

m.id

+ {% endfor %} +
+ +
+ \ No newline at end of file From 982acf5e09bba4aa8ae91965c55ce1fa534ee7ea Mon Sep 17 00:00:00 2001 From: Override-6 Date: Wed, 22 Nov 2023 01:58:50 +0100 Subject: [PATCH 16/27] integrate sessions for tactics --- sql/.guard | 0 sql/setup-tables.sql | 22 ++++--- src/Controller/FrontController.php | 66 ++++++++++++++------- src/Controller/Sub/AuthController.php | 11 +++- src/Controller/Sub/EditorController.php | 18 ++++-- src/Controller/Sub/VisualizerController.php | 11 +++- src/Controller/UserController.php | 33 +++-------- src/Controller/VisitorController.php | 30 ++++++++++ src/Data/Account.php | 17 ++++-- src/Data/TacticInfo.php | 13 +++- src/Gateway/AccountGateway.php | 15 ++--- src/Gateway/FormResultGateway.php | 35 ----------- src/Gateway/TacticInfoGateway.php | 15 +++-- src/Http/HttpCodes.php | 4 ++ src/Http/HttpResponse.php | 25 +++++++- src/Http/JsonHttpResponse.php | 2 +- src/Http/ViewHttpResponse.php | 2 +- src/Model/AuthModel.php | 13 ++-- src/Model/TacticModel.php | 8 +-- src/Session/MutableSessionHandle.php | 1 + src/Validation/ValidationFail.php | 2 +- src/Validator/TacticValidator.php | 22 +++++++ src/Views/error.html.twig | 2 +- 23 files changed, 231 insertions(+), 136 deletions(-) delete mode 100644 sql/.guard create mode 100644 src/Controller/VisitorController.php delete mode 100644 src/Gateway/FormResultGateway.php create mode 100644 src/Validator/TacticValidator.php diff --git a/sql/.guard b/sql/.guard deleted file mode 100644 index e69de29..0000000 diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index a289336..457803c 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -5,20 +5,24 @@ DROP TABLE IF EXISTS TacticInfo; CREATE TABLE FormEntries ( - name varchar, - description varchar + name varchar NOT NULL, + description varchar NOT NULL ); CREATE TABLE Account ( - username varchar, - hash varchar, - email varchar unique, - token varchar(256) NOT NULL UNIQUE + id integer PRIMARY KEY AUTOINCREMENT, + email varchar UNIQUE NOT NULL, + username varchar NOT NULL, + token varchar UNIQUE NOT NULL, + hash varchar NOT NULL ); CREATE TABLE TacticInfo ( id integer PRIMARY KEY AUTOINCREMENT, - name varchar, - creation_date timestamp DEFAULT CURRENT_TIMESTAMP -); \ No newline at end of file + name varchar NOT NULL, + creation_date timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL, + owner integer NOT NULL, + FOREIGN KEY (owner) REFERENCES Account +); + diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php index d5c6417..3d73d80 100644 --- a/src/Controller/FrontController.php +++ b/src/Controller/FrontController.php @@ -8,7 +8,7 @@ use App\Http\HttpResponse; use App\Http\JsonHttpResponse; use App\Http\ViewHttpResponse; use App\Session\MutableSessionHandle; -use Exception; +use App\Validation\ValidationFail; use Twig\Environment; use Twig\Error\LoaderError; use Twig\Error\RuntimeError; @@ -17,23 +17,33 @@ use Twig\Loader\FilesystemLoader; class FrontController { private AltoRouter $router; + private string $basePath; + + private const USER_CONTROLLER = "UserController"; + private const VISITOR_CONTROLLER = "VisitorController"; + public function __construct(string $basePath) { $this->router = $this->createRouter($basePath); $this->initializeRouterMap(); + $this->basePath = $basePath; } /** - * Main behavior of the FrontController - * + * @param MutableSessionHandle $session * @return void + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError */ public function run(MutableSessionHandle $session): void { $match = $this->router->match(); - if ($match != null) { + if ($match) { $this->handleMatch($match, $session); } else { - $this->displayViewByKind(ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND)); + $this->displayViewByKind(ViewHttpResponse::twig("error.html.twig", [ + 'failures' => [ValidationFail::notFound("Could not find page ${_SERVER['REQUEST_URI']}.")] + ], HttpCodes::NOT_FOUND)); } } @@ -55,13 +65,14 @@ class FrontController { * @return void */ private function initializeRouterMap(): void { - $this->router->map("GET", "/", "UserController"); - $this->router->map("GET|POST", "/[a:action]?", "UserController"); - $this->router->map("GET|POST", "/tactic/[a:action]/[i:idTactic]?", "UserController"); + $this->router->map("GET", "/home", self::USER_CONTROLLER); + $this->router->map("GET|POST", "/user/[a:action]/[i:idTactic]?", self::USER_CONTROLLER); + $this->router->map("GET|POST", "/visitor/[a:action]", self::VISITOR_CONTROLLER); } /** * @param array $match + * @param MutableSessionHandle $session * @return void */ private function handleMatch(array $match, MutableSessionHandle $session): void { @@ -73,26 +84,32 @@ class FrontController { $this->handleResponseByType($this->tryToCall($tag, $action, array_values($params), $session)); } + /** - * @param string $controller + * @param string $controllerName * @param string $action * @param array $params * @param MutableSessionHandle $session * @return HttpResponse */ - - private function tryToCall(string $controller, string $action, array $params, MutableSessionHandle $session): HttpResponse { - $controller = $this->getController($controller); - try { - if (is_callable([$controller, $action])) { - // append the session as the last parameter of a controller function - $params[] = $session; - return call_user_func_array([$controller, $action], $params); - } else { - return ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND); + private function tryToCall(string $controllerName, string $action, array $params, MutableSessionHandle $session): HttpResponse { + if ($controllerName != self::VISITOR_CONTROLLER) { + $account = $session->getAccount(); + if ($account == null) { + return HttpResponse::redirect($this->basePath . "/visitor/login"); } - } catch (Exception $e) { - return ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND); + } + + $controller = $this->getController($controllerName); + + if (is_callable([$controller, $action])) { + // append the session as the last parameter of a controller function + $params[] = $session; + return call_user_func_array([$controller, $action], $params); + } else { + return ViewHttpResponse::twig("error.html.twig", [ + 'failures' => [ValidationFail::notFound("Could not find page ${_SERVER['REQUEST_URI']}.")] + ], HttpCodes::NOT_FOUND); } } @@ -106,7 +123,7 @@ class FrontController { if (isset($match["params"]["action"])) { return $match["params"]["action"]; } - return "default"; + return "home"; } /** @@ -129,6 +146,11 @@ class FrontController { */ private function handleResponseByType(HttpResponse $response): void { http_response_code($response->getCode()); + + foreach ($response->getHeaders() as $header => $value) { + header("$header: $value"); + } + if ($response instanceof ViewHttpResponse) { $this->displayViewByKind($response); } elseif ($response instanceof JsonHttpResponse) { diff --git a/src/Controller/Sub/AuthController.php b/src/Controller/Sub/AuthController.php index 7a537f2..fc72f67 100644 --- a/src/Controller/Sub/AuthController.php +++ b/src/Controller/Sub/AuthController.php @@ -7,6 +7,7 @@ use App\Http\HttpRequest; use App\Http\HttpResponse; use App\Http\ViewHttpResponse; use App\Model\AuthModel; +use App\Session\MutableSessionHandle; use App\Validation\FieldValidationFail; use App\Validation\ValidationFail; use App\Validation\Validators; @@ -42,9 +43,10 @@ class AuthController { /** * @param mixed[] $request + * @param MutableSessionHandle $session * @return HttpResponse */ - public function confirmRegister(array $request): HttpResponse { + public function confirmRegister(array $request, MutableSessionHandle $session): HttpResponse { $fails = []; $request = HttpRequest::from($request, $fails, [ "username" => [Validators::name(), Validators::lenBetween(2, 32)], @@ -59,6 +61,9 @@ class AuthController { if (!empty($fails)) { return $this->displayBadFields("display_register.html.twig", $fails); } + + $session->setAccount($account); + return ViewHttpResponse::twig("display_auth_confirm.html.twig", ['username' => $account->getName(), 'email' => $account->getEmail()]); } @@ -71,7 +76,7 @@ class AuthController { * @param mixed[] $request * @return HttpResponse */ - public function confirmLogin(array $request): HttpResponse { + public function confirmLogin(array $request, MutableSessionHandle $session): HttpResponse { $fails = []; $request = HttpRequest::from($request, $fails, [ "password" => [Validators::lenBetween(6, 256)], @@ -86,6 +91,8 @@ class AuthController { return $this->displayBadFields("display_login.html.twig", $fails); } + $session->setAccount($account); + return ViewHttpResponse::twig("display_auth_confirm.html.twig", ['username' => $account->getName(), 'email' => $account->getEmail()]); } diff --git a/src/Controller/Sub/EditorController.php b/src/Controller/Sub/EditorController.php index ce7a0a3..1188bc9 100644 --- a/src/Controller/Sub/EditorController.php +++ b/src/Controller/Sub/EditorController.php @@ -3,6 +3,7 @@ namespace App\Controller\Sub; use App\Connexion; +use App\Controller\VisitorController; use App\Data\TacticInfo; use App\Gateway\TacticInfoGateway; use App\Http\HttpCodes; @@ -10,6 +11,9 @@ use App\Http\HttpResponse; use App\Http\JsonHttpResponse; use App\Http\ViewHttpResponse; use App\Model\TacticModel; +use App\Session\SessionHandle; +use App\Validation\ValidationFail; +use App\Validator\TacticValidator; class EditorController { private TacticModel $model; @@ -22,24 +26,28 @@ class EditorController { return ViewHttpResponse::react("views/Editor.tsx", ["name" => $tactic->getName(), "id" => $tactic->getId()]); } - public function createNew(): HttpResponse { - $tactic = $this->model->makeNewDefault(); + public function createNew(SessionHandle $session): HttpResponse { + $tactic = $this->model->makeNewDefault($session->getAccount()->getId()); return $this->openEditor($tactic); } /** * returns an editor view for a given tactic * @param int $id the targeted tactic identifier + * @param SessionHandle $session * @return HttpResponse */ - public function edit(int $id): HttpResponse { + public function edit(int $id, SessionHandle $session): HttpResponse { $tactic = $this->model->get($id); - if ($tactic == null) { - return new JsonHttpResponse("la tactique " . $id . " n'existe pas", HttpCodes::NOT_FOUND); + $failure = TacticValidator::validateAccess($tactic, $session->getAccount()->getId()); + + if ($failure != null) { + return ViewHttpResponse::twig('error.html.twig', ['failures' => [$failure]], HttpCodes::NOT_FOUND); } return $this->openEditor($tactic); } + } diff --git a/src/Controller/Sub/VisualizerController.php b/src/Controller/Sub/VisualizerController.php index e3b5663..f70b18e 100644 --- a/src/Controller/Sub/VisualizerController.php +++ b/src/Controller/Sub/VisualizerController.php @@ -7,6 +7,9 @@ use App\Http\HttpResponse; use App\Http\JsonHttpResponse; use App\Http\ViewHttpResponse; use App\Model\TacticModel; +use App\Session\SessionHandle; +use App\Validation\ValidationFail; +use App\Validator\TacticValidator; class VisualizerController { private TacticModel $tacticModel; @@ -19,11 +22,13 @@ class VisualizerController { $this->tacticModel = $tacticModel; } - public function visualize(int $id): HttpResponse { + public function visualize(int $id, SessionHandle $session): HttpResponse { $tactic = $this->tacticModel->get($id); - if ($tactic == null) { - return new JsonHttpResponse("la tactique " . $id . " n'existe pas", HttpCodes::NOT_FOUND); + $failure = TacticValidator::validateAccess($tactic, $session->getAccount()->getId()); + + if ($failure != null) { + return ViewHttpResponse::twig('error.html.twig', ['failures' => [$failure]], HttpCodes::NOT_FOUND); } return ViewHttpResponse::react("views/Visualizer.tsx", ["name" => $tactic->getName()]); diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 360c9a3..64a0caa 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -3,46 +3,29 @@ namespace App\Controller; use App\Connexion; -use App\Gateway\AccountGateway; use App\Gateway\TacticInfoGateway; use App\Http\HttpResponse; use App\Http\ViewHttpResponse; -use App\Model\AuthModel; use App\Model\TacticModel; +use App\Session\SessionHandle; -class UserController { +class UserController extends VisitorController { public function home(): HttpResponse { return ViewHttpResponse::twig("home.twig", []); } - public function register(): HttpResponse { - $model = new AuthModel(new AccountGateway(new Connexion(get_database()))); - if ($_SERVER['REQUEST_METHOD'] === 'GET') { - return (new Sub\AuthController($model))->displayRegister(); - } - return (new Sub\AuthController($model))->confirmRegister($_POST); - } - - public function login(): HttpResponse { - $model = new AuthModel(new AccountGateway(new Connexion(get_database()))); - if ($_SERVER['REQUEST_METHOD'] === 'GET') { - return (new Sub\AuthController($model))->displayLogin(); - } - return (new Sub\AuthController($model))->confirmLogin($_POST); - } - - public function open(int $id): HttpResponse { + public function view(int $id, SessionHandle $session): HttpResponse { $model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); - return (new Sub\VisualizerController($model))->visualize($id); + return (new Sub\VisualizerController($model))->visualize($id, $session); } - public function edit(int $id): HttpResponse { + public function edit(int $id, SessionHandle $session): HttpResponse { $model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); - return (new Sub\EditorController($model))->edit($id); + return (new Sub\EditorController($model))->edit($id, $session); } - public function create(): HttpResponse { + public function create(SessionHandle $session): HttpResponse { $model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); - return (new Sub\EditorController($model))->createNew(); + return (new Sub\EditorController($model))->createNew($session); } } diff --git a/src/Controller/VisitorController.php b/src/Controller/VisitorController.php new file mode 100644 index 0000000..56e0ec2 --- /dev/null +++ b/src/Controller/VisitorController.php @@ -0,0 +1,30 @@ +displayRegister(); + } + return (new Sub\AuthController($model))->confirmRegister($_POST, $session); + } + + public final function login(MutableSessionHandle $session): HttpResponse { + $model = new AuthModel(new AccountGateway(new Connexion(get_database()))); + if ($_SERVER['REQUEST_METHOD'] === 'GET') { + return (new Sub\AuthController($model))->displayLogin(); + } + return (new Sub\AuthController($model))->confirmLogin($_POST, $session); + } + +} \ No newline at end of file diff --git a/src/Data/Account.php b/src/Data/Account.php index 8b58e1c..0ed4339 100755 --- a/src/Data/Account.php +++ b/src/Data/Account.php @@ -25,18 +25,27 @@ class Account { */ private string $name; + /** + * @var int + */ + private int $id; + /** * @param string $email - * @param string $phoneNumber - * @param AccountUser $user - * @param Team[] $teams + * @param string $name + * @param string $token * @param int $id */ - public function __construct(string $email, string $name, string $token) { + public function __construct(string $email, string $name, string $token, int $id) { $this->email = $email; $this->name = $name; $this->token = $token; + $this->id = $id; + } + + public function getId(): int { + return $this->id; } public function getEmail(): string { diff --git a/src/Data/TacticInfo.php b/src/Data/TacticInfo.php index eef7bb3..a2a576e 100644 --- a/src/Data/TacticInfo.php +++ b/src/Data/TacticInfo.php @@ -7,14 +7,18 @@ class TacticInfo implements \JsonSerializable { private string $name; private int $creation_date; + private int $ownerId; + /** * @param int $id * @param string $name * @param int $creation_date + * @param int $ownerId */ - public function __construct(int $id, string $name, int $creation_date) { + public function __construct(int $id, string $name, int $creation_date, int $ownerId) { $this->id = $id; $this->name = $name; + $this->ownerId = $ownerId; $this->creation_date = $creation_date; } @@ -26,6 +30,13 @@ class TacticInfo implements \JsonSerializable { return $this->name; } + /** + * @return int + */ + public function getOwnerId(): int { + return $this->ownerId; + } + public function getCreationTimestamp(): int { return $this->creation_date; } diff --git a/src/Gateway/AccountGateway.php b/src/Gateway/AccountGateway.php index a4686d6..8320f33 100644 --- a/src/Gateway/AccountGateway.php +++ b/src/Gateway/AccountGateway.php @@ -22,13 +22,14 @@ class AccountGateway { } - public function insertAccount(Account $account, string $hash): void { - $this->con->exec("INSERT INTO Account VALUES (:username,:hash,:email)", [ - ':username' => [$account->getName(), PDO::PARAM_STR], + public function insertAccount(string $name, string $email, string $token, string $hash): int { + $this->con->exec("INSERT INTO Account(username, hash, email, token) VALUES (:username,:hash,:email,:token)", [ + ':username' => [$name, PDO::PARAM_STR], ':hash' => [$hash, PDO::PARAM_STR], - ':email' => [$account->getEmail(), PDO::PARAM_STR], - ':token' => [$account->getToken(), PDO::PARAM_STR] + ':email' => [$email, PDO::PARAM_STR], + ':token' => [$token, PDO::PARAM_STR] ]); + return intval($this->con->lastInsertId()); } public function getHash(string $email): string { @@ -44,12 +45,12 @@ class AccountGateway { * @return Account|null */ public function getAccount(string $email): ?Account { - $results = $this->con->fetch("SELECT username,email,token FROM Account WHERE email = :email", [':email' => [$email, PDO::PARAM_STR]]); + $results = $this->con->fetch("SELECT * FROM Account WHERE email = :email", [':email' => [$email, PDO::PARAM_STR]]); if (empty($results)) return null; $acc = $results[0]; - return new Account($acc["email"], $acc["name"], $acc["token"]); + return new Account($acc["email"], $acc["username"], $acc["token"], $acc["id"]); } diff --git a/src/Gateway/FormResultGateway.php b/src/Gateway/FormResultGateway.php deleted file mode 100644 index 36178ad..0000000 --- a/src/Gateway/FormResultGateway.php +++ /dev/null @@ -1,35 +0,0 @@ -con = $con; - } - - - public function insert(string $username, string $description): void { - $this->con->exec( - "INSERT INTO FormEntries VALUES (:name, :description)", - [ - ":name" => [$username, PDO::PARAM_STR], - "description" => [$description, PDO::PARAM_STR], - ] - ); - } - - /** - * @return array - */ - public function listResults(): array { - return $this->con->fetch("SELECT * FROM FormEntries", []); - } -} diff --git a/src/Gateway/TacticInfoGateway.php b/src/Gateway/TacticInfoGateway.php index 3441c9a..6aa2cca 100644 --- a/src/Gateway/TacticInfoGateway.php +++ b/src/Gateway/TacticInfoGateway.php @@ -28,19 +28,22 @@ class TacticInfoGateway { $row = $res[0]; - return new TacticInfo($id, $row["name"], strtotime($row["creation_date"])); + return new TacticInfo($id, $row["name"], strtotime($row["creation_date"]), $row["owner"]); } - public function insert(string $name): TacticInfo { + public function insert(string $name, int $owner): TacticInfo { $this->con->exec( - "INSERT INTO TacticInfo(name) VALUES(:name)", - [":name" => [$name, PDO::PARAM_STR]] + "INSERT INTO TacticInfo(name, owner) VALUES(:name, :owner)", + [ + ":name" => [$name, PDO::PARAM_STR], + ":owner" => [$owner, PDO::PARAM_INT] + ] ); $row = $this->con->fetch( - "SELECT id, creation_date FROM TacticInfo WHERE :id = id", + "SELECT id, creation_date, owner FROM TacticInfo WHERE :id = id", [':id' => [$this->con->lastInsertId(), PDO::PARAM_INT]] )[0]; - return new TacticInfo(intval($row["id"]), $name, strtotime($row["creation_date"])); + return new TacticInfo(intval($row["id"]), $name, strtotime($row["creation_date"]), $row["owner"]); } public function updateName(int $id, string $name): void { diff --git a/src/Http/HttpCodes.php b/src/Http/HttpCodes.php index f9d550c..c2b01df 100644 --- a/src/Http/HttpCodes.php +++ b/src/Http/HttpCodes.php @@ -7,7 +7,11 @@ namespace App\Http; */ class HttpCodes { public const OK = 200; + public const FOUND = 302; public const BAD_REQUEST = 400; + + public const FORBIDDEN = 403; + public const NOT_FOUND = 404; } diff --git a/src/Http/HttpResponse.php b/src/Http/HttpResponse.php index 5d8c3bf..d6d6a9a 100644 --- a/src/Http/HttpResponse.php +++ b/src/Http/HttpResponse.php @@ -3,21 +3,42 @@ namespace App\Http; class HttpResponse { + + /** + * @var array + */ + private array $headers; private int $code; /** * @param int $code + * @param array $headers */ - public function __construct(int $code) { + public function __construct(int $code, array $headers) { $this->code = $code; + $this->headers = $headers; } public function getCode(): int { return $this->code; } + /** + * @return array + */ + public function getHeaders(): array { + return $this->headers; + } + public static function fromCode(int $code): HttpResponse { - return new HttpResponse($code); + return new HttpResponse($code, []); + } + + public static function redirect(string $url, int $code = HttpCodes::FOUND): HttpResponse { + if ($code < 300 || $code >= 400) { + throw new \InvalidArgumentException("given code is not a redirection http code"); + } + return new HttpResponse($code, ["Location" => $url]); } } diff --git a/src/Http/JsonHttpResponse.php b/src/Http/JsonHttpResponse.php index bbd3d80..a01ffd9 100644 --- a/src/Http/JsonHttpResponse.php +++ b/src/Http/JsonHttpResponse.php @@ -12,7 +12,7 @@ class JsonHttpResponse extends HttpResponse { * @param mixed $payload */ public function __construct($payload, int $code = HttpCodes::OK) { - parent::__construct($code); + parent::__construct($code, []); $this->payload = $payload; } diff --git a/src/Http/ViewHttpResponse.php b/src/Http/ViewHttpResponse.php index 2e517d7..9db80c5 100644 --- a/src/Http/ViewHttpResponse.php +++ b/src/Http/ViewHttpResponse.php @@ -26,7 +26,7 @@ class ViewHttpResponse extends HttpResponse { * @param array $arguments */ private function __construct(int $kind, string $file, array $arguments, int $code = HttpCodes::OK) { - parent::__construct($code); + parent::__construct($code, []); $this->kind = $kind; $this->file = $file; $this->arguments = $arguments; diff --git a/src/Model/AuthModel.php b/src/Model/AuthModel.php index b786c2e..a12ce9e 100644 --- a/src/Model/AuthModel.php +++ b/src/Model/AuthModel.php @@ -2,7 +2,6 @@ namespace App\Model; -use App\Controller\AuthController; use App\Data\Account; use App\Gateway\AccountGateway; use App\Validation\FieldValidationFail; @@ -37,15 +36,15 @@ class AuthModel { $failures[] = new FieldValidationFail("email", "email already exist"); } - if (!empty($errors)) { + if (!empty($failures)) { return null; } $hash = password_hash($password, PASSWORD_DEFAULT); - $account = new Account($email, $username, $this->generateToken()); - $this->gateway->insertAccount($account, $hash); - return $account; + $token = $this->generateToken(); + $accountId = $this->gateway->insertAccount($username, $email, $token, $hash); + return new Account($email, $username, $token, $accountId); } private function generateToken(): string { @@ -60,13 +59,13 @@ class AuthModel { */ public function login(string $email, string $password, array &$failures): ?Account { if (!$this->gateway->exists($email)) { - $failures = new FieldValidationFail("email", "email doesnt exists"); + $failures[] = new FieldValidationFail("email", "email doesnt exists"); return null; } $hash = $this->gateway->getHash($email); if (!password_verify($password, $hash)) { - $failures = new FieldValidationFail("password", "invalid password"); + $failures[] = new FieldValidationFail("password", "invalid password"); return null; } diff --git a/src/Model/TacticModel.php b/src/Model/TacticModel.php index cabcdec..73cfd91 100644 --- a/src/Model/TacticModel.php +++ b/src/Model/TacticModel.php @@ -18,12 +18,12 @@ class TacticModel { $this->tactics = $tactics; } - public function makeNew(string $name): TacticInfo { - return $this->tactics->insert($name); + public function makeNew(string $name, int $ownerId): TacticInfo { + return $this->tactics->insert($name, $ownerId); } - public function makeNewDefault(): ?TacticInfo { - return $this->tactics->insert(self::TACTIC_DEFAULT_NAME); + public function makeNewDefault(int $ownerId): ?TacticInfo { + return $this->tactics->insert(self::TACTIC_DEFAULT_NAME, $ownerId); } /** diff --git a/src/Session/MutableSessionHandle.php b/src/Session/MutableSessionHandle.php index a7822a3..e142049 100644 --- a/src/Session/MutableSessionHandle.php +++ b/src/Session/MutableSessionHandle.php @@ -5,5 +5,6 @@ namespace App\Session; use App\Data\Account; interface MutableSessionHandle extends SessionHandle { + public function setAccount(Account $account): void; } \ No newline at end of file diff --git a/src/Validation/ValidationFail.php b/src/Validation/ValidationFail.php index 4f1ec22..83d6c76 100644 --- a/src/Validation/ValidationFail.php +++ b/src/Validation/ValidationFail.php @@ -34,7 +34,7 @@ class ValidationFail implements JsonSerializable { } public static function notFound(string $message): ValidationFail { - return new ValidationFail("not found", $message); + return new ValidationFail("Not found", $message); } } diff --git a/src/Validator/TacticValidator.php b/src/Validator/TacticValidator.php new file mode 100644 index 0000000..711fb72 --- /dev/null +++ b/src/Validator/TacticValidator.php @@ -0,0 +1,22 @@ +getId() . " n'existe pas"); + } + + if ($tactic->getOwnerId() != $ownerId) { + return new ValidationFail("Unauthorized", "Vous ne pouvez pas accéder à cette tactique.",); + } + + return null; + } + +} \ No newline at end of file diff --git a/src/Views/error.html.twig b/src/Views/error.html.twig index 1d1db7d..e30c2fa 100644 --- a/src/Views/error.html.twig +++ b/src/Views/error.html.twig @@ -51,7 +51,7 @@ {% endfor %} - + \ No newline at end of file From 39329c93257741db65a6f78cfe8b373dbf151523 Mon Sep 17 00:00:00 2001 From: Override-6 Date: Wed, 22 Nov 2023 03:43:03 +0100 Subject: [PATCH 17/27] integrate session in API, use tokens --- phpstan.neon | 2 + public/api/index.php | 126 ++++++++++++++++++-- public/index.php | 4 +- src/Controller/Api/APIAuthController.php | 39 ++++++ src/Controller/Api/APITacticController.php | 22 ++-- src/Controller/FrontController.php | 4 +- src/Controller/Sub/AuthController.php | 1 - src/Controller/Sub/EditorController.php | 2 +- src/Controller/Sub/VisualizerController.php | 2 +- src/Controller/VisitorController.php | 8 +- src/Gateway/AccountGateway.php | 47 +++++--- src/Gateway/TacticInfoGateway.php | 2 +- src/Http/HttpResponse.php | 1 - src/Model/AuthModel.php | 2 +- src/Model/TacticModel.php | 19 ++- src/Session/MutableSessionHandle.php | 3 +- src/Session/PhpSessionHandle.php | 5 +- src/Session/SessionHandle.php | 4 +- src/Validation/ValidationFail.php | 4 + src/Validator/TacticValidator.php | 9 +- 20 files changed, 239 insertions(+), 67 deletions(-) create mode 100644 src/Controller/Api/APIAuthController.php diff --git a/phpstan.neon b/phpstan.neon index bc6c041..715fe84 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -9,5 +9,7 @@ parameters: - sql/database.php - profiles/dev-config-profile.php - profiles/prod-config-profile.php + - public/api/index.php excludePaths: - src/react-display-file.php + - public/api/index.php diff --git a/public/api/index.php b/public/api/index.php index 3ed5caa..8cc7287 100644 --- a/public/api/index.php +++ b/public/api/index.php @@ -6,32 +6,134 @@ require "../../sql/database.php"; require "../utils.php"; use App\Connexion; +use App\Controller\Api\APIAuthController; use App\Controller\Api\APITacticController; +use App\Data\Account; +use App\Gateway\AccountGateway; use App\Gateway\TacticInfoGateway; +use App\Http\HttpResponse; use App\Http\JsonHttpResponse; use App\Http\ViewHttpResponse; +use App\Model\AuthModel; use App\Model\TacticModel; +use App\Session\SessionHandle; +use App\Validation\ValidationFail; -$con = new Connexion(get_database()); +function getTacticController(): APITacticController { + return new APITacticController(new TacticModel(new TacticInfoGateway(new Connexion(get_database())))); +} + +function getAuthController(): APIAuthController { + return new APIAuthController(new AuthModel(new AccountGateway(new Connexion(get_database())))); +} + +class Action { + /** + * @var callable(mixed[]): HttpResponse $action action to call + */ + private $action; + + private bool $isAuthRequired; + + /** + * @param callable(mixed[]): HttpResponse $action + */ + private function __construct(callable $action, bool $isAuthRequired) { + $this->action = $action; + $this->isAuthRequired = $isAuthRequired; + } + + public function isAuthRequired(): bool { + return $this->isAuthRequired; + } + + /** + * @param mixed[] $params + * @param ?Account $account + * @return HttpResponse + */ + public function run(array $params, ?Account $account): HttpResponse { + $params = array_values($params); + if ($this->isAuthRequired) { + if ($account == null) { + throw new Exception("action requires authorization."); + } + $params[] = $account; + } + + return call_user_func_array($this->action, $params); + } + + /** + * @param callable(mixed[]): HttpResponse $action + * @return Action an action that does not require to have an authorization. + */ + public static function noAuth(callable $action): Action { + return new Action($action, false); + } + + /** + * @param callable(mixed[]): HttpResponse $action + * @return Action an action that does require to have an authorization. + */ + public static function auth(callable $action): Action { + return new Action($action, true); + } +} + +/** + * @param mixed[] $match + * @return HttpResponse + * @throws Exception + */ +function handleMatch(array $match): HttpResponse { + if (!$match) { + return new JsonHttpResponse([ValidationFail::notFound("not found")]); + } + + $action = $match['target']; + if (!$action instanceof Action) { + throw new Exception("routed action is not an Action object."); + } + + $auth = null; + + if ($action->isAuthRequired()) { + $auth = tryGetAuthAccount(); + if ($auth == null) { + return new JsonHttpResponse([ValidationFail::unauthorized("Missing or invalid 'Authorization' header")]); + } + } + + return $action->run($match['params'], $auth); +} + +function tryGetAuthAccount(): ?Account { + $headers = getallheaders(); + + // If no authorization header is set, try fallback to php session. + if (!isset($headers['Authorization'])) { + $session = SessionHandle::init(); + return $session->getAccount(); + } + + $token = $headers['Authorization']; + $gateway = new AccountGateway(new Connexion(get_database())); + return $gateway->getAccountFromToken($token); +} $router = new AltoRouter(); $router->setBasePath(get_public_path() . "/api"); -$tacticEndpoint = new APITacticController(new TacticModel(new TacticInfoGateway($con))); -$router->map("POST", "/tactic/[i:id]/edit/name", fn(int $id) => $tacticEndpoint->updateName($id)); -$router->map("GET", "/tactic/[i:id]", fn(int $id) => $tacticEndpoint->getTacticInfo($id)); -$router->map("POST", "/tactic/new", fn() => $tacticEndpoint->newTactic()); +$router->map("POST", "/tactic/[i:id]/edit/name", Action::auth(fn(int $id, Account $acc) => getTacticController()->updateName($id, $acc))); +$router->map("GET", "/tactic/[i:id]", Action::auth(fn(int $id, Account $acc) => getTacticController()->getTacticInfo($id, $acc))); +$router->map("POST", "/tactic/new", Action::auth(fn(Account $acc) => getTacticController()->newTactic($acc))); +$router->map("POST", "/auth", Action::noAuth(fn() => getAuthController()->authorize())); $match = $router->match(); -if ($match == null) { - echo "404 not found"; - header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found'); - exit(1); -} - -$response = call_user_func_array($match['target'], $match['params']); +$response = handleMatch($match); http_response_code($response->getCode()); if ($response instanceof JsonHttpResponse) { diff --git a/public/index.php b/public/index.php index ce1df33..7d42305 100644 --- a/public/index.php +++ b/public/index.php @@ -8,9 +8,9 @@ require "utils.php"; require "../src/react-display.php"; use App\Controller\FrontController; -use App\Session\PhpSessionHandle; +use App\Session\SessionHandle; $basePath = get_public_path(); $frontController = new FrontController($basePath); -$frontController->run(PhpSessionHandle::init()); +$frontController->run(SessionHandle::init()); diff --git a/src/Controller/Api/APIAuthController.php b/src/Controller/Api/APIAuthController.php new file mode 100644 index 0000000..d9e4c23 --- /dev/null +++ b/src/Controller/Api/APIAuthController.php @@ -0,0 +1,39 @@ +model = $model; + } + + + public function authorize(): HttpResponse { + return Control::runChecked([ + "email" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/"), Validators::lenBetween(5, 256)], + "password" => [Validators::lenBetween(6, 256)], + ], function (HttpRequest $req) { + $failures = []; + $account = $this->model->login($req["email"], $req["password"], $failures); + + if (!empty($failures)) { + return new JsonHttpResponse($failures); + } + + return new JsonHttpResponse(["authorization" => $account->getToken()]); + }, true); + } + +} diff --git a/src/Controller/Api/APITacticController.php b/src/Controller/Api/APITacticController.php index ec0edc8..c1fcee3 100644 --- a/src/Controller/Api/APITacticController.php +++ b/src/Controller/Api/APITacticController.php @@ -3,6 +3,7 @@ namespace App\Controller\Api; use App\Controller\Control; +use App\Data\Account; use App\Http\HttpCodes; use App\Http\HttpRequest; use App\Http\HttpResponse; @@ -23,26 +24,33 @@ class APITacticController { $this->model = $model; } - public function updateName(int $tactic_id): HttpResponse { + public function updateName(int $tactic_id, Account $account): HttpResponse { return Control::runChecked([ "name" => [Validators::lenBetween(1, 50), Validators::nameWithSpaces()], - ], function (HttpRequest $request) use ($tactic_id) { - $this->model->updateName($tactic_id, $request["name"]); + ], function (HttpRequest $request) use ($tactic_id, $account) { + + $failures = $this->model->updateName($tactic_id, $request["name"], $account->getId()); + + if (!empty($failures)) { + //TODO find a system to handle Unauthorized error codes more easily from failures. + return new JsonHttpResponse($failures, HttpCodes::BAD_REQUEST); + } + return HttpResponse::fromCode(HttpCodes::OK); }, true); } - public function newTactic(): HttpResponse { + public function newTactic(Account $account): HttpResponse { return Control::runChecked([ "name" => [Validators::lenBetween(1, 50), Validators::nameWithSpaces()], - ], function (HttpRequest $request) { - $tactic = $this->model->makeNew($request["name"]); + ], function (HttpRequest $request) use ($account) { + $tactic = $this->model->makeNew($request["name"], $account->getId()); $id = $tactic->getId(); return new JsonHttpResponse(["id" => $id]); }, true); } - public function getTacticInfo(int $id): HttpResponse { + public function getTacticInfo(int $id, Account $account): HttpResponse { $tactic_info = $this->model->get($id); if ($tactic_info == null) { diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php index 3d73d80..d1bbd6d 100644 --- a/src/Controller/FrontController.php +++ b/src/Controller/FrontController.php @@ -42,7 +42,7 @@ class FrontController { $this->handleMatch($match, $session); } else { $this->displayViewByKind(ViewHttpResponse::twig("error.html.twig", [ - 'failures' => [ValidationFail::notFound("Could not find page ${_SERVER['REQUEST_URI']}.")] + 'failures' => [ValidationFail::notFound("Could not find page ${_SERVER['REQUEST_URI']}.")], ], HttpCodes::NOT_FOUND)); } } @@ -108,7 +108,7 @@ class FrontController { return call_user_func_array([$controller, $action], $params); } else { return ViewHttpResponse::twig("error.html.twig", [ - 'failures' => [ValidationFail::notFound("Could not find page ${_SERVER['REQUEST_URI']}.")] + 'failures' => [ValidationFail::notFound("Could not find page ${_SERVER['REQUEST_URI']}.")], ], HttpCodes::NOT_FOUND); } } diff --git a/src/Controller/Sub/AuthController.php b/src/Controller/Sub/AuthController.php index fc72f67..fcf12d5 100644 --- a/src/Controller/Sub/AuthController.php +++ b/src/Controller/Sub/AuthController.php @@ -2,7 +2,6 @@ namespace App\Controller\Sub; -use App\Gateway\AccountGateway; use App\Http\HttpRequest; use App\Http\HttpResponse; use App\Http\ViewHttpResponse; diff --git a/src/Controller/Sub/EditorController.php b/src/Controller/Sub/EditorController.php index 1188bc9..dff6e8a 100644 --- a/src/Controller/Sub/EditorController.php +++ b/src/Controller/Sub/EditorController.php @@ -40,7 +40,7 @@ class EditorController { public function edit(int $id, SessionHandle $session): HttpResponse { $tactic = $this->model->get($id); - $failure = TacticValidator::validateAccess($tactic, $session->getAccount()->getId()); + $failure = TacticValidator::validateAccess($tactic, $id, $session->getAccount()->getId()); if ($failure != null) { return ViewHttpResponse::twig('error.html.twig', ['failures' => [$failure]], HttpCodes::NOT_FOUND); diff --git a/src/Controller/Sub/VisualizerController.php b/src/Controller/Sub/VisualizerController.php index f70b18e..c7e5098 100644 --- a/src/Controller/Sub/VisualizerController.php +++ b/src/Controller/Sub/VisualizerController.php @@ -25,7 +25,7 @@ class VisualizerController { public function visualize(int $id, SessionHandle $session): HttpResponse { $tactic = $this->tacticModel->get($id); - $failure = TacticValidator::validateAccess($tactic, $session->getAccount()->getId()); + $failure = TacticValidator::validateAccess($tactic, $id, $session->getAccount()->getId()); if ($failure != null) { return ViewHttpResponse::twig('error.html.twig', ['failures' => [$failure]], HttpCodes::NOT_FOUND); diff --git a/src/Controller/VisitorController.php b/src/Controller/VisitorController.php index 56e0ec2..e74934a 100644 --- a/src/Controller/VisitorController.php +++ b/src/Controller/VisitorController.php @@ -9,9 +9,7 @@ use App\Model\AuthModel; use App\Session\MutableSessionHandle; class VisitorController { - - - public final function register(MutableSessionHandle $session): HttpResponse { + final public function register(MutableSessionHandle $session): HttpResponse { $model = new AuthModel(new AccountGateway(new Connexion(get_database()))); if ($_SERVER['REQUEST_METHOD'] === 'GET') { return (new Sub\AuthController($model))->displayRegister(); @@ -19,7 +17,7 @@ class VisitorController { return (new Sub\AuthController($model))->confirmRegister($_POST, $session); } - public final function login(MutableSessionHandle $session): HttpResponse { + final public function login(MutableSessionHandle $session): HttpResponse { $model = new AuthModel(new AccountGateway(new Connexion(get_database()))); if ($_SERVER['REQUEST_METHOD'] === 'GET') { return (new Sub\AuthController($model))->displayLogin(); @@ -27,4 +25,4 @@ class VisitorController { return (new Sub\AuthController($model))->confirmLogin($_POST, $session); } -} \ No newline at end of file +} diff --git a/src/Gateway/AccountGateway.php b/src/Gateway/AccountGateway.php index 8320f33..3a57abb 100644 --- a/src/Gateway/AccountGateway.php +++ b/src/Gateway/AccountGateway.php @@ -17,39 +17,56 @@ class AccountGateway { } - public function exists(string $email): bool { - return $this->getAccount($email) != null; - } - - public function insertAccount(string $name, string $email, string $token, string $hash): int { $this->con->exec("INSERT INTO Account(username, hash, email, token) VALUES (:username,:hash,:email,:token)", [ ':username' => [$name, PDO::PARAM_STR], ':hash' => [$hash, PDO::PARAM_STR], ':email' => [$email, PDO::PARAM_STR], - ':token' => [$token, PDO::PARAM_STR] + ':token' => [$token, PDO::PARAM_STR], ]); return intval($this->con->lastInsertId()); } - public function getHash(string $email): string { - $results = $this->con->fetch("SELECT hash FROM Account WHERE email = :email", [ - ':email' => [$email, PDO::PARAM_STR] - ]); - return $results[0]['hash']; + /** + * @param string $email + * @return array|null + */ + private function getRowsFromMail(string $email): ?array { + return $this->con->fetch("SELECT * FROM Account WHERE email = :email", [':email' => [$email, PDO::PARAM_STR]])[0] ?? null; + } + + + public function getHash(string $email): ?string { + $results = $this->getRowsFromMail($email); + if ($results == null) { + return null; + } + return $results['hash']; } + public function exists(string $email): bool { + return $this->getRowsFromMail($email) != null; + } /** * @param string $email * @return Account|null */ - public function getAccount(string $email): ?Account { - $results = $this->con->fetch("SELECT * FROM Account WHERE email = :email", [':email' => [$email, PDO::PARAM_STR]]); - if (empty($results)) + public function getAccountFromMail(string $email): ?Account { + $acc = $this->getRowsFromMail($email); + if (empty($acc)) { + return null; + } + + return new Account($email, $acc["username"], $acc["token"], $acc["id"]); + } + + public function getAccountFromToken(string $token): ?Account { + $acc = $this->con->fetch("SELECT * FROM Account WHERE token = :token", [':token' => [$token, PDO::PARAM_STR]])[0] ?? null; + if (empty($acc)) { return null; + } - $acc = $results[0]; return new Account($acc["email"], $acc["username"], $acc["token"], $acc["id"]); } diff --git a/src/Gateway/TacticInfoGateway.php b/src/Gateway/TacticInfoGateway.php index 6aa2cca..efa0a7c 100644 --- a/src/Gateway/TacticInfoGateway.php +++ b/src/Gateway/TacticInfoGateway.php @@ -36,7 +36,7 @@ class TacticInfoGateway { "INSERT INTO TacticInfo(name, owner) VALUES(:name, :owner)", [ ":name" => [$name, PDO::PARAM_STR], - ":owner" => [$owner, PDO::PARAM_INT] + ":owner" => [$owner, PDO::PARAM_INT], ] ); $row = $this->con->fetch( diff --git a/src/Http/HttpResponse.php b/src/Http/HttpResponse.php index d6d6a9a..e5db3e8 100644 --- a/src/Http/HttpResponse.php +++ b/src/Http/HttpResponse.php @@ -3,7 +3,6 @@ namespace App\Http; class HttpResponse { - /** * @var array */ diff --git a/src/Model/AuthModel.php b/src/Model/AuthModel.php index a12ce9e..1739ab5 100644 --- a/src/Model/AuthModel.php +++ b/src/Model/AuthModel.php @@ -69,7 +69,7 @@ class AuthModel { return null; } - return $this->gateway->getAccount($email); + return $this->gateway->getAccountFromMail($email); } diff --git a/src/Model/TacticModel.php b/src/Model/TacticModel.php index 73cfd91..8111963 100644 --- a/src/Model/TacticModel.php +++ b/src/Model/TacticModel.php @@ -2,8 +2,10 @@ namespace App\Model; +use App\Data\Account; use App\Data\TacticInfo; use App\Gateway\TacticInfoGateway; +use App\Validation\ValidationFail; class TacticModel { public const TACTIC_DEFAULT_NAME = "Nouvelle tactique"; @@ -39,15 +41,22 @@ class TacticModel { * Update the name of a tactic * @param int $id the tactic identifier * @param string $name the new name to set - * @return bool true if the update was done successfully + * @return ValidationFail[] failures, if any */ - public function updateName(int $id, string $name): bool { - if ($this->tactics->get($id) == null) { - return false; + public function updateName(int $id, string $name, int $authId): array { + + $tactic = $this->tactics->get($id); + + if ($tactic == null) { + return [ValidationFail::notFound("Could not find tactic")]; + } + + if ($tactic->getOwnerId() != $authId) { + return [ValidationFail::unauthorized()]; } $this->tactics->updateName($id, $name); - return true; + return []; } } diff --git a/src/Session/MutableSessionHandle.php b/src/Session/MutableSessionHandle.php index e142049..2bcb0fc 100644 --- a/src/Session/MutableSessionHandle.php +++ b/src/Session/MutableSessionHandle.php @@ -5,6 +5,5 @@ namespace App\Session; use App\Data\Account; interface MutableSessionHandle extends SessionHandle { - public function setAccount(Account $account): void; -} \ No newline at end of file +} diff --git a/src/Session/PhpSessionHandle.php b/src/Session/PhpSessionHandle.php index ea0e7c2..09d862c 100644 --- a/src/Session/PhpSessionHandle.php +++ b/src/Session/PhpSessionHandle.php @@ -5,8 +5,7 @@ namespace App\Session; use App\Data\Account; class PhpSessionHandle implements MutableSessionHandle { - - public static function init(): PhpSessionHandle { + public static function init(): SessionHandle { if (session_status() !== PHP_SESSION_NONE) { throw new \Exception("A php session is already started !"); } @@ -21,4 +20,4 @@ class PhpSessionHandle implements MutableSessionHandle { public function setAccount(Account $account): void { $_SESSION["account"] = $account; } -} \ No newline at end of file +} diff --git a/src/Session/SessionHandle.php b/src/Session/SessionHandle.php index 5a65794..6ffb9c0 100644 --- a/src/Session/SessionHandle.php +++ b/src/Session/SessionHandle.php @@ -5,7 +5,5 @@ namespace App\Session; use App\Data\Account; interface SessionHandle { - public function getAccount(): ?Account; - -} \ No newline at end of file +} diff --git a/src/Validation/ValidationFail.php b/src/Validation/ValidationFail.php index 83d6c76..2229a53 100644 --- a/src/Validation/ValidationFail.php +++ b/src/Validation/ValidationFail.php @@ -37,4 +37,8 @@ class ValidationFail implements JsonSerializable { return new ValidationFail("Not found", $message); } + public static function unauthorized(string $message = "Unauthorized"): ValidationFail { + return new ValidationFail("Unauthorized", $message); + } + } diff --git a/src/Validator/TacticValidator.php b/src/Validator/TacticValidator.php index 711fb72..e39cd51 100644 --- a/src/Validator/TacticValidator.php +++ b/src/Validator/TacticValidator.php @@ -6,17 +6,16 @@ use App\Data\TacticInfo; use App\Validation\ValidationFail; class TacticValidator { - - public static function validateAccess(?TacticInfo $tactic, int $ownerId): ?ValidationFail { + public static function validateAccess(?TacticInfo $tactic, int $tacticId, int $ownerId): ?ValidationFail { if ($tactic == null) { - return ValidationFail::notFound("La tactique " . $tactic->getId() . " n'existe pas"); + return ValidationFail::notFound("La tactique $tacticId n'existe pas"); } if ($tactic->getOwnerId() != $ownerId) { - return new ValidationFail("Unauthorized", "Vous ne pouvez pas accéder à cette tactique.",); + return ValidationFail::unauthorized("Vous ne pouvez pas accéder à cette tactique."); } return null; } -} \ No newline at end of file +} From 262fa6180e9ea0b6ba992fdf86db58f23007a10d Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Wed, 22 Nov 2023 08:40:15 +0100 Subject: [PATCH 18/27] applied suggestions --- public/index.php | 2 +- src/Controller/TeamController.php | 10 +++------- src/Data/Color.php | 1 + src/Data/MemberRole.php | 4 ++-- src/Views/display_team.html.twig | 7 ++++++- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/public/index.php b/public/index.php index cc63837..abc1a50 100644 --- a/public/index.php +++ b/public/index.php @@ -46,7 +46,7 @@ $router->map("POST","/team/new", fn()=>$teamController->SubmitTeam($_POST)); $router->map("GET","/team/list", fn()=>$teamController->displayListTeamByName()); $router->map("POST","/team/list", fn()=>$teamController->ListTeamByName($_POST)); -$router->map("GET","/team/[i:id]", fn(int $id)=>$teamController->displayTeam($id)); +$router->map("GET","/team/[i:id]", fn(int $id)=>$teamController->getTeam($id)); $match = $router->match(); diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index 288e733..d4c7bfe 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -13,15 +13,12 @@ use \Twig\Environment; class TeamController { private TeamModel $model; - private Environment $twig; /** * @param TeamModel $model - * @param Environment $twig */ - public function __construct(TeamModel $model, Environment $twig) { + public function __construct(TeamModel $model) { $this->model = $model; - $this->twig = $twig; } public function displaySubmitTeam() : HttpResponse { @@ -29,7 +26,6 @@ class TeamController } public function submitTeam(array $request): HttpResponse { - $errors = []; $request = HttpRequest::from($request, $errors, [ @@ -47,7 +43,7 @@ class TeamController } return ViewHttpResponse::twig('insert_team.html.twig', ['bad_fields' => $badFields]); } - return $this->displayTeam($this->model->createTeam($request['name'], $request['picture'], $request['mainColor'], $request['secondColor'])); + return $this->getTeam($this->model->createTeam($request['name'], $request['picture'], $request['mainColor'], $request['secondColor'])); } public function displayListTeamByName(): HttpResponse { @@ -74,7 +70,7 @@ class TeamController return ViewHttpResponse::twig('display_teams.html.twig', ['teams' => $results]); } - public function displayTeam(int $id): HttpResponse { + public function getTeam(int $id): HttpResponse { $result = $this->model->displayTeam($id); return ViewHttpResponse::twig('display_team.html.twig', ['team' => $result]); } diff --git a/src/Data/Color.php b/src/Data/Color.php index 2e934c0..d32d81f 100755 --- a/src/Data/Color.php +++ b/src/Data/Color.php @@ -28,6 +28,7 @@ class Color { public static function from(string $value): Color { $color = self::tryFrom($value); if ($color == null) { + var_dump($value); throw new InvalidArgumentException("The string is not an hexadecimal code"); } return $color; diff --git a/src/Data/MemberRole.php b/src/Data/MemberRole.php index bd8add3..3bc9d56 100755 --- a/src/Data/MemberRole.php +++ b/src/Data/MemberRole.php @@ -12,8 +12,8 @@ use http\Exception\InvalidArgumentException; */ final class MemberRole { - public const ROLE_PLAYER = 0; - public const ROLE_COACH = 1; + private const ROLE_PLAYER = 0; + private const ROLE_COACH = 1; private const MIN = self::ROLE_PLAYER; private const MAX = self::ROLE_COACH; diff --git a/src/Views/display_team.html.twig b/src/Views/display_team.html.twig index f7bb02b..9783fe2 100644 --- a/src/Views/display_team.html.twig +++ b/src/Views/display_team.html.twig @@ -8,10 +8,11 @@ background-color: #f1f1f1; display: flex; flex-direction: column; + align-items: center; } section{ - width: 80%; + width: 60%; } .square{ @@ -32,6 +33,10 @@ .team{ border-color: darkgrey; border-radius: 20px; + align-items: center; + } + .team.h1{ + } From 65151cc1eacf00bea776f3c8666536c9c7846770 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Wed, 22 Nov 2023 09:02:30 +0100 Subject: [PATCH 19/27] add some css --- src/Views/display_team.html.twig | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/Views/display_team.html.twig b/src/Views/display_team.html.twig index 9783fe2..732fd19 100644 --- a/src/Views/display_team.html.twig +++ b/src/Views/display_team.html.twig @@ -29,14 +29,27 @@ .container{ background-color: #fff; + display: flex; + flex-direction: column; + align-items: center; } .team{ border-color: darkgrey; border-radius: 20px; - align-items: center; + } - .team.h1{ + .colors{ + justify-content: space-between; + } + .color{ + flex-direction: row; + justify-content: space-between; + } + + .logo{ + height: 80px; + width: 80px; } @@ -47,11 +60,16 @@
-
-

{{ team.name }}

- Logo d'équipe -
-
+
+
+

{{ team.name }}

+ +
+
+

Couleur principale :

+

Couleur secondaire :

+
+ {% for m in team.members %}

m.id

From 69be9399549b2855bb01c715de63fa685c78c622 Mon Sep 17 00:00:00 2001 From: "maxime.batista" Date: Wed, 22 Nov 2023 09:38:35 +0100 Subject: [PATCH 20/27] add target url in session to bring back user to its initial target after auth --- public/api/index.php | 3 ++- public/index.php | 4 ++-- src/Controller/FrontController.php | 11 +++++++---- src/Controller/Sub/AuthController.php | 7 +++++-- src/Session/MutableSessionHandle.php | 3 +++ src/Session/PhpSessionHandle.php | 8 ++++++++ src/Session/SessionHandle.php | 2 ++ 7 files changed, 29 insertions(+), 9 deletions(-) diff --git a/public/api/index.php b/public/api/index.php index 8cc7287..580f794 100644 --- a/public/api/index.php +++ b/public/api/index.php @@ -16,6 +16,7 @@ use App\Http\JsonHttpResponse; use App\Http\ViewHttpResponse; use App\Model\AuthModel; use App\Model\TacticModel; +use App\Session\PhpSessionHandle; use App\Session\SessionHandle; use App\Validation\ValidationFail; @@ -113,7 +114,7 @@ function tryGetAuthAccount(): ?Account { // If no authorization header is set, try fallback to php session. if (!isset($headers['Authorization'])) { - $session = SessionHandle::init(); + $session = PhpSessionHandle::init(); return $session->getAccount(); } diff --git a/public/index.php b/public/index.php index 7d42305..ce1df33 100644 --- a/public/index.php +++ b/public/index.php @@ -8,9 +8,9 @@ require "utils.php"; require "../src/react-display.php"; use App\Controller\FrontController; -use App\Session\SessionHandle; +use App\Session\PhpSessionHandle; $basePath = get_public_path(); $frontController = new FrontController($basePath); -$frontController->run(SessionHandle::init()); +$frontController->run(PhpSessionHandle::init()); diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php index d1bbd6d..4ac3047 100644 --- a/src/Controller/FrontController.php +++ b/src/Controller/FrontController.php @@ -40,11 +40,12 @@ class FrontController { $match = $this->router->match(); if ($match) { $this->handleMatch($match, $session); - } else { - $this->displayViewByKind(ViewHttpResponse::twig("error.html.twig", [ - 'failures' => [ValidationFail::notFound("Could not find page ${_SERVER['REQUEST_URI']}.")], - ], HttpCodes::NOT_FOUND)); + return; } + + $this->displayViewByKind(ViewHttpResponse::twig("error.html.twig", [ + 'failures' => [ValidationFail::notFound("Could not find page ${_SERVER['REQUEST_URI']}.")], + ], HttpCodes::NOT_FOUND)); } /** @@ -96,6 +97,8 @@ class FrontController { if ($controllerName != self::VISITOR_CONTROLLER) { $account = $session->getAccount(); if ($account == null) { + // put in the session the initial url the user wanted to get + $session->setInitialTarget($_SERVER['REQUEST_URI']); return HttpResponse::redirect($this->basePath . "/visitor/login"); } } diff --git a/src/Controller/Sub/AuthController.php b/src/Controller/Sub/AuthController.php index fcf12d5..d94da43 100644 --- a/src/Controller/Sub/AuthController.php +++ b/src/Controller/Sub/AuthController.php @@ -63,7 +63,8 @@ class AuthController { $session->setAccount($account); - return ViewHttpResponse::twig("display_auth_confirm.html.twig", ['username' => $account->getName(), 'email' => $account->getEmail()]); + $target_url = $session->getInitialTarget(); + return HttpResponse::redirect($target_url ?? "/home"); } @@ -92,7 +93,9 @@ class AuthController { $session->setAccount($account); - return ViewHttpResponse::twig("display_auth_confirm.html.twig", ['username' => $account->getName(), 'email' => $account->getEmail()]); + $target_url = $session->getInitialTarget(); + $session->setInitialTarget(null); + return HttpResponse::redirect($target_url ?? "/home"); } } diff --git a/src/Session/MutableSessionHandle.php b/src/Session/MutableSessionHandle.php index 2bcb0fc..64ee52f 100644 --- a/src/Session/MutableSessionHandle.php +++ b/src/Session/MutableSessionHandle.php @@ -5,5 +5,8 @@ namespace App\Session; use App\Data\Account; interface MutableSessionHandle extends SessionHandle { + + public function setInitialTarget(?string $url): void; + public function setAccount(Account $account): void; } diff --git a/src/Session/PhpSessionHandle.php b/src/Session/PhpSessionHandle.php index 09d862c..5568eb5 100644 --- a/src/Session/PhpSessionHandle.php +++ b/src/Session/PhpSessionHandle.php @@ -17,7 +17,15 @@ class PhpSessionHandle implements MutableSessionHandle { return $_SESSION["account"] ?? null; } + public function getInitialTarget(): ?string { + return $_SESSION["target"] ?? null; + } + public function setAccount(Account $account): void { $_SESSION["account"] = $account; } + + public function setInitialTarget(?string $url): void { + $_SESSION["target"] = $url; + } } diff --git a/src/Session/SessionHandle.php b/src/Session/SessionHandle.php index 6ffb9c0..2114877 100644 --- a/src/Session/SessionHandle.php +++ b/src/Session/SessionHandle.php @@ -5,5 +5,7 @@ namespace App\Session; use App\Data\Account; interface SessionHandle { + public function getInitialTarget(): ?string; + public function getAccount(): ?Account; } From 76e64a5ae5511f7f1198358f6fb67564225558de Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Wed, 22 Nov 2023 12:23:45 +0100 Subject: [PATCH 21/27] formated the code and verification plus some more css --- public/index.php | 12 +++++----- src/Controller/TeamController.php | 20 +++++++++------- src/Data/Color.php | 13 +++++----- src/Data/MemberRole.php | 5 ++-- src/Data/Team.php | 2 +- src/Gateway/TeamGateway.php | 40 +++++++++++++++++++++---------- src/Model/TeamModel.php | 5 ++-- src/Validation/Validators.php | 7 ++---- src/Views/display_team.html.twig | 13 +++++----- 9 files changed, 66 insertions(+), 51 deletions(-) diff --git a/public/index.php b/public/index.php index 3e4bf87..6633860 100644 --- a/public/index.php +++ b/public/index.php @@ -49,14 +49,14 @@ $router->map("GET", "/tactic/new", fn() => $editorController->makeNew()); $router->map("GET", "/tactic/[i:id]/edit", fn(int $id) => $editorController->openEditorFor($id)); $router->map("GET", "/tactic/[i:id]", fn(int $id) => $visualizerController->openVisualizer($id)); -$teamController = new \App\Controller\TeamController(new \App\Model\TeamModel(new \App\Gateway\TeamGateway($con)),$twig); -$router->map("GET","/team/new", fn()=>$teamController->displaySubmitTeam()); -$router->map("POST","/team/new", fn()=>$teamController->SubmitTeam($_POST)); +$teamController = new \App\Controller\TeamController(new \App\Model\TeamModel(new \App\Gateway\TeamGateway($con))); +$router->map("GET", "/team/new", fn() => $teamController->displaySubmitTeam()); +$router->map("POST", "/team/new", fn() => $teamController->SubmitTeam($_POST)); -$router->map("GET","/team/list", fn()=>$teamController->displayListTeamByName()); -$router->map("POST","/team/list", fn()=>$teamController->ListTeamByName($_POST)); +$router->map("GET", "/team/list", fn() => $teamController->displayListTeamByName()); +$router->map("POST", "/team/list", fn() => $teamController->ListTeamByName($_POST)); -$router->map("GET","/team/[i:id]", fn(int $id)=>$teamController->getTeam($id)); +$router->map("GET", "/team/[i:id]", fn(int $id) => $teamController->getTeam($id)); $match = $router->match(); diff --git a/src/Controller/TeamController.php b/src/Controller/TeamController.php index d4c7bfe..08e22cc 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/TeamController.php @@ -8,10 +8,8 @@ use App\Http\ViewHttpResponse; use App\Model\TeamModel; use App\Validation\FieldValidationFail; use App\Validation\Validators; -use \Twig\Environment; -class TeamController -{ +class TeamController { private TeamModel $model; /** @@ -21,10 +19,14 @@ class TeamController $this->model = $model; } - public function displaySubmitTeam() : HttpResponse { + public function displaySubmitTeam(): HttpResponse { return ViewHttpResponse::twig("insert_team.html.twig", []); } + /** + * @param array $request + * @return HttpResponse + */ public function submitTeam(array $request): HttpResponse { $errors = []; @@ -32,7 +34,7 @@ class TeamController "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], "mainColor" => [Validators::regex('/#(?:[0-9a-fA-F]{6})/')], "secondColor" => [Validators::regex('/#(?:[0-9a-fA-F]{6})/')], - "picture" => [Validators::isURL()] + "picture" => [Validators::isURL()], ]); if (!empty($errors)) { $badFields = []; @@ -50,10 +52,14 @@ class TeamController return ViewHttpResponse::twig("list_team_by_name.html.twig", []); } + /** + * @param array $request + * @return HttpResponse + */ public function listTeamByName(array $request): HttpResponse { $errors = []; $request = HttpRequest::from($request, $errors, [ - "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()] + "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], ]); if (!empty($errors) && $errors[0] instanceof FieldValidationFail) { @@ -75,5 +81,3 @@ class TeamController return ViewHttpResponse::twig('display_team.html.twig', ['team' => $result]); } } - - diff --git a/src/Data/Color.php b/src/Data/Color.php index 875b615..b12e27a 100755 --- a/src/Data/Color.php +++ b/src/Data/Color.php @@ -2,31 +2,31 @@ namespace App\Data; -use \InvalidArgumentException; +use InvalidArgumentException; class Color { /** - * @var string 6 bytes unsigned int that represents an RGB color + * @var string that represents an hexadecimal color code */ private string $hex; /** * @param string $value 6 bytes unsigned int that represents an RGB color - * @throws \InvalidArgumentException if the value is negative or greater than 0xFFFFFF + * @throws InvalidArgumentException if the value is negative or greater than 0xFFFFFF */ private function __construct(string $value) { if ($value < 0 || $value > 0xFFFFFF) { throw new InvalidArgumentException("int color value is invalid, must be positive and lower than 0xFFFFFF"); } - $this->value = $value; + $this->hex = $value; } /** * @return string */ public function getValue(): string { - return $this->value; + return $this->hex; } public static function from(string $value): Color { @@ -39,10 +39,9 @@ class Color { } public static function tryFrom(string $value): ?Color { - if (!preg_match('/#(?:[0-9a-fA-F]{6})/',$value)) { + if (!preg_match('/#(?:[0-9a-fA-F]{6})/', $value)) { return null; } return new Color($value); } } - diff --git a/src/Data/MemberRole.php b/src/Data/MemberRole.php index 9838b92..559d516 100755 --- a/src/Data/MemberRole.php +++ b/src/Data/MemberRole.php @@ -10,7 +10,6 @@ use http\Exception\InvalidArgumentException; * encapsulates an integer value and use it as an enumeration discriminant */ final class MemberRole { - private const ROLE_PLAYER = 0; private const ROLE_COACH = 1; private const MIN = self::ROLE_PLAYER; @@ -25,11 +24,11 @@ final class MemberRole { $this->value = $val; } - public static function player(){ + public static function player(): MemberRole { return new MemberRole(MemberRole::ROLE_PLAYER); } - public static function coach(){ + public static function coach(): MemberRole { return new MemberRole(MemberRole::ROLE_COACH); } diff --git a/src/Data/Team.php b/src/Data/Team.php index a31829f..e50ad40 100755 --- a/src/Data/Team.php +++ b/src/Data/Team.php @@ -21,7 +21,7 @@ class Team { * @param Color $secondColor * @param Member[] $members */ - public function __construct(int $id,string $name, string $picture, Color $mainColor, Color $secondColor, array $members =[]) { + public function __construct(int $id, string $name, string $picture, Color $mainColor, Color $secondColor, array $members = []) { $this->id = $id; $this->name = $name; $this->picture = $picture; diff --git a/src/Gateway/TeamGateway.php b/src/Gateway/TeamGateway.php index 1cecbe1..c3d22dc 100644 --- a/src/Gateway/TeamGateway.php +++ b/src/Gateway/TeamGateway.php @@ -12,52 +12,68 @@ class TeamGateway { $this->con = $con; } - public function insert(string $name, string $picture, string $mainColor, string $secondColor) { + public function insert(string $name, string $picture, string $mainColor, string $secondColor): void { $this->con->exec( "INSERT INTO Team(name, picture, mainColor, secondColor) VALUES (:teamName , :picture, :mainColor, :secondColor)", [ ":teamName" => [$name, PDO::PARAM_STR], ":picture" => [$picture, PDO::PARAM_STR], ":mainColor" => [$mainColor, PDO::PARAM_STR], - ":secondColor" => [$secondColor, PDO::PARAM_STR] + ":secondColor" => [$secondColor, PDO::PARAM_STR], ] ); } + /** + * @param string $name + * @return array[] + */ public function listByName(string $name): array { return $this->con->fetch( "SELECT id,name,picture,mainColor,secondColor FROM Team WHERE name LIKE '%' || :name || '%'", [ - ":name" => [$name, PDO::PARAM_STR] + ":name" => [$name, PDO::PARAM_STR], ] ); } - public function getTeamById(int $id): array{ + /** + * @param int $id + * @return array[] + */ + public function getTeamById(int $id): array { return $this->con->fetch( "SELECT id,name,picture,mainColor,secondColor FROM Team WHERE id = :id", [ - ":id" => [$id, PDO::PARAM_INT] + ":id" => [$id, PDO::PARAM_INT], ] ); } - public function getIdTeamByName(string $name): array{ + /** + * @param string $name + * @return array[] + */ + public function getIdTeamByName(string $name): array { return $this->con->fetch( "SELECT id FROM Team WHERE name = :name", [ - ":name" => [$name, PDO::PARAM_STR] + ":name" => [$name, PDO::PARAM_STR], ] ); } - public function getMembersById($id):array{ + /** + * @param int $id + * @return array[] + */ + public function getMembersById(int $id): array { return $this->con->fetch( - "SELECT m.role,u.id FROM User u,Team t,Member m WHERE t.id = :id AND m.idTeam = t.id AND m.idMember = u.id", - [ - ":id" => [$id, PDO::PARAM_INT] + "SELECT m.role,u.id FROM User u,Team t,Member m WHERE t.id = :id AND m.idTeam = t.id AND m.idMember = u.id", + [ + ":id" => [$id, PDO::PARAM_INT], ] ); } -} \ No newline at end of file +} diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index c5521eb..b6e4ab1 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -8,8 +8,7 @@ use App\Data\Member; use App\Data\MemberRole; use App\Data\Color; -class TeamModel -{ +class TeamModel { private TeamGateway $gateway; /** @@ -48,4 +47,4 @@ class TeamModel } return new Team(intval($result['id']), $result['name'], $result['picture'], Color::from($result['mainColor']), Color::from($result['secondColor']), $members); } -} \ No newline at end of file +} diff --git a/src/Validation/Validators.php b/src/Validation/Validators.php index b1d1eaf..cb28007 100644 --- a/src/Validation/Validators.php +++ b/src/Validation/Validators.php @@ -56,7 +56,7 @@ class Validators { return self::regex("/^[0-9]+$/"); } - public static function isIntInRange(int $min,int $max): Validator { + public static function isIntInRange(int $min, int $max): Validator { return new SimpleFunctionValidator( fn(string $val) => intval($val) >= $min && intval($val) <= $max, fn(string $name) => [new FieldValidationFail($name, "The value is not in the range $min to $max ")] @@ -65,11 +65,8 @@ class Validators { public static function isURL(): Validator { return new SimpleFunctionValidator( - fn($val) => filter_var($val, FILTER_VALIDATE_URL) , + fn($val) => filter_var($val, FILTER_VALIDATE_URL), fn(string $name) => [new FieldValidationFail($name, "The value is not an URL")] ); } } - - - diff --git a/src/Views/display_team.html.twig b/src/Views/display_team.html.twig index 732fd19..ada8566 100644 --- a/src/Views/display_team.html.twig +++ b/src/Views/display_team.html.twig @@ -22,9 +22,15 @@ #mainColor{ background-color: {{ team.mainColor.getValue() }}; + {% if team.mainColor.getValue() == "#ffffff" %} + border-color: #666666; + {% endif %} } #secondColor{ background-color: {{ team.secondColor.getValue() }}; + {% if team.secondColor.getValue() == "#ffffff" %} + border-color: #666666; + {% endif %} } .container{ @@ -39,9 +45,6 @@ } - .colors{ - justify-content: space-between; - } .color{ flex-direction: row; justify-content: space-between; @@ -65,12 +68,10 @@

{{ team.name }}

-
+

Couleur principale :

Couleur secondaire :

- - {% for m in team.members %}

m.id

{% endfor %} From d8000bcf3b32fbd97512f6740ea02eae9dc9cf80 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Wed, 22 Nov 2023 12:33:27 +0100 Subject: [PATCH 22/27] fixing CI --- src/Model/TeamModel.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Model/TeamModel.php b/src/Model/TeamModel.php index b6e4ab1..65f22a7 100644 --- a/src/Model/TeamModel.php +++ b/src/Model/TeamModel.php @@ -24,6 +24,10 @@ class TeamModel { return intval($result[0]['id']); } + /** + * @param string $name + * @return Team[] + */ public function listByName(string $name): array { $teams = []; $results = $this->gateway->listByName($name); From e3875b4f15ce3b710187808420290445fe0b974c Mon Sep 17 00:00:00 2001 From: "maxime.batista" Date: Wed, 22 Nov 2023 13:02:28 +0100 Subject: [PATCH 23/27] add documentation --- Documentation/database_mcd.puml | 17 +++++++++++------ public/api/index.php | 4 +++- sql/setup-tables.sql | 10 ++-------- src/Gateway/TacticInfoGateway.php | 8 ++++---- src/Session/MutableSessionHandle.php | 10 +++++++++- src/Session/PhpSessionHandle.php | 5 ++++- src/Session/SessionHandle.php | 12 ++++++++++++ 7 files changed, 45 insertions(+), 21 deletions(-) diff --git a/Documentation/database_mcd.puml b/Documentation/database_mcd.puml index e698a69..710dfee 100644 --- a/Documentation/database_mcd.puml +++ b/Documentation/database_mcd.puml @@ -2,12 +2,10 @@ object Account { id - name - age email - phoneNumber - passwordHash - profilePicture + username + token + hash } object Team { @@ -26,7 +24,7 @@ object TacticFolder { object Tactic { id_json name - creationDate + creation_date } usecase have_team [ @@ -63,6 +61,10 @@ usecase contains_other_folder [ to contain ] +usecase owns [ + owns +] + Account "0,n" -- have_team have_team -- "1,n" Team @@ -73,6 +75,9 @@ shared_tactic_account -- "0,n" Tactic Tactic "0,n" -- shared_tactic_team shared_tactic_team -- "0,n" Team +Tactic "1,1" -- owns +owns -- Account + Team "0,n" -- shared_folder_team shared_folder_team -- "0,n"TacticFolder diff --git a/public/api/index.php b/public/api/index.php index 580f794..c5f8f2e 100644 --- a/public/api/index.php +++ b/public/api/index.php @@ -17,7 +17,6 @@ use App\Http\ViewHttpResponse; use App\Model\AuthModel; use App\Model\TacticModel; use App\Session\PhpSessionHandle; -use App\Session\SessionHandle; use App\Validation\ValidationFail; function getTacticController(): APITacticController { @@ -28,6 +27,9 @@ function getAuthController(): APIAuthController { return new APIAuthController(new AuthModel(new AccountGateway(new Connexion(get_database())))); } +/** + * A Front controller action + */ class Action { /** * @var callable(mixed[]): HttpResponse $action action to call diff --git a/sql/setup-tables.sql b/sql/setup-tables.sql index 457803c..26e58da 100644 --- a/sql/setup-tables.sql +++ b/sql/setup-tables.sql @@ -1,13 +1,7 @@ -- drop tables here -DROP TABLE IF EXISTS FormEntries; DROP TABLE IF EXISTS Account; -DROP TABLE IF EXISTS TacticInfo; +DROP TABLE IF EXISTS Tactic; -CREATE TABLE FormEntries -( - name varchar NOT NULL, - description varchar NOT NULL -); CREATE TABLE Account ( id integer PRIMARY KEY AUTOINCREMENT, @@ -17,7 +11,7 @@ CREATE TABLE Account hash varchar NOT NULL ); -CREATE TABLE TacticInfo +CREATE TABLE Tactic ( id integer PRIMARY KEY AUTOINCREMENT, name varchar NOT NULL, diff --git a/src/Gateway/TacticInfoGateway.php b/src/Gateway/TacticInfoGateway.php index efa0a7c..3075f69 100644 --- a/src/Gateway/TacticInfoGateway.php +++ b/src/Gateway/TacticInfoGateway.php @@ -18,7 +18,7 @@ class TacticInfoGateway { public function get(int $id): ?TacticInfo { $res = $this->con->fetch( - "SELECT * FROM TacticInfo WHERE id = :id", + "SELECT * FROM Tactic WHERE id = :id", [":id" => [$id, PDO::PARAM_INT]] ); @@ -33,14 +33,14 @@ class TacticInfoGateway { public function insert(string $name, int $owner): TacticInfo { $this->con->exec( - "INSERT INTO TacticInfo(name, owner) VALUES(:name, :owner)", + "INSERT INTO Tactic(name, owner) VALUES(:name, :owner)", [ ":name" => [$name, PDO::PARAM_STR], ":owner" => [$owner, PDO::PARAM_INT], ] ); $row = $this->con->fetch( - "SELECT id, creation_date, owner FROM TacticInfo WHERE :id = id", + "SELECT id, creation_date, owner FROM Tactic WHERE :id = id", [':id' => [$this->con->lastInsertId(), PDO::PARAM_INT]] )[0]; return new TacticInfo(intval($row["id"]), $name, strtotime($row["creation_date"]), $row["owner"]); @@ -48,7 +48,7 @@ class TacticInfoGateway { public function updateName(int $id, string $name): void { $this->con->exec( - "UPDATE TacticInfo SET name = :name WHERE id = :id", + "UPDATE Tactic SET name = :name WHERE id = :id", [ ":name" => [$name, PDO::PARAM_STR], ":id" => [$id, PDO::PARAM_INT], diff --git a/src/Session/MutableSessionHandle.php b/src/Session/MutableSessionHandle.php index 64ee52f..1459819 100644 --- a/src/Session/MutableSessionHandle.php +++ b/src/Session/MutableSessionHandle.php @@ -4,9 +4,17 @@ namespace App\Session; use App\Data\Account; +/** + * The mutable side of a session handle + */ interface MutableSessionHandle extends SessionHandle { - + /** + * @param string|null $url the url to redirect the user to after authentication. + */ public function setInitialTarget(?string $url): void; + /** + * @param Account $account update the session's account + */ public function setAccount(Account $account): void; } diff --git a/src/Session/PhpSessionHandle.php b/src/Session/PhpSessionHandle.php index 5568eb5..7d6b10b 100644 --- a/src/Session/PhpSessionHandle.php +++ b/src/Session/PhpSessionHandle.php @@ -4,8 +4,11 @@ namespace App\Session; use App\Data\Account; +/** + * A PHP session handle + */ class PhpSessionHandle implements MutableSessionHandle { - public static function init(): SessionHandle { + public static function init(): self { if (session_status() !== PHP_SESSION_NONE) { throw new \Exception("A php session is already started !"); } diff --git a/src/Session/SessionHandle.php b/src/Session/SessionHandle.php index 2114877..663c78c 100644 --- a/src/Session/SessionHandle.php +++ b/src/Session/SessionHandle.php @@ -4,8 +4,20 @@ namespace App\Session; use App\Data\Account; +/** + * An immutable session handle + */ interface SessionHandle { + /** + * The initial target url if the user wanted to perform an action that requires authentication + * but has been required to login first in the application. + * @return string|null Get the initial targeted URL + */ public function getInitialTarget(): ?string; + /** + * The session account if the user is logged in. + * @return Account|null + */ public function getAccount(): ?Account; } From a11dd2e1228299bd0947ce44131be4c21783ed37 Mon Sep 17 00:00:00 2001 From: Yanis DAHMANE-BOUNOUA Date: Tue, 21 Nov 2023 21:30:12 +0100 Subject: [PATCH 24/27] front-controller (#17) I did the FRONT CONTROLLERRRRR. (Salva version lol) When you create a new controller, define the path to call it here (image 1). Don't forget to associate a controller with a role. The main method have to return a `ViewHttpResponse`. If you create a new action, please make sure that the action's main method have the same name than the action (image 2 and 3) and to make the main method public. Co-authored-by: DahmaneYanis Co-authored-by: d_yanis Co-authored-by: Override-6 Reviewed-on: https://codefirst.iut.uca.fr/git/IQBall/Application-Web/pulls/17 Co-authored-by: Yanis DAHMANE-BOUNOUA Co-committed-by: Yanis DAHMANE-BOUNOUA --- Documentation/http.puml | 2 +- Documentation/php.puml | 8 ++ public/index.php | 90 +--------------- src/Controller/EditorController.php | 14 ++- src/Controller/FrontController.php | 162 ++++++++++++++++++++++++++++ src/Controller/UserController.php | 16 +++ src/Views/home.twig | 13 +++ 7 files changed, 210 insertions(+), 95 deletions(-) create mode 100644 Documentation/php.puml create mode 100644 src/Controller/FrontController.php create mode 100644 src/Controller/UserController.php create mode 100644 src/Views/home.twig diff --git a/Documentation/http.puml b/Documentation/http.puml index b41135d..9fbe606 100644 --- a/Documentation/http.puml +++ b/Documentation/http.puml @@ -35,7 +35,7 @@ class ViewHttpResponse extends HttpResponse { - arguments: array - kind: int - + __construct(kind: int, file: string, arguments: array, code: int = HttpCodes::OK) + - __construct(kind: int, file: string, arguments: array, code: int = HttpCodes::OK) + getViewKind(): int + getFile(): string + getArguments(): array diff --git a/Documentation/php.puml b/Documentation/php.puml new file mode 100644 index 0000000..8641900 --- /dev/null +++ b/Documentation/php.puml @@ -0,0 +1,8 @@ +@startuml + +class FrontController { + - router : AltoRouter + +} + +@enduml \ No newline at end of file diff --git a/public/index.php b/public/index.php index 6633860..f898d7f 100644 --- a/public/index.php +++ b/public/index.php @@ -4,92 +4,10 @@ require "../vendor/autoload.php"; require "../config.php"; require "../sql/database.php"; require "utils.php"; +require "../src/react-display.php"; -use App\Connexion; -use App\Controller\EditorController; -use App\Controller\SampleFormController; -use App\Gateway\FormResultGateway; -use App\Gateway\TacticInfoGateway; -use App\Http\JsonHttpResponse; -use App\Http\ViewHttpResponse; -use App\Model\TacticModel; -use Twig\Loader\FilesystemLoader; -use App\Gateway\AuthGateway; -use App\Controller\AuthController; -use App\Validation\ValidationFail; -use App\Controller\ErrorController; -use App\Controller\VisualizerController; - -$loader = new FilesystemLoader('../src/Views/'); -$twig = new \Twig\Environment($loader); +use App\Controller\FrontController; $basePath = get_public_path(); -$con = new Connexion(get_database()); - -// routes initialization -$router = new AltoRouter(); -$router->setBasePath($basePath); - -$sampleFormController = new SampleFormController(new FormResultGateway($con)); -$authGateway = new AuthGateway($con); -$authController = new \App\Controller\AuthController(new \App\Model\AuthModel($authGateway)); -$editorController = new EditorController(new TacticModel(new TacticInfoGateway($con))); -$visualizerController = new VisualizerController(new TacticModel(new TacticInfoGateway($con))); - - -$router->map("GET", "/", fn() => $sampleFormController->displayFormReact()); -$router->map("POST", "/submit", fn() => $sampleFormController->submitFormReact($_POST)); -$router->map("GET", "/twig", fn() => $sampleFormController->displayFormTwig()); -$router->map("POST", "/submit-twig", fn() => $sampleFormController->submitFormTwig($_POST)); -$router->map("GET", "/register", fn() => $authController->displayRegister()); -$router->map("POST", "/register", fn() => $authController->confirmRegister($_POST)); -$router->map("GET", "/login", fn() => $authController->displayLogin()); -$router->map("POST", "/login", fn() => $authController->confirmLogin($_POST)); -$router->map("GET", "/tactic/new", fn() => $editorController->makeNew()); -$router->map("GET", "/tactic/[i:id]/edit", fn(int $id) => $editorController->openEditorFor($id)); -$router->map("GET", "/tactic/[i:id]", fn(int $id) => $visualizerController->openVisualizer($id)); - -$teamController = new \App\Controller\TeamController(new \App\Model\TeamModel(new \App\Gateway\TeamGateway($con))); -$router->map("GET", "/team/new", fn() => $teamController->displaySubmitTeam()); -$router->map("POST", "/team/new", fn() => $teamController->SubmitTeam($_POST)); - -$router->map("GET", "/team/list", fn() => $teamController->displayListTeamByName()); -$router->map("POST", "/team/list", fn() => $teamController->ListTeamByName($_POST)); - -$router->map("GET", "/team/[i:id]", fn(int $id) => $teamController->getTeam($id)); - -$match = $router->match(); - -if ($match == null) { - http_response_code(404); - ErrorController::displayFailures([ValidationFail::notFound("Cette page n'existe pas")], $twig); - return; -} - -$response = call_user_func_array($match['target'], $match['params']); - -http_response_code($response->getCode()); - -if ($response instanceof ViewHttpResponse) { - $file = $response->getFile(); - $args = $response->getArguments(); - - switch ($response->getViewKind()) { - case ViewHttpResponse::REACT_VIEW: - send_react_front($file, $args); - break; - case ViewHttpResponse::TWIG_VIEW: - try { - $twig->display($file, $args); - } catch (\Twig\Error\RuntimeError|\Twig\Error\SyntaxError $e) { - http_response_code(500); - echo "There was an error rendering your view, please refer to an administrator.\nlogs date: " . date("YYYD, d M Y H:i:s"); - throw $e; - } - break; - } - -} elseif ($response instanceof JsonHttpResponse) { - header('Content-type: application/json'); - echo $response->getJson(); -} +$frontController = new FrontController($basePath); +$frontController->run(); diff --git a/src/Controller/EditorController.php b/src/Controller/EditorController.php index ed270d1..eb07184 100644 --- a/src/Controller/EditorController.php +++ b/src/Controller/EditorController.php @@ -2,9 +2,10 @@ namespace App\Controller; +use App\Connexion; use App\Data\TacticInfo; +use App\Gateway\TacticInfoGateway; use App\Http\HttpCodes; -use App\Http\HttpRequest; use App\Http\HttpResponse; use App\Http\JsonHttpResponse; use App\Http\ViewHttpResponse; @@ -13,18 +14,15 @@ use App\Model\TacticModel; class EditorController { private TacticModel $model; - /** - * @param TacticModel $model - */ - public function __construct(TacticModel $model) { - $this->model = $model; + public function __construct() { + $this->model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); } private function openEditor(TacticInfo $tactic): HttpResponse { return ViewHttpResponse::react("views/Editor.tsx", ["name" => $tactic->getName(), "id" => $tactic->getId()]); } - public function makeNew(): HttpResponse { + public function create(): HttpResponse { $tactic = $this->model->makeNewDefault(); return $this->openEditor($tactic); } @@ -34,7 +32,7 @@ class EditorController { * @param int $id the targeted tactic identifier * @return HttpResponse */ - public function openEditorFor(int $id): HttpResponse { + public function edit(int $id): HttpResponse { $tactic = $this->model->get($id); if ($tactic == null) { diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php new file mode 100644 index 0000000..66d5d4f --- /dev/null +++ b/src/Controller/FrontController.php @@ -0,0 +1,162 @@ +router = $this->createRouter($basePath); + $this->initializeRouterMap(); + } + + /** + * Main behavior of the FrontController + * + * @return void + */ + public function run(): void { + $match = $this->router->match(); + if ($match != null) { + $this->handleMatch($match); + } else { + $this->displayViewByKind(ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND)); + } + } + + /** + * Create a new instance of an AltoRouter + * + * @param string $basePath + * @return AltoRouter + */ + public function createRouter(string $basePath): AltoRouter { + $router = new AltoRouter(); + $router->setBasePath($basePath); + return $router; + } + + /** + * Initialize project's routes + * + * @return void + */ + private function initializeRouterMap(): void { + $this->router->map("GET", "/", "UserController"); + $this->router->map("GET", "/[a:action]?", "UserController"); + $this->router->map("GET", "/tactic/[a:action]/[i:idTactic]?", "EditorController"); + } + + /** + * @param array $match + * @return void + */ + private function handleMatch(array $match): void { + $tag = $match['target']; + + $action = $this->getAction($match); + $params = $match["params"]; + unset($params['action']); + $this->handleResponseByType($this->tryToCall($tag, $action, array_values($params))); + } + + /** + * @param string $controller + * @param string $action + * @param array $params + * @return HttpResponse + */ + private function tryToCall(string $controller, string $action, array $params): HttpResponse { + $controller = $this->getController($controller); + try { + if (is_callable([$controller, $action])) { + return call_user_func_array([$controller, $action], $params); + } else { + return ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND); + } + } catch (Exception $e) { + return ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND); + } + } + + /** + * Get the right method to call to do an action + * + * @param array $match + * @return string + */ + private function getAction(array $match): string { + if (isset($match["params"]["action"])) { + return $match["params"]["action"]; + } + return "default"; + } + + /** + * Initialize the right controller by the user's role + * + * @param string $controller + * @return mixed + */ + private function getController(string $controller) { + $namespace = "\\App\\Controller\\"; + $controller = $namespace . $controller; + return new $controller(); + } + + /** + * Redirect the return by the response's type + * + * @param HttpResponse $response + * @return void + */ + private function handleResponseByType(HttpResponse $response): void { + http_response_code($response->getCode()); + if ($response instanceof ViewHttpResponse) { + $this->displayViewByKind($response); + } elseif ($response instanceof JsonHttpResponse) { + header('Content-type: application/json'); + echo $response->getJson(); + } + } + + /** + * Use the right method to display the response + * + * @param ViewHttpResponse $response + * @return void + */ + private function displayViewByKind(ViewHttpResponse $response): void { + $file = $response->getFile(); + $args = $response->getArguments(); + + switch ($response->getViewKind()) { + case ViewHttpResponse::REACT_VIEW: + send_react_front($file, $args); + break; + case ViewHttpResponse::TWIG_VIEW: + try { + $loader = new FilesystemLoader('../src/Views/'); + $twig = new Environment($loader); + $twig->display($file, $args); + } catch (RuntimeError|SyntaxError|LoaderError $e) { + http_response_code(500); + echo "There was an error rendering your view, please refer to an administrator.\nlogs date: " . date("YYYD, d M Y H:i:s"); + throw $e; + } + break; + } + } +} diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php new file mode 100644 index 0000000..930cd67 --- /dev/null +++ b/src/Controller/UserController.php @@ -0,0 +1,16 @@ + + + + + + + Document + + +

Page Home à faire

+ + \ No newline at end of file From 038686e2ed2fcab7fb461bf37cc75ee42f7f2d18 Mon Sep 17 00:00:00 2001 From: Override-6 Date: Tue, 21 Nov 2023 22:57:36 +0100 Subject: [PATCH 25/27] add salva-like routes and controller architecture --- src/Controller/ErrorController.php | 26 -------- src/Controller/FrontController.php | 2 +- src/Controller/SampleFormController.php | 64 ------------------- src/Controller/{ => Sub}/AuthController.php | 4 +- src/Controller/{ => Sub}/EditorController.php | 4 +- .../{ => Sub}/VisualizerController.php | 5 +- src/Controller/UserController.php | 36 ++++++++++- src/Model/AuthModel.php | 1 - 8 files changed, 40 insertions(+), 102 deletions(-) delete mode 100644 src/Controller/ErrorController.php delete mode 100644 src/Controller/SampleFormController.php rename src/Controller/{ => Sub}/AuthController.php (97%) rename src/Controller/{ => Sub}/EditorController.php (93%) rename src/Controller/{ => Sub}/VisualizerController.php (88%) diff --git a/src/Controller/ErrorController.php b/src/Controller/ErrorController.php deleted file mode 100644 index 7fc5239..0000000 --- a/src/Controller/ErrorController.php +++ /dev/null @@ -1,26 +0,0 @@ -display("error.html.twig", ['failures' => $failures]); - } catch (LoaderError|RuntimeError|SyntaxError $e) { - echo "Twig error: $e"; - } - } -} diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php index 66d5d4f..0600a01 100644 --- a/src/Controller/FrontController.php +++ b/src/Controller/FrontController.php @@ -56,7 +56,7 @@ class FrontController { private function initializeRouterMap(): void { $this->router->map("GET", "/", "UserController"); $this->router->map("GET", "/[a:action]?", "UserController"); - $this->router->map("GET", "/tactic/[a:action]/[i:idTactic]?", "EditorController"); + $this->router->map("GET", "/tactic/[a:action]/[i:idTactic]?", "UserController"); } /** diff --git a/src/Controller/SampleFormController.php b/src/Controller/SampleFormController.php deleted file mode 100644 index 773a4db..0000000 --- a/src/Controller/SampleFormController.php +++ /dev/null @@ -1,64 +0,0 @@ -gateway = $gateway; - } - - - public function displayFormReact(): HttpResponse { - return ViewHttpResponse::react("views/SampleForm.tsx", []); - } - - public function displayFormTwig(): HttpResponse { - return ViewHttpResponse::twig('sample_form.html.twig', []); - } - - /** - * @param array $form - * @param callable(array>): ViewHttpResponse $response - * @return HttpResponse - */ - private function submitForm(array $form, callable $response): HttpResponse { - return Control::runCheckedFrom($form, [ - "name" => [Validators::lenBetween(0, 32), Validators::name("Le nom ne peut contenir que des lettres, des chiffres et des accents")], - "description" => [Validators::lenBetween(0, 512)], - ], function (HttpRequest $req) use ($response) { - $description = htmlspecialchars($req["description"]); - $this->gateway->insert($req["name"], $description); - $results = ["results" => $this->gateway->listResults()]; - return call_user_func_array($response, [$results]); - }, false); - } - - /** - * @param array $form - * @return HttpResponse - */ - public function submitFormTwig(array $form): HttpResponse { - return $this->submitForm($form, fn(array $results) => ViewHttpResponse::twig('display_results.html.twig', $results)); - } - - /** - * @param array $form - * @return HttpResponse - */ - public function submitFormReact(array $form): HttpResponse { - return $this->submitForm($form, fn(array $results) => ViewHttpResponse::react('views/DisplayResults.tsx', $results)); - } -} diff --git a/src/Controller/AuthController.php b/src/Controller/Sub/AuthController.php similarity index 97% rename from src/Controller/AuthController.php rename to src/Controller/Sub/AuthController.php index e42c27d..a78b02e 100644 --- a/src/Controller/AuthController.php +++ b/src/Controller/Sub/AuthController.php @@ -1,8 +1,7 @@ $tactic->getName(), "id" => $tactic->getId()]); } - public function create(): HttpResponse { + public function createNew(): HttpResponse { $tactic = $this->model->makeNewDefault(); return $this->openEditor($tactic); } diff --git a/src/Controller/VisualizerController.php b/src/Controller/Sub/VisualizerController.php similarity index 88% rename from src/Controller/VisualizerController.php rename to src/Controller/Sub/VisualizerController.php index 3c8b55e..e3b5663 100644 --- a/src/Controller/VisualizerController.php +++ b/src/Controller/Sub/VisualizerController.php @@ -1,6 +1,6 @@ tacticModel = $tacticModel; } - public function openVisualizer(int $id): HttpResponse { + public function visualize(int $id): HttpResponse { $tactic = $this->tacticModel->get($id); if ($tactic == null) { @@ -27,6 +27,5 @@ class VisualizerController { } return ViewHttpResponse::react("views/Visualizer.tsx", ["name" => $tactic->getName()]); - } } diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 930cd67..2a95bcc 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -2,15 +2,47 @@ namespace App\Controller; +use App\Connexion; +use App\Gateway\AuthGateway; +use App\Gateway\TacticInfoGateway; use App\Http\HttpResponse; use App\Http\ViewHttpResponse; +use App\Model\AuthModel; +use App\Model\TacticModel; class UserController { public function home(): HttpResponse { return ViewHttpResponse::twig("home.twig", []); } - public function default(): HttpResponse { - return self::home(); + public function register(): HttpResponse { + $model = new AuthModel(new AuthGateway(new Connexion(get_database()))); + if ($_SERVER['REQUEST_METHOD'] === 'GET') { + return (new Sub\AuthController($model))->displayRegister(); + } + return (new Sub\AuthController($model))->confirmRegister($_POST); + } + + public function login(): HttpResponse { + $model = new AuthModel(new AuthGateway(new Connexion(get_database()))); + if ($_SERVER['REQUEST_METHOD'] === 'GET') { + return (new Sub\AuthController($model))->displayLogin(); + } + return (new Sub\AuthController($model))->confirmLogin($_POST); + } + + public function open(int $id): HttpResponse { + $model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); + return (new Sub\VisualizerController($model))->visualize($id); + } + + public function edit(int $id): HttpResponse { + $model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); + return (new Sub\EditorController($model))->edit($id); + } + + public function create(): HttpResponse { + $model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); + return (new Sub\EditorController($model))->createNew(); } } diff --git a/src/Model/AuthModel.php b/src/Model/AuthModel.php index 45b63e4..312cca8 100644 --- a/src/Model/AuthModel.php +++ b/src/Model/AuthModel.php @@ -2,7 +2,6 @@ namespace App\Model; -use App\Controller\AuthController; use App\Gateway\AuthGateway; use App\Validation\FieldValidationFail; use App\Validation\ValidationFail; From 851aa72e2c4e6ec8bc42a21cd0c3bb8fac996d64 Mon Sep 17 00:00:00 2001 From: "vivien.dufour" Date: Wed, 22 Nov 2023 08:38:58 +0100 Subject: [PATCH 26/27] fix on routes --- src/Controller/FrontController.php | 49 ++++++++++++--------- src/Controller/Sub/EditorController.php | 4 +- src/Controller/{ => Sub}/TeamController.php | 2 +- src/Controller/UserController.php | 26 +++++++++++ src/Views/display_teams.html.twig | 4 +- src/Views/insert_team.html.twig | 2 +- src/Views/list_team_by_name.html.twig | 2 +- 7 files changed, 61 insertions(+), 28 deletions(-) rename src/Controller/{ => Sub}/TeamController.php (98%) diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php index 0600a01..7d61b28 100644 --- a/src/Controller/FrontController.php +++ b/src/Controller/FrontController.php @@ -14,10 +14,12 @@ use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; use Twig\Loader\FilesystemLoader; -class FrontController { +class FrontController +{ private AltoRouter $router; - public function __construct(string $basePath) { + public function __construct(string $basePath) + { $this->router = $this->createRouter($basePath); $this->initializeRouterMap(); } @@ -27,7 +29,8 @@ class FrontController { * * @return void */ - public function run(): void { + public function run(): void + { $match = $this->router->match(); if ($match != null) { $this->handleMatch($match); @@ -42,7 +45,8 @@ class FrontController { * @param string $basePath * @return AltoRouter */ - public function createRouter(string $basePath): AltoRouter { + public function createRouter(string $basePath): AltoRouter + { $router = new AltoRouter(); $router->setBasePath($basePath); return $router; @@ -53,17 +57,19 @@ class FrontController { * * @return void */ - private function initializeRouterMap(): void { + private function initializeRouterMap(): void + { $this->router->map("GET", "/", "UserController"); - $this->router->map("GET", "/[a:action]?", "UserController"); - $this->router->map("GET", "/tactic/[a:action]/[i:idTactic]?", "UserController"); + $this->router->map("GET|POST", "/[a:action]?/[i:id]", "UserController"); + $this->router->map("GET|POST", "/tactic/[a:action]/[i:idTactic]?", "UserController"); } /** * @param array $match * @return void */ - private function handleMatch(array $match): void { + private function handleMatch(array $match): void + { $tag = $match['target']; $action = $this->getAction($match); @@ -78,15 +84,12 @@ class FrontController { * @param array $params * @return HttpResponse */ - private function tryToCall(string $controller, string $action, array $params): HttpResponse { + private function tryToCall(string $controller, string $action, array $params): HttpResponse + { $controller = $this->getController($controller); - try { - if (is_callable([$controller, $action])) { - return call_user_func_array([$controller, $action], $params); - } else { - return ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND); - } - } catch (Exception $e) { + if (is_callable([$controller, $action])) { + return call_user_func_array([$controller, $action], $params); + } else { return ViewHttpResponse::twig("error.html.twig", [], HttpCodes::NOT_FOUND); } } @@ -97,7 +100,8 @@ class FrontController { * @param array $match * @return string */ - private function getAction(array $match): string { + private function getAction(array $match): string + { if (isset($match["params"]["action"])) { return $match["params"]["action"]; } @@ -110,7 +114,8 @@ class FrontController { * @param string $controller * @return mixed */ - private function getController(string $controller) { + private function getController(string $controller) + { $namespace = "\\App\\Controller\\"; $controller = $namespace . $controller; return new $controller(); @@ -122,7 +127,8 @@ class FrontController { * @param HttpResponse $response * @return void */ - private function handleResponseByType(HttpResponse $response): void { + private function handleResponseByType(HttpResponse $response): void + { http_response_code($response->getCode()); if ($response instanceof ViewHttpResponse) { $this->displayViewByKind($response); @@ -138,7 +144,8 @@ class FrontController { * @param ViewHttpResponse $response * @return void */ - private function displayViewByKind(ViewHttpResponse $response): void { + private function displayViewByKind(ViewHttpResponse $response): void + { $file = $response->getFile(); $args = $response->getArguments(); @@ -151,7 +158,7 @@ class FrontController { $loader = new FilesystemLoader('../src/Views/'); $twig = new Environment($loader); $twig->display($file, $args); - } catch (RuntimeError|SyntaxError|LoaderError $e) { + } catch (RuntimeError | SyntaxError | LoaderError $e) { http_response_code(500); echo "There was an error rendering your view, please refer to an administrator.\nlogs date: " . date("YYYD, d M Y H:i:s"); throw $e; diff --git a/src/Controller/Sub/EditorController.php b/src/Controller/Sub/EditorController.php index def6ac9..ce7a0a3 100644 --- a/src/Controller/Sub/EditorController.php +++ b/src/Controller/Sub/EditorController.php @@ -14,8 +14,8 @@ use App\Model\TacticModel; class EditorController { private TacticModel $model; - public function __construct() { - $this->model = new TacticModel(new TacticInfoGateway(new Connexion(get_database()))); + public function __construct(TacticModel $model) { + $this->model = $model; } private function openEditor(TacticInfo $tactic): HttpResponse { diff --git a/src/Controller/TeamController.php b/src/Controller/Sub/TeamController.php similarity index 98% rename from src/Controller/TeamController.php rename to src/Controller/Sub/TeamController.php index 08e22cc..c3991a3 100644 --- a/src/Controller/TeamController.php +++ b/src/Controller/Sub/TeamController.php @@ -1,6 +1,6 @@ createNew(); } + + public function createTeam(): HttpResponse { + $model = new TeamModel(new TeamGateway(new Connexion(get_database()))); + $ctrl = new Sub\TeamController($model); + if ($_SERVER['REQUEST_METHOD'] === 'GET') { + return $ctrl->displaySubmitTeam(); + } + return $ctrl->submitTeam($_POST); + } + + public function listTeams(): HttpResponse { + $model = new TeamModel(new TeamGateway(new Connexion(get_database()))); + $ctrl = new Sub\TeamController($model); + if ($_SERVER['REQUEST_METHOD'] === 'GET') { + return $ctrl->displayListTeamByName(); + } + return $ctrl->listTeamByName($_POST); + } + + public function getTeam(int $id): HttpResponse { + $model = new TeamModel(new TeamGateway(new Connexion(get_database()))); + $ctrl = new Sub\TeamController($model); + return $ctrl->getTeam($id); + } } diff --git a/src/Views/display_teams.html.twig b/src/Views/display_teams.html.twig index c0ac185..47b3cbc 100644 --- a/src/Views/display_teams.html.twig +++ b/src/Views/display_teams.html.twig @@ -10,7 +10,7 @@

Aucune équipe n'a été trouvée

Chercher une équipe

-
+
@@ -22,7 +22,7 @@
{% else %} {% for t in teams %} -
+

Nom de l'équipe : {{ t.name }}

logo de l'équipe
diff --git a/src/Views/insert_team.html.twig b/src/Views/insert_team.html.twig index 0cc85dd..fbe6917 100644 --- a/src/Views/insert_team.html.twig +++ b/src/Views/insert_team.html.twig @@ -64,7 +64,7 @@

Créer une équipe

- +
diff --git a/src/Views/list_team_by_name.html.twig b/src/Views/list_team_by_name.html.twig index 1d9ddf3..1165da3 100644 --- a/src/Views/list_team_by_name.html.twig +++ b/src/Views/list_team_by_name.html.twig @@ -62,7 +62,7 @@

Chercher une équipe

- +
From ad4208e6dafca2573be166bcdcf852883481e98e Mon Sep 17 00:00:00 2001 From: "maxime.batista" Date: Wed, 22 Nov 2023 13:46:08 +0100 Subject: [PATCH 27/27] fix format --- src/Controller/FrontController.php | 33 ++++++++++-------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/Controller/FrontController.php b/src/Controller/FrontController.php index 7d61b28..eb796f7 100644 --- a/src/Controller/FrontController.php +++ b/src/Controller/FrontController.php @@ -14,12 +14,10 @@ use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; use Twig\Loader\FilesystemLoader; -class FrontController -{ +class FrontController { private AltoRouter $router; - public function __construct(string $basePath) - { + public function __construct(string $basePath) { $this->router = $this->createRouter($basePath); $this->initializeRouterMap(); } @@ -29,8 +27,7 @@ class FrontController * * @return void */ - public function run(): void - { + public function run(): void { $match = $this->router->match(); if ($match != null) { $this->handleMatch($match); @@ -45,8 +42,7 @@ class FrontController * @param string $basePath * @return AltoRouter */ - public function createRouter(string $basePath): AltoRouter - { + public function createRouter(string $basePath): AltoRouter { $router = new AltoRouter(); $router->setBasePath($basePath); return $router; @@ -57,8 +53,7 @@ class FrontController * * @return void */ - private function initializeRouterMap(): void - { + private function initializeRouterMap(): void { $this->router->map("GET", "/", "UserController"); $this->router->map("GET|POST", "/[a:action]?/[i:id]", "UserController"); $this->router->map("GET|POST", "/tactic/[a:action]/[i:idTactic]?", "UserController"); @@ -68,8 +63,7 @@ class FrontController * @param array $match * @return void */ - private function handleMatch(array $match): void - { + private function handleMatch(array $match): void { $tag = $match['target']; $action = $this->getAction($match); @@ -84,8 +78,7 @@ class FrontController * @param array $params * @return HttpResponse */ - private function tryToCall(string $controller, string $action, array $params): HttpResponse - { + private function tryToCall(string $controller, string $action, array $params): HttpResponse { $controller = $this->getController($controller); if (is_callable([$controller, $action])) { return call_user_func_array([$controller, $action], $params); @@ -100,8 +93,7 @@ class FrontController * @param array $match * @return string */ - private function getAction(array $match): string - { + private function getAction(array $match): string { if (isset($match["params"]["action"])) { return $match["params"]["action"]; } @@ -114,8 +106,7 @@ class FrontController * @param string $controller * @return mixed */ - private function getController(string $controller) - { + private function getController(string $controller) { $namespace = "\\App\\Controller\\"; $controller = $namespace . $controller; return new $controller(); @@ -127,8 +118,7 @@ class FrontController * @param HttpResponse $response * @return void */ - private function handleResponseByType(HttpResponse $response): void - { + private function handleResponseByType(HttpResponse $response): void { http_response_code($response->getCode()); if ($response instanceof ViewHttpResponse) { $this->displayViewByKind($response); @@ -144,8 +134,7 @@ class FrontController * @param ViewHttpResponse $response * @return void */ - private function displayViewByKind(ViewHttpResponse $response): void - { + private function displayViewByKind(ViewHttpResponse $response): void { $file = $response->getFile(); $args = $response->getArguments();