From a123145acdf07a2aab499c1177f93ca0f23cf032 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Tue, 9 Jan 2024 17:28:45 +0100 Subject: [PATCH 1/6] new branch where the documentation/conception will be updated --- Documentation/models.puml | 43 +++++++++++++++++++------------------- src/Core/Data/TeamInfo.php | 3 --- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/Documentation/models.puml b/Documentation/models.puml index 86ca699..727b2e3 100755 --- a/Documentation/models.puml +++ b/Documentation/models.puml @@ -29,18 +29,11 @@ class Account { class Member { - userId: int - teamId: int - - + __construct(role : MemberRole) + - role : string + + __construct(role : string) + getUserId(): int + getTeamId(): int - + getRole(): MemberRole -} - -Member --> "- role" MemberRole - -enum MemberRole { - PLAYER - COACH + + getRole(): string } @@ -48,28 +41,34 @@ class TeamInfo { - creationDate: int - name: string - picture: string - + - mainColor : string + - secondColor : string + getName(): string + getPicture(): string - + getMainColor(): Color - + getSecondColor(): Color + + getMainColor(): string + + getSecondColor(): string } -TeamInfo --> "- mainColor" Color -TeamInfo --> "- secondaryColor" Color - class Team { - getInfo(): TeamInfo - listMembers(): Member[] + + __construct() + + getInfo(): TeamInfo + + listMembers(): Member[] } Team --> "- info" TeamInfo Team --> "- members *" Member -class Color { - - value: int - - + getValue(): int +class User{ + - id : int + - name : string + - email : string + - profilePicture : string + + + __construct(id : int,name : string,email: string,profilePicture:string) + + getId() : id + + getName() : string + + getEmail() : string + + getProfilePicture() : string } @enduml \ No newline at end of file diff --git a/src/Core/Data/TeamInfo.php b/src/Core/Data/TeamInfo.php index 0f741fe..964990c 100644 --- a/src/Core/Data/TeamInfo.php +++ b/src/Core/Data/TeamInfo.php @@ -24,7 +24,6 @@ class TeamInfo implements \JsonSerializable { $this->secondColor = $secondColor; } - public function getId(): int { return $this->id; } @@ -48,6 +47,4 @@ class TeamInfo implements \JsonSerializable { public function jsonSerialize() { return get_object_vars($this); } - - } -- 2.36.3 From e1056f4ade452bda355072350c0b455cf583ddf9 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Fri, 12 Jan 2024 11:34:29 +0100 Subject: [PATCH 2/6] updated old documentation --- Documentation/models.puml | 48 ++++++++++++----- Documentation/mvc/auth.puml | 32 ++++++------ Documentation/mvc/editor.puml | 38 ++++++++++++++ Documentation/mvc/team.puml | 96 +++++++++++++++++++++------------- Documentation/validation.puml | 30 ++++++++--- src/Core/Model/TacticModel.php | 8 --- 6 files changed, 172 insertions(+), 80 deletions(-) create mode 100644 Documentation/mvc/editor.puml diff --git a/Documentation/models.puml b/Documentation/models.puml index 727b2e3..b88b543 100755 --- a/Documentation/models.puml +++ b/Documentation/models.puml @@ -6,51 +6,75 @@ class TacticInfo { - creationDate: string - ownerId: string - content: string - + + + __construct(id:int,name:string,creationDate:int,ownerId:int,courtType:CourtType,content:string) + getId(): int + getOwnerId(): int + getCreationTimestamp(): int + getName(): string + getContent(): string + + getCourtType() : CourtType +} + +TacticInfo -->"- courtType" CourtType + +class CourtType{ + - value : int + - COURT_PLAIN : int {static} {frozen} + - COURT_HALF : int {static} {frozen} + + - __construct(val:int) + + plain() : CourtType {static} + + half() : CourtType {static} + + name() : string + + fromName(name:string) : CourtType + + isPlain() : bool + + isHalf() : bool } +note bottom: Basically an evoluated enum + class Account { - - email: string - token: string - - name: string - - id: int - + getMailAddress(): string - + getToken(): string - + getName(): string - + getId(): int + + __construct(token:string,user:User) + + getUser() : User + + getToken() : string } +Account -->"- user" User + class Member { - - userId: int - teamId: int - role : string + + __construct(role : string) - + getUserId(): int + + getUser(): User + getTeamId(): int + getRole(): string } +note bottom: Member's role is either "Coach" or "Player" + +Member -->"- user" User class TeamInfo { - - creationDate: int - name: string - picture: string - mainColor : string - secondColor : string + + + __construct(id:int,name:string,picture:string,mainColor:string,secondColor:string) + getName(): string + getPicture(): string + getMainColor(): string + getSecondColor(): string } +note left: Both team's color are the hex code of the color + class Team { - + __construct() + + __construct(info:TeamInfo,members: Member[]) + getInfo(): TeamInfo + listMembers(): Member[] } diff --git a/Documentation/mvc/auth.puml b/Documentation/mvc/auth.puml index 8a2bb1f..d6b56fa 100644 --- a/Documentation/mvc/auth.puml +++ b/Documentation/mvc/auth.puml @@ -7,26 +7,26 @@ class AuthController { + displayLogin() : HttpResponse + login(request : array , session : MutableSessionHandle) : HttpResponse } -AuthController --> "- model" AuthModel +AuthController *-- "- model" AuthModel class AuthModel { - +__construct(gateway : AccountGateway) - + register(username : string, password : string, confirmPassword : string, email : string, failures : array): Account - + generateToken() : string - + login(email : string, password : string) + + __construct(gateway : AccountGateway) + + register(username:string, password:string, confirmPassword:string, email:string, &failures:array): ?Account + generateToken() : string + + generateToken(): string + + login(email:string, password:string, &failures:array): ?Account } -AuthModel --> "- gateway" AccountGateway - -class AccountGateway { - -con : Connection - +__construct(con : Connection) - + insertAccount(name : string, email : string, hash : string, token : string) : int - + getRowsFromMail(email : string): array - + getHash(email : string) : array - + exists(email : string) : bool - + getAccountFromMail(email : string ): Account - + getAccountFromToken(email : string ): Account +AuthModel *-- "- gateway" AccountGateway +class AccountGateway{ + + __construct(con : Connexion) + + insertAccount(name:string, email:string, token:string, hash:string, profilePicture:string): int + + getRowsFromMail(email:string): ?array + + getHash(email:string): ?string + + exists(email:string): bool + + getAccountFromMail(email:string): ?Account + + getAccountFromToken(token:string): ?Account } +AccountGateway *--"- con" Connexion + @enduml \ No newline at end of file diff --git a/Documentation/mvc/editor.puml b/Documentation/mvc/editor.puml new file mode 100644 index 0000000..d684e5b --- /dev/null +++ b/Documentation/mvc/editor.puml @@ -0,0 +1,38 @@ +@startuml +class EditorController { + +__construct (model : TacticModel) + + openEditorFor(tactic:TacticInfo): ViewHttpResponse + + createNew(): ViewHttpResponse + + openTestEditor(courtType:CourtType): ViewHttpResponse + + createNewOfKind(type:CourtType, session:SessionHandle): ViewHttpResponse + + openEditor(id:int, session:SessionHandle): ViewHttpResponse +} +EditorController *-- "- model" TacticModel + +class TacticModel { + + TACTIC_DEFAULT_NAME:int {static}{frozen} + + __construct(tactics : TacticInfoGateway) + + makeNew(name:string, ownerId:int, type:CourtType): TacticInfo + + makeNewDefault(ownerId:int, type:CourtType): ?TacticInfo + + get(id:int): ?TacticInfo + + getLast(nb:int, ownerId:int): array + + getAll(ownerId:int): ?array + + updateName(id:int, name:string, authId:int): array + + updateContent(id:int, json:string): ?ValidationFail +} + +TacticModel *-- "- tactics" TacticInfoGateway + +class TacticInfoGateway{ + + __construct(con : Connexion) + + get(id:int): ?TacticInfo + + getLast(nb:int, ownerId:int): ?array + + getAll(ownerId:int): ?array + + insert(name:string, owner:int, type:CourtType): int + + updateName(id:int, name:string): bool + + updateContent(id:int, json:string): bool +} + +TacticInfoGateway *--"- con" Connexion + +@enduml \ No newline at end of file diff --git a/Documentation/mvc/team.puml b/Documentation/mvc/team.puml index ad5e201..8762ffe 100644 --- a/Documentation/mvc/team.puml +++ b/Documentation/mvc/team.puml @@ -1,63 +1,87 @@ @startuml -class Team { - - name: string - - picture: Url - - members: array - - + __construct(name : string, picture : string, mainColor : Colo, secondColor : Color) - + getName(): string - + getPicture(): Url - + getMainColor(): Color - + getSecondColor(): Color - + listMembers(): array -} -Team --> "- mainColor" Color -Team --> "- secondColor" Color -class Color { - - value: string - - __construct(value : string) - + getValue(): string - + from(value: string): Color - + tryFrom(value : string) : ?Color -} class TeamGateway{ - -- + + __construct(con : Connexion) + insert(name : string ,picture : string, mainColor : Color, secondColor : Color) + listByName(name : string): array + + getTeamById(id:int): ?TeamInfo + + getTeamIdByName(name:string): ?int + + deleteTeam(idTeam:int): void + + editTeam(idTeam:int, newName:string, newPicture:string, newMainColor:string, newSecondColor:string) + + getAll(user:int): array } TeamGateway *--"- con" Connexion -TeamGateway ..> Color + + +class MemberGateway{ + + + __construct(con : Connexion) + + insert(idTeam:int, userId:int, role:string): void + + getMembersOfTeam(teamId:int): array + + remove(idTeam:int, idMember:int): void + + isCoach(email:string, idTeam:int): bool + + isMemberOfTeam(idTeam:int, idCurrentUser:int): bool +} + +MemberGateway *--"- con" Connexion + +class AccountGateway{ + + __construct(con : Connexion) + + insertAccount(name:string, email:string, token:string, hash:string, profilePicture:string): int + + getRowsFromMail(email:string): ?array + + getHash(email:string): ?string + + exists(email:string): bool + + getAccountFromMail(email:string): ?Account + + getAccountFromToken(token:string): ?Account +} + +AccountGateway *--"- con" Connexion class TeamModel{ - --- + + __construct(gateway : TeamGateway) + createTeam(name : string,picture : string, mainColorValue : int, secondColorValue : int, errors : array) + + addMember(mail:string, teamId:int, role:string): int + listByName(name : string ,errors : array) : ?array - + displayTeam(id : int): Team + + getTeam(idTeam:int, idCurrentUser:int): ?Team + + deleteMember(idMember:int, teamId:int): int + + deleteTeam(email:string, idTeam:int): int + + isCoach(idTeam:int, email:string): bool + + editTeam(idTeam:int, newName:string, newPicture:string, newMainColor:string, newSecondColor:string) + + getAll(user:int): array } -TeamModel *--"- gateway" TeamGateway -TeamModel ..> Team -TeamModel ..> Color +TeamModel *--"- members" MemberGateway +TeamModel *--"- teams" TeamGateway +TeamModel *--"- teams" AccountGateway + class TeamController{ - - twig : Environement - -- - + __construct( model : TeamModel, twig : Environement) - + displaySubmitTeam() : HttpResponse - + submitTeam(request : array) : HttpResponse - + displayListTeamByName(): HttpResponse - + listTeamByName(request : array) : HttpResponse - + displayTeam(id : int): HttpResponse + + __construct( model : TeamModel) + + displayCreateTeam(session:SessionHandle): ViewHttpResponse + + displayDeleteMember(session:SessionHandle): ViewHttpResponse + + submitTeam(request:array, session:SessionHandle): HttpResponse + + displayListTeamByName(session:SessionHandle): ViewHttpResponse + + listTeamByName(request:array, session:SessionHandle): HttpResponse + + deleteTeamById(id:int, session:SessionHandle): HttpResponse + + displayTeam(id:int, session:SessionHandle): ViewHttpResponse + + displayAddMember(idTeam:int, session:SessionHandle): ViewHttpResponse + + addMember(idTeam:int, request:array, session:SessionHandle): HttpResponse + + deleteMember(idTeam:int, idMember:int, session:SessionHandle): HttpResponse + + displayEditTeam(idTeam:int, session:SessionHandle): ViewHttpResponse + + editTeam(idTeam:int, request:array, session:SessionHandle): HttpResponse } TeamController *--"- model" TeamModel + + + + class Connexion { } @enduml \ No newline at end of file diff --git a/Documentation/validation.puml b/Documentation/validation.puml index f509cf4..4595a5a 100644 --- a/Documentation/validation.puml +++ b/Documentation/validation.puml @@ -1,18 +1,20 @@ @startuml abstract class Validator { - + validate(name: string, val: mixed): array + + validate(name: string, val: mixed): array {abstract} + then(other: Validator): Validator } class ComposedValidator extends Validator { - - first: Validator - - then: Validator + __construct(first: Validator, then: Validator) - validate(name: string, val: mixed): array + + validate(name: string, val: mixed): array } +ComposedValidator -->"- first" Validator +ComposedValidator -->"- then" Validator + + class SimpleFunctionValidator extends Validator { - predicate: callable - error_factory: callable @@ -28,9 +30,9 @@ class ValidationFail implements JsonSerialize { + __construct(kind: string, message: string) + getMessage(): string + getKind(): string - + jsonSerialize() - + notFound(message: string): ValidationFail + + unauthorized(message:string): ValidationFail + + error(message:string): ValidationFail } class FieldValidationFail extends ValidationFail { @@ -49,13 +51,25 @@ class Validation { + validate(val: mixed, valName: string, failures: &array, validators: Validator...): bool } +Validation ..> Validator + class Validators { - --- + nonEmpty(): Validator + shorterThan(limit: int): Validator + userString(maxLen: int): Validator - ... + + regex(regex:string, msg:string): Validator + + hex(msg:string): Validator + + name(msg:string): Validator + + nameWithSpaces(): Validator + + lenBetween(min:int, max:int): Validator + + email(msg:string): Validator + + isInteger(): Validator + + isIntInRange(min:int, max:int): Validator + + isURL(): Validator } +Validators ..> Validator + + @enduml \ No newline at end of file diff --git a/src/Core/Model/TacticModel.php b/src/Core/Model/TacticModel.php index 7057e7f..4953fb2 100644 --- a/src/Core/Model/TacticModel.php +++ b/src/Core/Model/TacticModel.php @@ -11,7 +11,6 @@ use IQBall\Core\Validation\ValidationFail; class TacticModel { public const TACTIC_DEFAULT_NAME = "Nouvelle tactique"; - private TacticInfoGateway $tactics; /** @@ -52,13 +51,6 @@ class TacticModel { return $this->tactics->get($id); } - /** - * Return the nb last tactics created - * - * @param integer $nb - * @return array> - */ - /** * Return the nb last tactics * -- 2.36.3 From f71835352d718daa12c2811cc23c3b1ce7150e5b Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Fri, 12 Jan 2024 13:45:54 +0100 Subject: [PATCH 3/6] updated editor's mvc conception --- Documentation/Conception.md | 2 +- Documentation/mvc/editor.puml | 6 +++++ Documentation/validation.puml | 4 ++-- src/Api/Controller/APIAuthController.php | 6 ++--- src/Api/Controller/APITacticController.php | 4 ++-- src/App/Control.php | 4 ++-- src/App/Controller/AuthController.php | 10 ++++----- src/App/Controller/TeamController.php | 22 +++++++++---------- .../{Validators.php => DefaultValidators.php} | 2 +- 9 files changed, 33 insertions(+), 27 deletions(-) rename src/Core/Validation/{Validators.php => DefaultValidators.php} (99%) diff --git a/Documentation/Conception.md b/Documentation/Conception.md index 68b4cd9..177be45 100644 --- a/Documentation/Conception.md +++ b/Documentation/Conception.md @@ -64,7 +64,7 @@ et de reporter le plus d'erreurs possibles lorsqu'une requĂȘte ne valide pas le public function doPostAction(array $form): HttpResponse { $failures = []; $req = HttpRequest::from($form, $failures, [ - 'email' => [Validators::email(), Validators::isLenBetween(6, 64)] + 'email' => [DefaultValidators::email(), DefaultValidators::isLenBetween(6, 64)] ]); if (!empty($failures)) { //ou $req == null diff --git a/Documentation/mvc/editor.puml b/Documentation/mvc/editor.puml index d684e5b..3e68d33 100644 --- a/Documentation/mvc/editor.puml +++ b/Documentation/mvc/editor.puml @@ -35,4 +35,10 @@ class TacticInfoGateway{ TacticInfoGateway *--"- con" Connexion +class TacticValidator{ + + validateAccess(tacticId:int, tactic:?TacticInfo, ownerId:int): ?ValidationFail {static} +} + +EditorController ..> TacticValidator + @enduml \ No newline at end of file diff --git a/Documentation/validation.puml b/Documentation/validation.puml index 4595a5a..64ac1a5 100644 --- a/Documentation/validation.puml +++ b/Documentation/validation.puml @@ -53,7 +53,7 @@ class Validation { Validation ..> Validator -class Validators { +class DefaultValidators { + nonEmpty(): Validator + shorterThan(limit: int): Validator + userString(maxLen: int): Validator @@ -68,7 +68,7 @@ class Validators { + isURL(): Validator } -Validators ..> Validator +DefaultValidators ..> Validator diff --git a/src/Api/Controller/APIAuthController.php b/src/Api/Controller/APIAuthController.php index fc0eef6..8e6291c 100644 --- a/src/Api/Controller/APIAuthController.php +++ b/src/Api/Controller/APIAuthController.php @@ -8,7 +8,7 @@ use IQBall\Core\Http\HttpRequest; use IQBall\Core\Http\HttpResponse; use IQBall\Core\Http\JsonHttpResponse; use IQBall\Core\Model\AuthModel; -use IQBall\Core\Validation\Validators; +use IQBall\Core\Validation\DefaultValidators; class APIAuthController { private AuthModel $model; @@ -27,8 +27,8 @@ class APIAuthController { */ public function authorize(): HttpResponse { return Control::runChecked([ - "email" => [Validators::email(), Validators::lenBetween(5, 256)], - "password" => [Validators::lenBetween(6, 256)], + "email" => [DefaultValidators::email(), DefaultValidators::lenBetween(5, 256)], + "password" => [DefaultValidators::lenBetween(6, 256)], ], function (HttpRequest $req) { $failures = []; $account = $this->model->login($req["email"], $req["password"], $failures); diff --git a/src/Api/Controller/APITacticController.php b/src/Api/Controller/APITacticController.php index a116add..2156538 100644 --- a/src/Api/Controller/APITacticController.php +++ b/src/Api/Controller/APITacticController.php @@ -10,7 +10,7 @@ use IQBall\Core\Http\HttpResponse; use IQBall\Core\Http\JsonHttpResponse; use IQBall\Core\Model\TacticModel; use IQBall\Core\Validation\FieldValidationFail; -use IQBall\Core\Validation\Validators; +use IQBall\Core\Validation\DefaultValidators; /** * API endpoint related to tactics @@ -33,7 +33,7 @@ class APITacticController { */ public function updateName(int $tactic_id, Account $account): HttpResponse { return Control::runChecked([ - "name" => [Validators::lenBetween(1, 50), Validators::nameWithSpaces()], + "name" => [DefaultValidators::lenBetween(1, 50), DefaultValidators::nameWithSpaces()], ], function (HttpRequest $request) use ($tactic_id, $account) { $failures = $this->model->updateName($tactic_id, $request["name"], $account->getUser()->getId()); diff --git a/src/App/Control.php b/src/App/Control.php index 5c2fe0f..b8148bb 100644 --- a/src/App/Control.php +++ b/src/App/Control.php @@ -11,7 +11,7 @@ use IQBall\Core\Validation\Validator; class Control { /** * Runs given callback, if the request's json validates the given schema. - * @param array $schema an array of `fieldName => Validators` which represents the request object schema + * @param array $schema an array of `fieldName => DefaultValidators` which represents the request object schema * @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema. * THe callback must accept an HttpRequest, and return an HttpResponse object. * @return HttpResponse @@ -30,7 +30,7 @@ class Control { /** * Runs given callback, if the given request data array validates the given schema. * @param array $data the request's data array. - * @param array $schema an array of `fieldName => Validators` which represents the request object schema + * @param array $schema an array of `fieldName => DefaultValidators` which represents the request object schema * @param callable(HttpRequest): HttpResponse $run the callback to run if the request is valid according to the given schema. * THe callback must accept an HttpRequest, and return an HttpResponse object. * @return HttpResponse diff --git a/src/App/Controller/AuthController.php b/src/App/Controller/AuthController.php index cd89d11..4d9eebe 100644 --- a/src/App/Controller/AuthController.php +++ b/src/App/Controller/AuthController.php @@ -7,7 +7,7 @@ use IQBall\App\ViewHttpResponse; use IQBall\Core\Http\HttpRequest; use IQBall\Core\Http\HttpResponse; use IQBall\Core\Model\AuthModel; -use IQBall\Core\Validation\Validators; +use IQBall\Core\Validation\DefaultValidators; class AuthController { private AuthModel $model; @@ -32,10 +32,10 @@ class AuthController { public function register(array $request, MutableSessionHandle $session): HttpResponse { $fails = []; $request = HttpRequest::from($request, $fails, [ - "username" => [Validators::name(), Validators::lenBetween(2, 32)], - "password" => [Validators::lenBetween(6, 256)], - "confirmpassword" => [Validators::lenBetween(6, 256)], - "email" => [Validators::email(), Validators::lenBetween(5, 256)], + "username" => [DefaultValidators::name(), DefaultValidators::lenBetween(2, 32)], + "password" => [DefaultValidators::lenBetween(6, 256)], + "confirmpassword" => [DefaultValidators::lenBetween(6, 256)], + "email" => [DefaultValidators::email(), DefaultValidators::lenBetween(5, 256)], ]); if (!empty($fails)) { return ViewHttpResponse::twig("display_register.html.twig", ['fails' => $fails]); diff --git a/src/App/Controller/TeamController.php b/src/App/Controller/TeamController.php index 4ab3fd7..048d182 100644 --- a/src/App/Controller/TeamController.php +++ b/src/App/Controller/TeamController.php @@ -11,7 +11,7 @@ use IQBall\Core\Http\HttpResponse; use IQBall\Core\Model\TeamModel; use IQBall\Core\Validation\FieldValidationFail; use IQBall\Core\Validation\ValidationFail; -use IQBall\Core\Validation\Validators; +use IQBall\Core\Validation\DefaultValidators; class TeamController { private TeamModel $model; @@ -48,10 +48,10 @@ class TeamController { public function submitTeam(array $request, SessionHandle $session): HttpResponse { $failures = []; $request = HttpRequest::from($request, $failures, [ - "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], - "main_color" => [Validators::hexColor()], - "second_color" => [Validators::hexColor()], - "picture" => [Validators::isURL()], + "name" => [DefaultValidators::lenBetween(1, 32), DefaultValidators::nameWithSpaces()], + "main_color" => [DefaultValidators::hexColor()], + "second_color" => [DefaultValidators::hexColor()], + "picture" => [DefaultValidators::isURL()], ]); if (!empty($failures)) { $badFields = []; @@ -84,7 +84,7 @@ class TeamController { public function listTeamByName(array $request, SessionHandle $session): HttpResponse { $errors = []; $request = HttpRequest::from($request, $errors, [ - "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], + "name" => [DefaultValidators::lenBetween(1, 32), DefaultValidators::nameWithSpaces()], ]); if (!empty($errors) && $errors[0] instanceof FieldValidationFail) { @@ -166,7 +166,7 @@ class TeamController { ], HttpCodes::FORBIDDEN); } $request = HttpRequest::from($request, $errors, [ - "email" => [Validators::email(), Validators::lenBetween(5, 256)], + "email" => [DefaultValidators::email(), DefaultValidators::lenBetween(5, 256)], ]); if(!empty($errors)) { return ViewHttpResponse::twig('add_member.html.twig', ['badEmail' => true,'idTeam' => $idTeam]); @@ -226,10 +226,10 @@ class TeamController { } $failures = []; $request = HttpRequest::from($request, $failures, [ - "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], - "main_color" => [Validators::hexColor()], - "second_color" => [Validators::hexColor()], - "picture" => [Validators::isURL()], + "name" => [DefaultValidators::lenBetween(1, 32), DefaultValidators::nameWithSpaces()], + "main_color" => [DefaultValidators::hexColor()], + "second_color" => [DefaultValidators::hexColor()], + "picture" => [DefaultValidators::isURL()], ]); if (!empty($failures)) { $badFields = []; diff --git a/src/Core/Validation/Validators.php b/src/Core/Validation/DefaultValidators.php similarity index 99% rename from src/Core/Validation/Validators.php rename to src/Core/Validation/DefaultValidators.php index 52bc08c..b6ffc38 100644 --- a/src/Core/Validation/Validators.php +++ b/src/Core/Validation/DefaultValidators.php @@ -5,7 +5,7 @@ namespace IQBall\Core\Validation; /** * A collection of standard validators */ -class Validators { +class DefaultValidators { /** * @return Validator a validator that validates a given regex */ -- 2.36.3 From 88e0687cd267e4177a6b640acc4472169f82d350 Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Mon, 15 Jan 2024 16:20:29 +0100 Subject: [PATCH 4/6] updated the of the documentation + added a general markdown with a description for each diagram --- Documentation/Description.md | 82 +++++++++++++++++++++++++++ Documentation/architecture.puml | 60 ++++++++++++++++++++ Documentation/assets/architecture.svg | 1 + Documentation/assets/auth.svg | 1 + Documentation/assets/editor.svg | 1 + Documentation/assets/http.svg | 1 + Documentation/assets/models.svg | 1 + Documentation/assets/session.svg | 1 + Documentation/assets/team.svg | 1 + Documentation/assets/validation.svg | 1 + Documentation/http.puml | 32 ++++++++--- Documentation/session.puml | 27 +++++++++ Documentation/validation.puml | 6 ++ 13 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 Documentation/Description.md create mode 100644 Documentation/architecture.puml create mode 100644 Documentation/assets/architecture.svg create mode 100644 Documentation/assets/auth.svg create mode 100644 Documentation/assets/editor.svg create mode 100644 Documentation/assets/http.svg create mode 100644 Documentation/assets/models.svg create mode 100644 Documentation/assets/session.svg create mode 100644 Documentation/assets/team.svg create mode 100644 Documentation/assets/validation.svg create mode 100644 Documentation/session.puml diff --git a/Documentation/Description.md b/Documentation/Description.md new file mode 100644 index 0000000..ddb7dd0 --- /dev/null +++ b/Documentation/Description.md @@ -0,0 +1,82 @@ +# Welcome on the documentation's description + +## Let's get started with the architecture diagram. +![architecture diagram](./assets/architecture.svg) + +As you can see our entire application is build around three main package. +All of them contained in "src" package. +The core represent the main code of the web application. +It contains all the validation protocol, detailed below, the model of the imposed MVC architecture. +It also has a package named "data", it is a package of the structure of all the data we use in our application. +Of course there is package containing all the gateways as its name indicates. It is where we use the connection to our database. +Allowing to operate on it. + +The App now is more about the web application itself. +Having all the controllers of the MVC architecture the use the model, the validation system and the http system in the core. +It also calls the twig's views inside of App. Finally, it uses the package Session. This one replace the $_SESSION we all know in PHP. +Thanks to this we have a way cleaner use of all session's data. +Nevertheless, all the controllers call not only twig views but also react ones. +Those are present in the package "front", dispatched in several other packages. +Such as assets having all the image and stuff, model containing all the data's structure, style centralizing all css file and eventually components the last package used for the editor. + +Finally, we have the package "Api" that allows to share code and bind all the different third-hand application such as the web admin one. + +## Main class diagram. +![Class diagram](./assets/models.svg) + +You can see how our data is structured contained in the package "data" as explained right above. +There is two clear part. +First of all, the Tactic one. +We got a nice class named TacticInfo representing as it says the information about a tactic, nothing to discuss more about. +It associates an attribute of type "CourtType". This last is just an "evoluated" type of enum with some more features. +We had to do it this way because of the language PHP that doesn't implement such a thing as an enum. + +Now, let's discuss a much bigger part of the diagram. +In this part we find all the team logic. Actually, a team only have an array of members and a "TeamInfo". +The class "TeamInfo" exist to allows to split the data of the members. +The type Team does only link the information about a team and its members. +Talking about them, their class indicate what role they have (either Coach or Player) in the team. +Because a member is registered in the app, therefore he is a user of it. Represented by the type of the same name. +This class does only contain all the user's basic information. +The last class we have is the Account. It could directly be incorporated in User but we decided to split it the same way we did for the team. +Then, Account only has a user and a token which is an identifier. + +## Validation's class diagram +![validation's class diagram](./assets/Validation.svg) + +We implemented our own validation system, here it is! +For the validation methods (for instance those in DefaultValidators) we use lambda to instantiate a Validator. +In general, we use the implementation "SimpleFunctionValidator". +We reconize the strategy pattern. Indeed, we need a family of algorithms because we have many classes that only differ by the way they validate. +Futhermore, you may have notices the ComposedValidator that allows to chain several Validator. +We naturally used the composite pattern to solve this problem. +The other part of the diagram is about the failure a specific field's validation. +We have a concrete class to return a something more general. All the successors are just more precise about the failure. + +## Http's class diagram +![Http's class diagram](./assets/http.svg) +It were we centralize what the app can render, and what the api can receive. +Then, we got the "basic" response (HttpResponse) that just render a HttpCodes. +We have two successors for now. ViewHttpResponse render not only a code but also a view, either react or twig ones. +Finally, we have the JsonHttpResponse that renders, as it's name says, some Json. + +## Session's class diagram +![Session's class diagram](./assets/session.svg) + +It encapsulates the PHP's array "$_SESSION" and kind of replaces it. With two interfaces that dictate how a session should be handled, and same for a mutable one. + +## Model View Controller +All class diagram, separated by their range of action, of the imposed MVC architecture. +All of them have a controller that validates entries with the validation system and check the permission the user has,and whether or not actually do the action. +These controllers are composed by a Model that handle the pure data and is the point of contact between these and the gateways. +Speaking of which, Gateways are composing Models. They use the connection class to access the database and send their query. + +### Team +![team's mvc](./assets/team.svg) + +### Editor +![editor's mvc](./assets/editor.svg) + +### Authentification +![auth's mvc](./assets/auth.svg) + diff --git a/Documentation/architecture.puml b/Documentation/architecture.puml new file mode 100644 index 0000000..3e8f98f --- /dev/null +++ b/Documentation/architecture.puml @@ -0,0 +1,60 @@ +@startuml +'https://plantuml.com/component-diagram + +package front{ + package assets + package components + package model + package style + package views +} + +database sql{ + +} + +package src { + + package "Api"{ + + } + + package "App" { + package Controller + package Session + package Views + } + + package Core{ + package Data + package Gateway + package Http + package Model + package Validation + [Connection] + } + +} + +[sql] -- [Connection] + +[views] -- [style] +[views] -- [components] +[views] -- [assets] +[views] -- [model] + +[Gateway] -- [Connection] + +[Validation] -- [Controller] +[Controller] -- [Session] +[Controller] -- [Http] +[Controller] -- [Views] +[Controller] -- [views] +[Controller] -- [Model] +[Model] -- [Gateway] + +[Api] -- [Validation] +[Api] -- [Model] +[Api] -- [Http] + +@enduml \ No newline at end of file diff --git a/Documentation/assets/architecture.svg b/Documentation/assets/architecture.svg new file mode 100644 index 0000000..a93591b --- /dev/null +++ b/Documentation/assets/architecture.svg @@ -0,0 +1 @@ +frontsrcAppCoreassetscomponentsmodelstyleviewssqlApiControllerSessionViewsDataGatewayHttpModelValidationConnection \ No newline at end of file diff --git a/Documentation/assets/auth.svg b/Documentation/assets/auth.svg new file mode 100644 index 0000000..b25a316 --- /dev/null +++ b/Documentation/assets/auth.svg @@ -0,0 +1 @@ +AuthController__construct (model : AuthModel)displayRegister() : HttpResponseregister(request : array,session : MutableSessionHandle) : HttpResponsedisplayLogin() : HttpResponselogin(request : array , session : MutableSessionHandle) : HttpResponseAuthModel__construct(gateway : AccountGateway)register(username:string, password:string, confirmPassword:string, email:string, &failures:array): ?Account + generateToken() : stringgenerateToken(): stringlogin(email:string, password:string, &failures:array): ?AccountAccountGateway__construct(con : Connexion)insertAccount(name:string, email:string, token:string, hash:string, profilePicture:string): intgetRowsFromMail(email:string): ?arraygetHash(email:string): ?stringexists(email:string): boolgetAccountFromMail(email:string): ?AccountgetAccountFromToken(token:string): ?AccountConnexion- model- gateway- con \ No newline at end of file diff --git a/Documentation/assets/editor.svg b/Documentation/assets/editor.svg new file mode 100644 index 0000000..ddc8750 --- /dev/null +++ b/Documentation/assets/editor.svg @@ -0,0 +1 @@ +EditorController__construct (model : TacticModel)openEditorFor(tactic:TacticInfo): ViewHttpResponsecreateNew(): ViewHttpResponseopenTestEditor(courtType:CourtType): ViewHttpResponsecreateNewOfKind(type:CourtType, session:SessionHandle): ViewHttpResponseopenEditor(id:int, session:SessionHandle): ViewHttpResponseTacticModelTACTIC_DEFAULT_NAME:int {frozen}__construct(tactics : TacticInfoGateway)makeNew(name:string, ownerId:int, type:CourtType): TacticInfomakeNewDefault(ownerId:int, type:CourtType): ?TacticInfoget(id:int): ?TacticInfogetLast(nb:int, ownerId:int): arraygetAll(ownerId:int): ?arrayupdateName(id:int, name:string, authId:int): arrayupdateContent(id:int, json:string): ?ValidationFailTacticInfoGateway__construct(con : Connexion)get(id:int): ?TacticInfogetLast(nb:int, ownerId:int): ?arraygetAll(ownerId:int): ?arrayinsert(name:string, owner:int, type:CourtType): intupdateName(id:int, name:string): boolupdateContent(id:int, json:string): boolConnexionTacticValidatorvalidateAccess(tacticId:int, tactic:?TacticInfo, ownerId:int): ?ValidationFail- model- tactics- con \ No newline at end of file diff --git a/Documentation/assets/http.svg b/Documentation/assets/http.svg new file mode 100644 index 0000000..67ddc0a --- /dev/null +++ b/Documentation/assets/http.svg @@ -0,0 +1 @@ +HttpRequestdata: array__construct(data: array) offsetExists(offset: mixed): booloffsetGet(offset: mixed): mixedoffsetSet(offset: mixed, value: mixed)offsetUnset(offset: mixed) from(request: array, fails: &array, schema: array): HttpRequestfromPayload(fails: &array, schema: array): HttpRequestArrayAccessHttpResponsecode: intheaders : array__construct(code: int,headers:array)getCode(): intgetHeaders(): arrayredirect(url:string, code:int): HttpResponsefromCode(code: int): HttpResponseJsonHttpResponsepayload: mixed__construct(payload: mixed, code: int)getJson(): stringViewHttpResponseTWIG_VIEW: int {frozen}REACT_VIEW: int {frozen} file: stringarguments: arraykind: int__construct(kind: int, file: string, arguments: array, code: int)getViewKind(): intgetFile(): stringgetArguments(): array twig(file: string, arguments: array, code: int): ViewHttpResponsereact(file: string, arguments: array, code: int): ViewHttpResponseInto src/AppHttpCodesOK : int {frozen}FOUND : int {frozen}BAD_REQUEST : int {frozen}UNAUTHORIZED : int {frozen}FORBIDDEN : int {frozen}NOT_FOUND : int {frozen} \ No newline at end of file diff --git a/Documentation/assets/models.svg b/Documentation/assets/models.svg new file mode 100644 index 0000000..e691fa7 --- /dev/null +++ b/Documentation/assets/models.svg @@ -0,0 +1 @@ +TacticInfoid: intname: stringcreationDate: stringownerId: stringcontent: string__construct(id:int,name:string,creationDate:int,ownerId:int,courtType:CourtType,content:string)getId(): intgetOwnerId(): intgetCreationTimestamp(): intgetName(): stringgetContent(): stringgetCourtType() : CourtTypeCourtTypevalue : intCOURT_PLAIN : int {frozen}COURT_HALF : int {frozen}__construct(val:int)plain() : CourtTypehalf() : CourtTypename() : stringfromName(name:string) : CourtTypeisPlain() : boolisHalf() : boolBasically an evoluated enumAccounttoken: string__construct(token:string,user:User)getUser() : UsergetToken() : stringUserid : intname : stringemail : stringprofilePicture : string__construct(id : int,name : string,email: string,profilePicture:string)getId() : idgetName() : stringgetEmail() : stringgetProfilePicture() : stringMemberteamId: introle : string__construct(role : string)getUser(): UsergetTeamId(): intgetRole(): stringMember's role is either "Coach" or "Player"TeamInfoname: stringpicture: stringmainColor : stringsecondColor : string__construct(id:int,name:string,picture:string,mainColor:string,secondColor:string)getName(): stringgetPicture(): stringgetMainColor(): stringgetSecondColor(): stringBoth team's color are the hex code of the colorTeam__construct(info:TeamInfo,members: Member[])getInfo(): TeamInfolistMembers(): Member[]- courtType- user- user- info- members * \ No newline at end of file diff --git a/Documentation/assets/session.svg b/Documentation/assets/session.svg new file mode 100644 index 0000000..85ec404 --- /dev/null +++ b/Documentation/assets/session.svg @@ -0,0 +1 @@ +SessionHandlegetInitialTarget(): ?stringgetAccount(): ?AccountMutableSessionHandlesetInitialTarget(url:?string): voidsetAccount(account:Account): voiddestroy(): voidPhpSessionHandleinit(): selfgetAccount(): ?AccountgetInitialTarget(): ?stringsetAccount(account:Account): voidsetInitialTarget(url:?string): voiddestroy(): void \ No newline at end of file diff --git a/Documentation/assets/team.svg b/Documentation/assets/team.svg new file mode 100644 index 0000000..edf8a32 --- /dev/null +++ b/Documentation/assets/team.svg @@ -0,0 +1 @@ +TeamGateway__construct(con : Connexion)insert(name : string ,picture : string, mainColor : Color, secondColor : Color)listByName(name : string): arraygetTeamById(id:int): ?TeamInfogetTeamIdByName(name:string): ?intdeleteTeam(idTeam:int): voideditTeam(idTeam:int, newName:string, newPicture:string, newMainColor:string, newSecondColor:string)getAll(user:int): arrayConnexionMemberGateway__construct(con : Connexion)insert(idTeam:int, userId:int, role:string): voidgetMembersOfTeam(teamId:int): arrayremove(idTeam:int, idMember:int): voidisCoach(email:string, idTeam:int): boolisMemberOfTeam(idTeam:int, idCurrentUser:int): boolAccountGateway__construct(con : Connexion)insertAccount(name:string, email:string, token:string, hash:string, profilePicture:string): intgetRowsFromMail(email:string): ?arraygetHash(email:string): ?stringexists(email:string): boolgetAccountFromMail(email:string): ?AccountgetAccountFromToken(token:string): ?AccountTeamModel__construct(gateway : TeamGateway)createTeam(name : string,picture : string, mainColorValue : int, secondColorValue : int, errors : array)addMember(mail:string, teamId:int, role:string): intlistByName(name : string ,errors : array) : ?arraygetTeam(idTeam:int, idCurrentUser:int): ?TeamdeleteMember(idMember:int, teamId:int): intdeleteTeam(email:string, idTeam:int): intisCoach(idTeam:int, email:string): booleditTeam(idTeam:int, newName:string, newPicture:string, newMainColor:string, newSecondColor:string)getAll(user:int): arrayTeamController__construct( model : TeamModel)displayCreateTeam(session:SessionHandle): ViewHttpResponsedisplayDeleteMember(session:SessionHandle): ViewHttpResponsesubmitTeam(request:array, session:SessionHandle): HttpResponsedisplayListTeamByName(session:SessionHandle): ViewHttpResponselistTeamByName(request:array, session:SessionHandle): HttpResponsedeleteTeamById(id:int, session:SessionHandle): HttpResponsedisplayTeam(id:int, session:SessionHandle): ViewHttpResponsedisplayAddMember(idTeam:int, session:SessionHandle): ViewHttpResponseaddMember(idTeam:int, request:array, session:SessionHandle): HttpResponsedeleteMember(idTeam:int, idMember:int, session:SessionHandle): HttpResponsedisplayEditTeam(idTeam:int, session:SessionHandle): ViewHttpResponseeditTeam(idTeam:int, request:array, session:SessionHandle): HttpResponse- con- con- con- members- teams- teams- model \ No newline at end of file diff --git a/Documentation/assets/validation.svg b/Documentation/assets/validation.svg new file mode 100644 index 0000000..7f19c31 --- /dev/null +++ b/Documentation/assets/validation.svg @@ -0,0 +1 @@ +Validatorvalidate(name: string, val: mixed): arraythen(other: Validator): ValidatorComposedValidator__construct(first: Validator, then: Validator)validate(name: string, val: mixed): arraySimpleFunctionValidatorpredicate: callableerror_factory: callable__construct(predicate: callable, errorsFactory: callable)validate(name: string, val: mixed): arrayValidationFailkind: stringmessage: string__construct(kind: string, message: string)getMessage(): stringgetKind(): stringnotFound(message: string): ValidationFailunauthorized(message:string): ValidationFailerror(message:string): ValidationFailJsonSerializeFieldValidationFailfieldName: string__construct(fieldName: string, message: string)getFieldName(): stringjsonSerialize() invalidChars(fieldName: string): FieldValidationFailempty(fieldName: string): FieldValidationFailmissing(fieldName: string): FieldValidationFailValidation+ validate(val: mixed, valName: string, failures: &array, validators: Validator...): boolDefaultValidatorsnonEmpty(): ValidatorshorterThan(limit: int): ValidatoruserString(maxLen: int): Validatorregex(regex:string, msg:string): Validatorhex(msg:string): Validatorname(msg:string): ValidatornameWithSpaces(): ValidatorlenBetween(min:int, max:int): Validatoremail(msg:string): ValidatorisInteger(): ValidatorisIntInRange(min:int, max:int): ValidatorisURL(): ValidatorFunctionValidatorvalidate_fn: callable__construct(validate_fn:callable)validate(name:string, val:mixed): array- first- then \ No newline at end of file diff --git a/Documentation/http.puml b/Documentation/http.puml index 2f8e22b..e8fd478 100644 --- a/Documentation/http.puml +++ b/Documentation/http.puml @@ -15,37 +15,51 @@ class HttpRequest implements ArrayAccess { class HttpResponse { - code: int - + __construct(code: int) + - headers : array + + __construct(code: int,headers:array) + getCode(): int - - fromCode(code: int): HttpResponse + + getHeaders(): array + + redirect(url:string, code:int): HttpResponse {static} + + fromCode(code: int): HttpResponse {static} } class JsonHttpResponse extends HttpResponse { - payload: mixed - + __construct(payload: mixed, code: int = HttpCodes::OK) + + __construct(payload: mixed, code: int) + getJson(): string } class ViewHttpResponse extends HttpResponse { - + TWIG_VIEW: int {frozen} - + REACT_VIEW: int {frozen} + + TWIG_VIEW: int {frozen} {static} + + REACT_VIEW: int {frozen} {static} - file: string - arguments: array - kind: int - - __construct(kind: int, file: string, arguments: array, code: int = HttpCodes::OK) + - __construct(kind: int, file: string, arguments: array, code: int) + getViewKind(): int + getFile(): string + getArguments(): array - + twig(file: string, arguments: array, code: int = HttpCodes::OK): ViewHttpResponse - + react(file: string, arguments: array, code: int = HttpCodes::OK): ViewHttpResponse + + twig(file: string, arguments: array, code: int): ViewHttpResponse + + react(file: string, arguments: array, code: int): ViewHttpResponse } note right of ViewHttpResponse Into src/App end note +class HttpCodes{ + + OK : int {static} {frozen} + + FOUND : int {static} {frozen} + + BAD_REQUEST : int {static} {frozen} + + UNAUTHORIZED : int {static} {frozen} + + FORBIDDEN : int {static} {frozen} + + NOT_FOUND : int {static} {frozen} +} + +HttpCodes <.. ViewHttpResponse +HttpCodes <.. HttpResponse + @enduml \ No newline at end of file diff --git a/Documentation/session.puml b/Documentation/session.puml new file mode 100644 index 0000000..ccc8568 --- /dev/null +++ b/Documentation/session.puml @@ -0,0 +1,27 @@ +@startuml + +interface SessionHandle{ + + getInitialTarget(): ?string {abstract} + + getAccount(): ?Account {abstract} +} + +interface MutableSessionHandle{ + + setInitialTarget(url:?string): void + + setAccount(account:Account): void + + destroy(): void +} + +class PhpSessionHandle{ + + init(): self {static} + + getAccount(): ?Account + + getInitialTarget(): ?string + + setAccount(account:Account): void + + setInitialTarget(url:?string): void + + destroy(): void +} + + +PhpSessionHandle ..|> MutableSessionHandle +MutableSessionHandle ..|> SessionHandle + +@enduml \ No newline at end of file diff --git a/Documentation/validation.puml b/Documentation/validation.puml index 64ac1a5..77a8e60 100644 --- a/Documentation/validation.puml +++ b/Documentation/validation.puml @@ -70,6 +70,12 @@ class DefaultValidators { DefaultValidators ..> Validator +class FunctionValidator{ + - validate_fn: callable + + __construct(validate_fn:callable) + + validate(name:string, val:mixed): array +} +Validator <|-- FunctionValidator @enduml \ No newline at end of file -- 2.36.3 From c596bfba719361b5f44c554286dcaf3f8c22b59f Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Mon, 15 Jan 2024 16:42:07 +0100 Subject: [PATCH 5/6] Fixed bugs --- src/App/Controller/AuthController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/App/Controller/AuthController.php b/src/App/Controller/AuthController.php index 386f896..3d16733 100644 --- a/src/App/Controller/AuthController.php +++ b/src/App/Controller/AuthController.php @@ -32,10 +32,10 @@ class AuthController { public function register(array $request, MutableSessionHandle $session): HttpResponse { $fails = []; HttpRequest::from($request, $fails, [ - "username" => [Validators::name(), Validators::lenBetween(2, 32)], - "password" => [Validators::lenBetween(6, 256)], - "confirmpassword" => [Validators::lenBetween(6, 256)], - "email" => [Validators::email(), Validators::lenBetween(5, 256)], + "username" => [DefaultValidators::name(), DefaultValidators::lenBetween(2, 32)], + "password" => [DefaultValidators::lenBetween(6, 256)], + "confirmpassword" => [DefaultValidators::lenBetween(6, 256)], + "email" => [DefaultValidators::email(), DefaultValidators::lenBetween(5, 256)], ]); if (!empty($fails)) { -- 2.36.3 From d9862b85c0240b520e5c473368173c021f96315f Mon Sep 17 00:00:00 2001 From: "mael.daim" Date: Tue, 16 Jan 2024 09:54:21 +0100 Subject: [PATCH 6/6] applied required review's changement --- Documentation/Description.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Documentation/Description.md b/Documentation/Description.md index ddb7dd0..fee90c5 100644 --- a/Documentation/Description.md +++ b/Documentation/Description.md @@ -21,7 +21,7 @@ Such as assets having all the image and stuff, model containing all the data's s Finally, we have the package "Api" that allows to share code and bind all the different third-hand application such as the web admin one. -## Main class diagram. +## Main data class diagram. ![Class diagram](./assets/models.svg) You can see how our data is structured contained in the package "data" as explained right above. @@ -33,7 +33,7 @@ We had to do it this way because of the language PHP that doesn't implement such Now, let's discuss a much bigger part of the diagram. In this part we find all the team logic. Actually, a team only have an array of members and a "TeamInfo". -The class "TeamInfo" exist to allows to split the data of the members. +The class "TeamInfo" only exists to split the team's information data (name, id etc) from the members. The type Team does only link the information about a team and its members. Talking about them, their class indicate what role they have (either Coach or Player) in the team. Because a member is registered in the app, therefore he is a user of it. Represented by the type of the same name. @@ -42,14 +42,14 @@ The last class we have is the Account. It could directly be incorporated in User Then, Account only has a user and a token which is an identifier. ## Validation's class diagram -![validation's class diagram](./assets/Validation.svg) +![validation's class diagram](./assets/validation.svg) We implemented our own validation system, here it is! For the validation methods (for instance those in DefaultValidators) we use lambda to instantiate a Validator. In general, we use the implementation "SimpleFunctionValidator". We reconize the strategy pattern. Indeed, we need a family of algorithms because we have many classes that only differ by the way they validate. -Futhermore, you may have notices the ComposedValidator that allows to chain several Validator. -We naturally used the composite pattern to solve this problem. +Futhermore, you may have notices the ComposedValidator that allows to chain several Validator. +We can see that this system uses the composite pattern The other part of the diagram is about the failure a specific field's validation. We have a concrete class to return a something more general. All the successors are just more precise about the failure. @@ -63,8 +63,7 @@ Finally, we have the JsonHttpResponse that renders, as it's name says, some Json ## Session's class diagram ![Session's class diagram](./assets/session.svg) -It encapsulates the PHP's array "$_SESSION" and kind of replaces it. With two interfaces that dictate how a session should be handled, and same for a mutable one. - +It encapsulates the PHP's array "$_SESSION". With two interfaces that dictate how a session should be handled, and same for a mutable one. ## Model View Controller All class diagram, separated by their range of action, of the imposed MVC architecture. All of them have a controller that validates entries with the validation system and check the permission the user has,and whether or not actually do the action. -- 2.36.3