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