divide src in three subdirs, exploded FrontController into index.php and api/index.php
continuous-integration/drone/push Build is passing Details

pull/22/head
maxime.batista 1 year ago
parent 36051ebd83
commit de75577f3d

@ -1,7 +1,7 @@
{ {
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"App\\": "src/" "IQBall\\": "src/"
} }
}, },
"require": { "require": {

@ -3,13 +3,10 @@ parameters:
level: 6 level: 6
paths: paths:
- src - src
- public
scanFiles: scanFiles:
- config.php - config.php
- sql/database.php - sql/database.php
- profiles/dev-config-profile.php - profiles/dev-config-profile.php
- profiles/prod-config-profile.php - profiles/prod-config-profile.php
- public/api/index.php
excludePaths: excludePaths:
- src/react-display-file.php - src/App/react-display-file.php
- public/api/index.php

@ -5,84 +5,39 @@ require "../../vendor/autoload.php";
require "../../sql/database.php"; require "../../sql/database.php";
require "../utils.php"; require "../utils.php";
use App\Connexion; use IQBall\Api\ApiAction;
use App\Controller\Api\APIAuthController; use IQBall\Core\Connection;
use App\Controller\Api\APITacticController; use IQBall\Api\APIAuthController;
use App\Data\Account; use IQBall\Api\APITacticController;
use App\Gateway\AccountGateway; use IQBall\Core\Data\Account;
use App\Gateway\TacticInfoGateway; use IQBall\Core\Gateway\AccountGateway;
use App\Http\HttpResponse; use IQBall\Core\Gateway\TacticInfoGateway;
use App\Http\JsonHttpResponse; use IQBall\Core\Http\HttpResponse;
use App\Http\ViewHttpResponse; use IQBall\Core\Http\JsonHttpResponse;
use App\Model\AuthModel; use IQBall\Core\Http\ViewHttpResponse;
use App\Model\TacticModel; use IQBall\Core\Model\AuthModel;
use App\Session\PhpSessionHandle; use IQBall\Core\Model\TacticModel;
use App\Validation\ValidationFail; use IQBall\Core\Session\PhpSessionHandle;
use IQBall\Core\Validation\ValidationFail;
function getTacticController(): APITacticController { function getTacticController(): APITacticController {
return new APITacticController(new TacticModel(new TacticInfoGateway(new Connexion(get_database())))); return new APITacticController(new TacticModel(new TacticInfoGateway(new Connection(get_database()))));
} }
function getAuthController(): APIAuthController { function getAuthController(): APIAuthController {
return new APIAuthController(new AuthModel(new AccountGateway(new Connexion(get_database())))); return new APIAuthController(new AuthModel(new AccountGateway(new Connection(get_database()))));
} }
/** function getRoutes(): AltoRouter {
* A Front controller action $router = new AltoRouter();
*/ $router->setBasePath(get_public_path() . "/api");
//TODO workaround for generic Action
class ApiAction {
/**
* @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 { $router->map("POST", "/tactic/[i:id]/edit/name", ApiAction::auth(fn(int $id, Account $acc) => getTacticController()->updateName($id, $acc)));
return $this->isAuthRequired; $router->map("GET", "/tactic/[i:id]", ApiAction::auth(fn(int $id, Account $acc) => getTacticController()->getTacticInfo($id, $acc)));
} $router->map("POST", "/tactic/new", ApiAction::auth(fn(Account $acc) => getTacticController()->newTactic($acc)));
$router->map("POST", "/auth", ApiAction::noAuth(fn() => getAuthController()->authorize()));
/** return $router;
* @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 ApiAction an action that does not require to have an authorization.
*/
public static function noAuth(callable $action): ApiAction {
return new ApiAction($action, false);
}
/**
* @param callable(mixed[]): HttpResponse $action
* @return ApiAction an action that does require to have an authorization.
*/
public static function auth(callable $action): ApiAction {
return new ApiAction($action, true);
}
} }
/** /**
@ -97,7 +52,7 @@ function handleMatch(array $match): HttpResponse {
$action = $match['target']; $action = $match['target'];
if (!$action instanceof ApiAction) { if (!$action instanceof ApiAction) {
throw new Exception("routed action is not an Action object."); throw new Exception("routed action is not an AppAction object.");
} }
$auth = null; $auth = null;
@ -122,27 +77,23 @@ function tryGetAuthorization(): ?Account {
} }
$token = $headers['Authorization']; $token = $headers['Authorization'];
$gateway = new AccountGateway(new Connexion(get_database())); $gateway = new AccountGateway(new Connection(get_database()));
return $gateway->getAccountFromToken($token); return $gateway->getAccountFromToken($token);
} }
$router = new AltoRouter(); function render(HttpResponse $response): void {
$router->setBasePath(get_public_path() . "/api"); http_response_code($response->getCode());
$router->map("POST", "/tactic/[i:id]/edit/name", ApiAction::auth(fn(int $id, Account $acc) => getTacticController()->updateName($id, $acc)));
$router->map("GET", "/tactic/[i:id]", ApiAction::auth(fn(int $id, Account $acc) => getTacticController()->getTacticInfo($id, $acc)));
$router->map("POST", "/tactic/new", ApiAction::auth(fn(Account $acc) => getTacticController()->newTactic($acc)));
$router->map("POST", "/auth", ApiAction::noAuth(fn() => getAuthController()->authorize()));
$match = $router->match();
foreach ($response->getHeaders() as $header => $value) {
header("$header: $value");
}
$response = handleMatch($match); if ($response instanceof JsonHttpResponse) {
http_response_code($response->getCode()); header('Content-type: application/json');
echo $response->getJson();
if ($response instanceof JsonHttpResponse) { } elseif ($response instanceof ViewHttpResponse) {
header('Content-type: application/json'); throw new Exception("API returned a view http response.");
echo $response->getJson(); }
} elseif ($response instanceof ViewHttpResponse) {
throw new Exception("API returned a view http response.");
} }
render(handleMatch(getRoutes()->match()));

@ -4,96 +4,157 @@
require "../vendor/autoload.php"; require "../vendor/autoload.php";
require "../config.php"; require "../config.php";
require "../sql/database.php"; require "../sql/database.php";
require "utils.php"; require "../src/utils.php";
require "../src/react-display.php"; require "../src/App/react-display.php";
use App\Controller\AuthController; use IQBall\App\AppAction;
use App\Controller\EditorController; use IQBall\App\Controller\AuthController;
use App\Controller\Route\Action; use IQBall\App\Controller\EditorController;
use App\Controller\Route\FrontController; use IQBall\App\Controller\TeamController;
use App\Controller\TeamController; use IQBall\App\Controller\UserController;
use App\Controller\UserController; use IQBall\App\Controller\VisualizerController;
use App\Controller\VisualizerController; use IQBall\Core\Connection;
use App\Gateway\AccountGateway; use IQBall\Core\Gateway\AccountGateway;
use App\Gateway\TacticInfoGateway; use IQBall\Core\Gateway\TacticInfoGateway;
use App\Gateway\TeamGateway; use IQBall\Core\Gateway\TeamGateway;
use App\Model\AuthModel; use IQBall\Core\Http\HttpCodes;
use App\Model\TacticModel; use IQBall\Core\Http\HttpResponse;
use App\Model\TeamModel; use IQBall\Core\Http\JsonHttpResponse;
use App\Session\MutableSessionHandle; use IQBall\Core\Http\ViewHttpResponse;
use App\Session\PhpSessionHandle; use IQBall\Core\Model\AuthModel;
use App\Connexion; use IQBall\Core\Model\TacticModel;
use App\Session\SessionHandle; use IQBall\Core\Model\TeamModel;
use IQBall\Core\Session\MutableSessionHandle;
$connexion = new Connexion(get_database()); use IQBall\Core\Session\PhpSessionHandle;
use IQBall\Core\Session\SessionHandle;
use IQBall\Core\Validation\ValidationFail;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\Loader\FilesystemLoader;
function getConnection(): Connection {
return new Connection(get_database());
}
function getUserController(): UserController { function getUserController(): UserController {
global $connexion; return new UserController(new TacticModel(new TacticInfoGateway(getConnection())));
return new UserController(new TacticModel(new TacticInfoGateway($connexion)));
} }
function getVisualizerController(): VisualizerController { function getVisualizerController(): VisualizerController {
global $connexion; return new VisualizerController(new TacticModel(new TacticInfoGateway(getConnection())));
return new VisualizerController(new TacticModel(new TacticInfoGateway($connexion)));
} }
function getEditorController(): EditorController { function getEditorController(): EditorController {
global $connexion; return new EditorController(new TacticModel(new TacticInfoGateway(getConnection())));
return new EditorController(new TacticModel(new TacticInfoGateway($connexion)));
} }
function getTeamController(): TeamController { function getTeamController(): TeamController {
global $connexion; return new TeamController(new TeamModel(new TeamGateway(getConnection())));
return new TeamController(new TeamModel(new TeamGateway($connexion)));
} }
function getAuthController(): AuthController { function getAuthController(): AuthController {
global $connexion; return new AuthController(new AuthModel(new AccountGateway(getConnection())));
return new AuthController(new AuthModel(new AccountGateway($connexion)));
} }
function initFrontController(FrontController $fc) { function getRoutes(): AltoRouter {
global $basePath;
$ar = new AltoRouter();
$ar->setBasePath($basePath);
//authentication //authentication
$fc->addRoute("GET", "/login", Action::noAuth(fn() => getAuthController()->displayLogin())); $ar->map("GET", "/login", AppAction::noAuth(fn() => getAuthController()->displayLogin()));
$fc->addRoute("GET", "/register", Action::noAuth(fn() => getAuthController()->displayRegister())); $ar->map("GET", "/register", AppAction::noAuth(fn() => getAuthController()->displayRegister()));
$fc->addRoute("POST", "/login", Action::noAuth(fn(SessionHandle $s) => getAuthController()->confirmLogin($_POST, $s))); $ar->map("POST", "/login", AppAction::noAuth(fn(SessionHandle $s) => getAuthController()->confirmLogin($_POST, $s)));
$fc->addRoute("POST", "/register", Action::noAuth(fn(SessionHandle $s) => getAuthController()->confirmRegister($_POST, $s))); $ar->map("POST", "/register", AppAction::noAuth(fn(SessionHandle $s) => getAuthController()->confirmRegister($_POST, $s)));
//user-related //user-related
$fc->addRoute("GET", "/home", Action::auth(fn(SessionHandle $s) => getUserController()->home($s))); $ar->map("GET", "/home", AppAction::auth(fn(SessionHandle $s) => getUserController()->home($s)));
$fc->addRoute("GET", "/settings", Action::auth(fn(SessionHandle $s) => getUserController()->settings($s))); $ar->map("GET", "/settings", AppAction::auth(fn(SessionHandle $s) => getUserController()->settings($s)));
//tactic-related //tactic-related
$fc->addRoute("GET", "/tactic/[i:id]/view", Action::auth(fn(int $id, SessionHandle $s) => getVisualizerController()->visualize($id, $s))); $ar->map("GET", "/tactic/[i:id]/view", AppAction::auth(fn(int $id, SessionHandle $s) => getVisualizerController()->visualize($id, $s)));
$fc->addRoute("GET", "/tactic/[i:id]/edit", Action::auth(fn(int $id, SessionHandle $s) => getEditorController()->edit($id, $s))); $ar->map("GET", "/tactic/[i:id]/edit", AppAction::auth(fn(int $id, SessionHandle $s) => getEditorController()->edit($id, $s)));
$fc->addRoute("GET", "/tactic/new", Action::auth(fn(SessionHandle $s) => getEditorController()->createNew($s))); $ar->map("GET", "/tactic/new", AppAction::auth(fn(SessionHandle $s) => getEditorController()->createNew($s)));
//team-related //team-related
$fc->addRoute("GET", "/team/new", Action::auth(fn(SessionHandle $s) => getTeamController()->displayCreateTeam($s))); $ar->map("GET", "/team/new", AppAction::auth(fn(SessionHandle $s) => getTeamController()->displayCreateTeam($s)));
$fc->addRoute("POST", "/team/new", Action::auth(fn(SessionHandle $s) => getTeamController()->submitTeam($_POST, $s))); $ar->map("POST", "/team/new", AppAction::auth(fn(SessionHandle $s) => getTeamController()->submitTeam($_POST, $s)));
$fc->addRoute("GET", "/team/search", Action::auth(fn(SessionHandle $s) => getTeamController()->displayListTeamByName($s))); $ar->map("GET", "/team/search", AppAction::auth(fn(SessionHandle $s) => getTeamController()->displayListTeamByName($s)));
$fc->addRoute("POST", "/team/search", Action::auth(fn(SessionHandle $s) => getTeamController()->listTeamByName($_POST, $s))); $ar->map("POST", "/team/search", AppAction::auth(fn(SessionHandle $s) => getTeamController()->listTeamByName($_POST, $s)));
$fc->addRoute("GET", "/team/[i:id]", Action::auth(fn(int $id, SessionHandle $s) => getTeamController()->displayTeam($id, $s))); $ar->map("GET", "/team/[i:id]", AppAction::auth(fn(int $id, SessionHandle $s) => getTeamController()->displayTeam($id, $s)));
$fc->addRoute("GET", "/team/members/add", Action::auth(fn(SessionHandle $s) => getTeamController()->displayAddMember($s))); $ar->map("GET", "/team/members/add", AppAction::auth(fn(SessionHandle $s) => getTeamController()->displayAddMember($s)));
$fc->addRoute("POST", "/team/members/add", Action::auth(fn(SessionHandle $s) => getTeamController()->addMember($_POST, $s))); $ar->map("POST", "/team/members/add", AppAction::auth(fn(SessionHandle $s) => getTeamController()->addMember($_POST, $s)));
$fc->addRoute("GET", "/team/members/remove", Action::auth(fn(SessionHandle $s) => getTeamController()->displayDeleteMember($s))); $ar->map("GET", "/team/members/remove", AppAction::auth(fn(SessionHandle $s) => getTeamController()->displayDeleteMember($s)));
$fc->addRoute("POST", "/team/members/remove", Action::auth(fn(SessionHandle $s) => getTeamController()->deleteMember($_POST, $s))); $ar->map("POST", "/team/members/remove", AppAction::auth(fn(SessionHandle $s) => getTeamController()->deleteMember($_POST, $s)));
return $ar;
} }
//this is a global variable function render(HttpResponse $response): void {
$basePath = get_public_path(); http_response_code($response->getCode());
foreach ($response->getHeaders() as $header => $value) {
header("$header: $value");
}
function run() { if ($response instanceof ViewHttpResponse) {
global $basePath; renderView($response);
} elseif ($response instanceof JsonHttpResponse) {
header('Content-type: application/json');
echo $response->getJson();
}
}
$fc = new FrontController($basePath); function renderView(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;
}
}
initFrontController($fc); function runAction(AppAction $action, array $params, MutableSessionHandle $session): HttpResponse {
$fc->run(PhpSessionHandle::init()); global $basePath;
if ($action->isAuthRequired()) {
$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($basePath . "/login");
}
}
return $action->run($params, $session);
} }
function runMatch(array $match, MutableSessionHandle $session): HttpResponse {
if (!$match) {
return ViewHttpResponse::twig("error.html.twig", [
'failures' => [ValidationFail::notFound("Could not find page ${_SERVER['REQUEST_URI']}.")],
], HttpCodes::NOT_FOUND);
}
run(); return runAction($match['target'], $match['params'], $session);
}
//this is a global variable
$basePath = get_public_path();
render(runMatch(getRoutes()->match(), PhpSessionHandle::init()));

@ -1,13 +1,13 @@
<?php <?php
namespace App\Controller\Api; namespace IQBall\Api;
use App\Controller\Control; use IQBall\Core\Route\Control;
use App\Http\HttpRequest; use IQBall\Core\Http\HttpRequest;
use App\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use App\Http\JsonHttpResponse; use IQBall\Core\Http\JsonHttpResponse;
use App\Model\AuthModel; use IQBall\Core\Model\AuthModel;
use App\Validation\Validators; use IQBall\Core\Validation\Validators;
class APIAuthController { class APIAuthController {
private AuthModel $model; private AuthModel $model;

@ -1,15 +1,15 @@
<?php <?php
namespace App\Controller\Api; namespace IQBall\Api;
use App\Controller\Control; use IQBall\Core\Route\Control;
use App\Data\Account; use IQBall\Core\Data\Account;
use App\Http\HttpCodes; use IQBall\Core\Http\HttpCodes;
use App\Http\HttpRequest; use IQBall\Core\Http\HttpRequest;
use App\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use App\Http\JsonHttpResponse; use IQBall\Core\Http\JsonHttpResponse;
use App\Model\TacticModel; use IQBall\Core\Model\TacticModel;
use App\Validation\Validators; use IQBall\Core\Validation\Validators;
/** /**
* API endpoint related to tactics * API endpoint related to tactics

@ -0,0 +1,46 @@
<?php
namespace IQBall\Api;
use IQBall\Core\Data\Account;
use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Route\AbstractAction;
/**
* @extends AbstractAction<?Account>
*/
class ApiAction extends AbstractAction {
/**
* @param mixed[] $params
* @param ?Account $session
* @return HttpResponse
*/
public function run(array $params, $session): HttpResponse {
$params = array_values($params);
if ($this->isAuthRequired()) {
if ($session == null) {
throw new \Exception("action requires authorization.");
}
$params[] = $session;
}
return call_user_func_array($this->action, $params);
}
/**
* @param callable(mixed[]): HttpResponse $action
* @return ApiAction an action that does not require to have an authorization.
*/
public static function noAuth(callable $action): ApiAction {
return new ApiAction($action, false);
}
/**
* @param callable(mixed[]): HttpResponse $action
* @return ApiAction an action that does require to have an authorization.
*/
public static function auth(callable $action): ApiAction {
return new ApiAction($action, true);
}
}

@ -0,0 +1,55 @@
<?php
namespace IQBall\App;
use IQBall\Core\Http\HttpResponse;
use IQBall\Core\Session\MutableSessionHandle;
use Exception;
use IQBall\Core\Route\AbstractAction;
/**
* A Front controller action
* @extends AbstractAction<MutableSessionHandle>
*/
class AppAction extends AbstractAction {
/**
* @param mixed[] $params
* @param MutableSessionHandle $session
* @return HttpResponse
* @throws Exception <p>
* thrown if this action is required to be authenticated, but the given session does not contain a logged-in account.
* </p>
* <p>
* Caller is supposed to ensure that the user is logged-in before, if `$this->isAuthRequired()` is true before
* running this action.
* </p>
*/
public function run(array $params, $session): HttpResponse {
$params = array_values($params);
if ($this->isAuthRequired()) {
if ($session->getAccount() == null) {
throw new Exception("action requires authorization.");
}
}
$params[] = $session;
return call_user_func_array($this->action, $params);
}
/**
* @param callable(mixed[]): HttpResponse $action
* @return AppAction an action that does not require to have an authorization.
*/
public static function noAuth(callable $action): AppAction {
return new AppAction($action, false);
}
/**
* @param callable(mixed[]): HttpResponse $action
* @return AppAction an action that does require to have an authorization.
*/
public static function auth(callable $action): AppAction {
return new AppAction($action, true);
}
}

@ -1,14 +1,14 @@
<?php <?php
namespace App\Controller; namespace IQBall\App\Controller;
use App\Http\HttpRequest; use IQBall\Core\Http\HttpRequest;
use App\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use App\Http\ViewHttpResponse; use IQBall\Core\Http\ViewHttpResponse;
use App\Model\AuthModel; use IQBall\Core\Model\AuthModel;
use App\Session\MutableSessionHandle; use IQBall\Core\Session\MutableSessionHandle;
use App\Validation\ValidationFail; use IQBall\Core\Validation\ValidationFail;
use App\Validation\Validators; use IQBall\Core\Validation\Validators;
class AuthController { class AuthController {
private AuthModel $model; private AuthModel $model;
@ -29,7 +29,7 @@ class AuthController {
* @param ValidationFail[] $fails * @param ValidationFail[] $fails
* @return HttpResponse * @return HttpResponse
*/ */
private function displayBadFields(string $viewName, array $fails): HttpResponse{ private function displayBadFields(string $viewName, array $fails): HttpResponse {
return ViewHttpResponse::twig($viewName, ['fails' => $fails]); return ViewHttpResponse::twig($viewName, ['fails' => $fails]);
} }
@ -44,7 +44,7 @@ class AuthController {
"username" => [Validators::name(), Validators::lenBetween(2, 32)], "username" => [Validators::name(), Validators::lenBetween(2, 32)],
"password" => [Validators::lenBetween(6, 256)], "password" => [Validators::lenBetween(6, 256)],
"confirmpassword" => [Validators::lenBetween(6, 256)], "confirmpassword" => [Validators::lenBetween(6, 256)],
"email" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/","invalide"),Validators::lenBetween(5, 256)], "email" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/", "invalide"),Validators::lenBetween(5, 256)],
]); ]);
if (!empty($fails)) { if (!empty($fails)) {
return $this->displayBadFields("display_register.html.twig", $fails); return $this->displayBadFields("display_register.html.twig", $fails);
@ -73,7 +73,7 @@ class AuthController {
$fails = []; $fails = [];
$request = HttpRequest::from($request, $fails, [ $request = HttpRequest::from($request, $fails, [
"password" => [Validators::lenBetween(6, 256)], "password" => [Validators::lenBetween(6, 256)],
"email" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/","invalide"),Validators::lenBetween(5, 256)], "email" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/", "invalide"),Validators::lenBetween(5, 256)],
]); ]);
if (!empty($fails)) { if (!empty($fails)) {
return $this->displayBadFields("display_login.html.twig", $fails); return $this->displayBadFields("display_login.html.twig", $fails);

@ -1,14 +1,14 @@
<?php <?php
namespace App\Controller; namespace IQBall\App\Controller;
use App\Data\TacticInfo; use IQBall\Core\Data\TacticInfo;
use App\Http\HttpCodes; use IQBall\Core\Http\HttpCodes;
use App\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use App\Http\ViewHttpResponse; use IQBall\Core\Http\ViewHttpResponse;
use App\Model\TacticModel; use IQBall\Core\Model\TacticModel;
use App\Session\SessionHandle; use IQBall\Core\Session\SessionHandle;
use App\Validator\TacticValidator; use IQBall\Core\Validator\TacticValidator;
class EditorController { class EditorController {
private TacticModel $model; private TacticModel $model;

@ -1,14 +1,14 @@
<?php <?php
namespace App\Controller; namespace IQBall\App\Controller;
use App\Http\HttpRequest; use IQBall\Core\Http\HttpRequest;
use App\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use App\Http\ViewHttpResponse; use IQBall\Core\Http\ViewHttpResponse;
use App\Model\TeamModel; use IQBall\Core\Model\TeamModel;
use App\Session\SessionHandle; use IQBall\Core\Session\SessionHandle;
use App\Validation\FieldValidationFail; use IQBall\Core\Validation\FieldValidationFail;
use App\Validation\Validators; use IQBall\Core\Validation\Validators;
class TeamController { class TeamController {
private TeamModel $model; private TeamModel $model;
@ -33,19 +33,22 @@ class TeamController {
return ViewHttpResponse::twig("delete_member.html.twig", []); return ViewHttpResponse::twig("delete_member.html.twig", []);
} }
/**
* @param array<string, mixed> $request
* @param SessionHandle $session
* @return HttpResponse
*/
public function submitTeam(array $request, SessionHandle $session): HttpResponse { public function submitTeam(array $request, SessionHandle $session): HttpResponse {
$failures = [];
$errors = []; $request = HttpRequest::from($request, $failures, [
$request = HttpRequest::from($request, $errors, [
"name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()], "name" => [Validators::lenBetween(1, 32), Validators::nameWithSpaces()],
"mainColor" => [Validators::regex('/#(?:[0-9a-fA-F]{6})/')], "mainColor" => [Validators::regex('/#(?:[0-9a-fA-F]{6})/')],
"secondColor" => [Validators::regex('/#(?:[0-9a-fA-F]{6})/')], "secondColor" => [Validators::regex('/#(?:[0-9a-fA-F]{6})/')],
"picture" => [Validators::isURL()], "picture" => [Validators::isURL()],
]); ]);
if (!empty($errors)) { if (!empty($failures)) {
$badFields = []; $badFields = [];
foreach ($errors as $e) { foreach ($failures as $e) {
if ($e instanceof FieldValidationFail) { if ($e instanceof FieldValidationFail) {
$badFields[] = $e->getFieldName(); $badFields[] = $e->getFieldName();
} }
@ -88,23 +91,33 @@ class TeamController {
return ViewHttpResponse::twig('display_team.html.twig', ['team' => $result]); return ViewHttpResponse::twig('display_team.html.twig', ['team' => $result]);
} }
/**
* @param array<string, mixed> $request
* @param SessionHandle $session
* @return HttpResponse
*/
public function addMember(array $request, SessionHandle $session): HttpResponse { public function addMember(array $request, SessionHandle $session): HttpResponse {
$errors = []; $errors = [];
$request = HttpRequest::from($request, $errors, [ $request = HttpRequest::from($request, $errors, [
"team" => [Validators::isInteger()], "team" => [Validators::isInteger()],
"mail" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/"), Validators::lenBetween(5, 256)] "mail" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/"), Validators::lenBetween(5, 256)],
]); ]);
return $this->displayTeam($this->model->addMember($request['mail'], intval($request['team']), $request['role']), $session); return $this->displayTeam($this->model->addMember($request['mail'], intval($request['team']), $request['role']), $session);
} }
/**
* @param array<string, mixed> $request
* @param SessionHandle $session
* @return HttpResponse
*/
public function deleteMember(array $request, SessionHandle $session): HttpResponse { public function deleteMember(array $request, SessionHandle $session): HttpResponse {
$errors = []; $errors = [];
$request = HttpRequest::from($request, $errors, [ $request = HttpRequest::from($request, $errors, [
"team" => [Validators::isInteger()], "team" => [Validators::isInteger()],
"mail" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/"), Validators::lenBetween(5, 256)] "mail" => [Validators::regex("/^\\S+@\\S+\\.\\S+$/"), Validators::lenBetween(5, 256)],
]); ]);
return $this->displayTeam($this->model->deleteMember($request['mail'], intval($request['team'])), $session); return $this->displayTeam($this->model->deleteMember($request['mail'], intval($request['team'])), $session);

@ -1,16 +1,13 @@
<?php <?php
namespace App\Controller; namespace IQBall\App\Controller;
use App\Connexion; use IQBall\Core\Http\HttpResponse;
use App\Gateway\TacticInfoGateway; use IQBall\Core\Http\ViewHttpResponse;
use App\Http\HttpResponse; use IQBall\Core\Model\TacticModel;
use App\Http\ViewHttpResponse; use IQBall\Core\Session\SessionHandle;
use App\Model\TacticModel;
use App\Session\SessionHandle;
class UserController { class UserController {
private TacticModel $tactics; private TacticModel $tactics;
/** /**

@ -1,13 +1,13 @@
<?php <?php
namespace App\Controller; namespace IQBall\App\Controller;
use App\Http\HttpCodes; use IQBall\Core\Http\HttpCodes;
use App\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use App\Http\ViewHttpResponse; use IQBall\Core\Http\ViewHttpResponse;
use App\Model\TacticModel; use IQBall\Core\Model\TacticModel;
use App\Session\SessionHandle; use IQBall\Core\Session\SessionHandle;
use App\Validator\TacticValidator; use IQBall\Core\Validator\TacticValidator;
class VisualizerController { class VisualizerController {
private TacticModel $tacticModel; private TacticModel $tacticModel;

@ -54,7 +54,7 @@
<h1>IQ Ball</h1> <h1>IQ Ball</h1>
<div id="account" onclick="location.pathname='/settings'"> <div id="account" onclick="location.pathname='/settings'">
<img <img
src="front/assets/icon/account.svg" src="../../../front/assets/icon/account.svg"
alt="Account logo" alt="Account logo"
/> />
<p>Mon profil<p> <p>Mon profil<p>

@ -1,71 +0,0 @@
<?php
namespace App\Controller\Route;
use App\Http\HttpResponse;
use App\Session\SessionHandle;
use Exception;
/**
* A Front controller action
*/
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 SessionHandle $session
* @return HttpResponse
* @throws Exception <p>
* thrown if this action is required to be authenticated, but the given session does not contain a logged-in account.
* </p>
* <p>
* Caller is supposed to ensure that the user is logged-in before, if `$this->isAuthRequired()` is true before
* running this action.
* </p>
*/
public function run(array $params, SessionHandle $session): HttpResponse {
$params = array_values($params);
if ($this->isAuthRequired) {
if ($session->getAccount() == null) {
throw new Exception("action requires authorization.");
}
}
$params[] = $session;
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);
}
}

@ -1,143 +0,0 @@
<?php
namespace App\Controller\Route;
use AltoRouter;
use App\Http\HttpCodes;
use App\Http\HttpResponse;
use App\Http\JsonHttpResponse;
use App\Http\ViewHttpResponse;
use App\Session\MutableSessionHandle;
use App\Validation\ValidationFail;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\Loader\FilesystemLoader;
class FrontController {
private AltoRouter $router;
private string $basePath;
public function __construct(string $basePath) {
$this->router = $this->createRouter($basePath);
$this->basePath = $basePath;
}
public function addRoute(string $method, string $path, Action $action): void {
$this->router->map($method, $path, $action);
}
/**
* @param MutableSessionHandle $session
* @return void
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
*/
public function run(MutableSessionHandle $session): void {
$match = $this->router->match();
if ($match) {
$this->handleMatch($match, $session);
return;
}
$this->displayViewByKind(ViewHttpResponse::twig("error.html.twig", [
'failures' => [ValidationFail::notFound("Could not find page ${_SERVER['REQUEST_URI']}.")],
], 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;
}
/**
* @param array<string, mixed> $match
* @param MutableSessionHandle $session
* @return void
*/
private function handleMatch(array $match, MutableSessionHandle $session): void {
$action = $match['target'];
$params = array_values($match["params"]);
$this->handleResponseByType($this->tryToCall($action, $params, $session));
}
/**
* @param Action $action
* @param array<int, mixed> $params
* @param MutableSessionHandle $session
* @return HttpResponse
*/
private function tryToCall(Action $action, array $params, MutableSessionHandle $session): HttpResponse {
$account = null;
if ($action->isAuthRequired()) {
$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 . "/login");
}
}
return $action->run($params, $session);
}
/**
* Redirect the return by the response's type
*
* @param HttpResponse $response
* @return void
*/
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) {
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;
}
}
}

@ -1,10 +1,10 @@
<?php <?php
namespace App; namespace IQBall\Core;
use PDO; use PDO;
class Connexion { class Connection {
private PDO $pdo; private PDO $pdo;
/** /**

@ -1,6 +1,6 @@
<?php <?php
namespace App\Data; namespace IQBall\Core\Data;
/** /**
* Base class of a user account. * Base class of a user account.

@ -1,6 +1,6 @@
<?php <?php
namespace App\Data; namespace IQBall\Core\Data;
use http\Url; use http\Url;

@ -1,6 +1,6 @@
<?php <?php
namespace App\Data; namespace IQBall\Core\Data;
use InvalidArgumentException; use InvalidArgumentException;

@ -1,13 +1,13 @@
<?php <?php
namespace App\Data; namespace IQBall\Core\Data;
/** /**
* information about a team member * information about a team member
*/ */
class Member { class Member {
/** /**
* @var AccountUser The member's user account * @var int The member's user account
*/ */
private int $userId; private int $userId;
@ -49,8 +49,7 @@ class Member {
/** /**
* @return int * @return int
*/ */
public function getTeamId(): int public function getTeamId(): int {
{
return $this->teamId; return $this->teamId;
} }
} }

@ -1,8 +1,8 @@
<?php <?php
namespace App\Data; namespace IQBall\Core\Data;
use http\Exception\InvalidArgumentException; use InvalidArgumentException;
/** /**
* Enumeration class workaround * Enumeration class workaround

@ -1,6 +1,6 @@
<?php <?php
namespace App\Data; namespace IQBall\Core\Data;
class TacticInfo implements \JsonSerializable { class TacticInfo implements \JsonSerializable {
private int $id; private int $id;

@ -1,6 +1,6 @@
<?php <?php
namespace App\Data; namespace IQBall\Core\Data;
class Team { class Team {
private int $id; private int $id;
@ -72,7 +72,7 @@ class Team {
return $this->members; return $this->members;
} }
public function addMember(Member $m) { public function addMember(Member $m): void {
$this->members[] = $m; $this->members[] = $m;
} }

@ -1,6 +1,6 @@
<?php <?php
namespace App\Data; namespace IQBall\Core\Data;
use http\Url; use http\Url;

@ -1,18 +1,18 @@
<?php <?php
namespace App\Gateway; namespace IQBall\Core\Gateway;
use App\Connexion; use IQBall\Core\Connection;
use App\Data\Account; use IQBall\Core\Data\Account;
use PDO; use PDO;
class AccountGateway { class AccountGateway {
private Connexion $con; private Connection $con;
/** /**
* @param Connexion $con * @param Connection $con
*/ */
public function __construct(Connexion $con) { public function __construct(Connection $con) {
$this->con = $con; $this->con = $con;
} }

@ -1,17 +1,17 @@
<?php <?php
namespace App\Gateway; namespace IQBall\Core\Gateway;
use App\Connexion; use IQBall\Core\Connection;
use PDO; use PDO;
class AuthGateway { class AuthGateway {
private Connexion $con; private Connection $con;
/** /**
* @param Connexion $con * @param Connection $con
*/ */
public function __construct(Connexion $con) { public function __construct(Connection $con) {
$this->con = $con; $this->con = $con;
} }

@ -1,18 +1,18 @@
<?php <?php
namespace App\Gateway; namespace IQBall\Core\Gateway;
use App\Connexion; use IQBall\Core\Connection;
use App\Data\TacticInfo; use IQBall\Core\Data\TacticInfo;
use PDO; use PDO;
class TacticInfoGateway { class TacticInfoGateway {
private Connexion $con; private Connection $con;
/** /**
* @param Connexion $con * @param Connection $con
*/ */
public function __construct(Connexion $con) { public function __construct(Connection $con) {
$this->con = $con; $this->con = $con;
} }
@ -38,7 +38,7 @@ class TacticInfoGateway {
* @param integer $nb * @param integer $nb
* @return array<array<string,mixed>> * @return array<array<string,mixed>>
*/ */
public function getLast(int $nb) : ?array { public function getLast(int $nb): ?array {
$res = $this->con->fetch( $res = $this->con->fetch(
"SELECT * FROM Tactic ORDER BY creation_date DESC LIMIT :nb ", "SELECT * FROM Tactic ORDER BY creation_date DESC LIMIT :nb ",
[":nb" => [$nb, PDO::PARAM_INT]] [":nb" => [$nb, PDO::PARAM_INT]]

@ -1,14 +1,14 @@
<?php <?php
namespace App\Gateway; namespace IQBall\Core\Gateway;
use App\Connexion; use IQBall\Core\Connection;
use PDO; use PDO;
class TeamGateway { class TeamGateway {
private Connexion $con; private Connection $con;
public function __construct(Connexion $con) { public function __construct(Connection $con) {
$this->con = $con; $this->con = $con;
} }
@ -25,18 +25,22 @@ class TeamGateway {
} }
public function insertMember(int $idTeam, int $idMember, string $role) { public function insertMember(int $idTeam, int $idMember, string $role): void {
$this->con->exec( $this->con->exec(
"INSERT INTO Member(idTeam, idMember, role) VALUES (:idTeam , :idMember, :role)", "INSERT INTO Member(idTeam, idMember, role) VALUES (:idTeam , :idMember, :role)",
[ [
":idTeam" => [$idTeam, PDO::PARAM_INT], ":idTeam" => [$idTeam, PDO::PARAM_INT],
":idMember" => [$idMember, PDO::PARAM_INT], ":idMember" => [$idMember, PDO::PARAM_INT],
":role" => [$role, PDO::PARAM_STR] ":role" => [$role, PDO::PARAM_STR],
] ]
); );
} }
/**
* @param string $name
* @return array<string, mixed>[]
*/
public function listByName(string $name): array { public function listByName(string $name): array {
return $this->con->fetch( return $this->con->fetch(
"SELECT id,name,picture,mainColor,secondColor FROM Team WHERE name LIKE '%' || :name || '%'", "SELECT id,name,picture,mainColor,secondColor FROM Team WHERE name LIKE '%' || :name || '%'",
@ -48,28 +52,28 @@ class TeamGateway {
/** /**
* @param int $id * @param int $id
* @return array<string,mixed>[] * @return array<string,mixed>
*/ */
public function getTeamById(int $id): array { public function getTeamById(int $id): ?array {
return $this->con->fetch( return $this->con->fetch(
"SELECT id,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], ":id" => [$id, PDO::PARAM_INT],
] ]
); )[0] ?? null;
} }
/** /**
* @param string $name * @param string $name
* @return array<string,int>[] * @return int|null
*/ */
public function getIdTeamByName(string $name): array { public function getIdTeamByName(string $name): ?int {
return $this->con->fetch( return $this->con->fetch(
"SELECT id FROM Team WHERE name = :name", "SELECT id FROM Team WHERE name = :name",
[ [
":name" => [$name, PDO::PARAM_STR], ":name" => [$name, PDO::PARAM_INT],
] ]
); )[0]['id'] ?? null;
} }
/** /**
@ -78,23 +82,27 @@ class TeamGateway {
*/ */
public function getMembersById(int $id): array { public function getMembersById(int $id): array {
return $this->con->fetch( return $this->con->fetch(
"SELECT a.id,m.role,a.email,a.username FROM Account a,Team t,Member m WHERE t.id = :id AND m.idTeam = t.id AND m.idMember = a.id", "SELECT a.id,m.role,a.email,a.username FROM Account a,Team t,Member m WHERE t.id = :id AND m.idTeam = t.id AND m.idMember = a.id",
[ [
":id" => [$id, PDO::PARAM_INT] ":id" => [$id, PDO::PARAM_INT],
] ]
); );
} }
public function getMemberIdByMail($mail) : array { /**
* @param string $mail
* @return int|null
*/
public function getMemberIdByMail(string $mail): ?int {
return $this->con->fetch( return $this->con->fetch(
"SELECT id FROM Account WHERE email = :mail", "SELECT id FROM Account WHERE email = :mail",
[ [
":mail" => [$mail, PDO::PARAM_STR] ":mail" => [$mail, PDO::PARAM_STR],
] ]
); )[0]['id'] ?? null;
} }
public function deleteMember(int $idTeam, int $idMember) { public function deleteMember(int $idTeam, int $idMember): void {
$this->con->exec( $this->con->exec(
"DELETE FROM Member WHERE idTeam = :idTeam AND idMember = :idMember", "DELETE FROM Member WHERE idTeam = :idTeam AND idMember = :idMember",
[ [

@ -1,6 +1,6 @@
<?php <?php
namespace App\Http; namespace IQBall\Core\Http;
/** /**
* Utility class to define constants of used http codes * Utility class to define constants of used http codes

@ -1,11 +1,11 @@
<?php <?php
namespace App\Http; namespace IQBall\Core\Http;
use App\Validation\FieldValidationFail; use IQBall\Core\Validation\FieldValidationFail;
use App\Validation\Validation; use IQBall\Core\Validation\Validation;
use App\Validation\ValidationFail; use IQBall\Core\Validation\ValidationFail;
use App\Validation\Validator; use IQBall\Core\Validation\Validator;
use ArrayAccess; use ArrayAccess;
use Exception; use Exception;

@ -1,6 +1,6 @@
<?php <?php
namespace App\Http; namespace IQBall\Core\Http;
class HttpResponse { class HttpResponse {
/** /**

@ -1,6 +1,6 @@
<?php <?php
namespace App\Http; namespace IQBall\Core\Http;
class JsonHttpResponse extends HttpResponse { class JsonHttpResponse extends HttpResponse {
/** /**

@ -1,6 +1,6 @@
<?php <?php
namespace App\Http; namespace IQBall\Core\Http;
class ViewHttpResponse extends HttpResponse { class ViewHttpResponse extends HttpResponse {
public const TWIG_VIEW = 0; public const TWIG_VIEW = 0;

@ -1,11 +1,11 @@
<?php <?php
namespace App\Model; namespace IQBall\Core\Model;
use App\Data\Account; use IQBall\Core\Data\Account;
use App\Gateway\AccountGateway; use IQBall\Core\Gateway\AccountGateway;
use App\Validation\FieldValidationFail; use IQBall\Core\Validation\FieldValidationFail;
use App\Validation\ValidationFail; use IQBall\Core\Validation\ValidationFail;
class AuthModel { class AuthModel {
private AccountGateway $gateway; private AccountGateway $gateway;

@ -1,11 +1,10 @@
<?php <?php
namespace App\Model; namespace IQBall\Core\Model;
use App\Data\Account; use IQBall\Core\Gateway\TacticInfoGateway;
use App\Data\TacticInfo; use IQBall\Core\Validation\ValidationFail;
use App\Gateway\TacticInfoGateway; use IQBall\Core\Data\TacticInfo;
use App\Validation\ValidationFail;
class TacticModel { class TacticModel {
public const TACTIC_DEFAULT_NAME = "Nouvelle tactique"; public const TACTIC_DEFAULT_NAME = "Nouvelle tactique";
@ -43,7 +42,7 @@ class TacticModel {
* @param integer $nb * @param integer $nb
* @return array<array<string,mixed>> * @return array<array<string,mixed>>
*/ */
public function getLast(int $nb) : ?array { public function getLast(int $nb): ?array {
return $this->tactics->getLast($nb); return $this->tactics->getLast($nb);
} }

@ -1,12 +1,12 @@
<?php <?php
namespace App\Model; namespace IQBall\Core\Model;
use App\Gateway\TeamGateway; use IQBall\Core\Gateway\TeamGateway;
use App\Data\Team; use IQBall\Core\Data\Team;
use App\Data\Member; use IQBall\Core\Data\Member;
use App\Data\MemberRole; use IQBall\Core\Data\MemberRole;
use App\Data\Color; use IQBall\Core\Data\Color;
class TeamModel { class TeamModel {
private TeamGateway $gateway; private TeamGateway $gateway;
@ -20,18 +20,19 @@ class TeamModel {
public function createTeam(string $name, string $picture, string $mainColor, string $secondColor): int { public function createTeam(string $name, string $picture, string $mainColor, string $secondColor): int {
$this->gateway->insert($name, $picture, $mainColor, $secondColor); $this->gateway->insert($name, $picture, $mainColor, $secondColor);
$result = $this->gateway->getIdTeamByName($name); return $this->gateway->getIdTeamByName($name);
return intval($result[0]['id']);
} }
public function addMember(string $mail, int $teamId, string $role): int {
public function addMember(string $mail, int $teamId, string $role) : int { $id = $this->gateway->getMemberIdByMail($mail);
$result = $this->gateway->getMemberIdByMail($mail)[0]; $this->gateway->insertMember($teamId, $id, $role);
$memberId = intval($result['id']);
$this->gateway->insertMember($teamId, $memberId, $role);
return $teamId; return $teamId;
} }
/**
* @param string $name
* @return Team[]
*/
public function listByName(string $name): array { public function listByName(string $name): array {
$teams = []; $teams = [];
$results = $this->gateway->listByName($name); $results = $this->gateway->listByName($name);
@ -43,7 +44,7 @@ class TeamModel {
public function displayTeam(int $id): Team { public function displayTeam(int $id): Team {
$members = []; $members = [];
$result = $this->gateway->getTeamById($id)[0]; $result = $this->gateway->getTeamById($id);
$resultMembers = $this->gateway->getMembersById($id); $resultMembers = $this->gateway->getMembersById($id);
foreach ($resultMembers as $row) { foreach ($resultMembers as $row) {
var_dump($row['role']); var_dump($row['role']);
@ -57,9 +58,8 @@ class TeamModel {
return new Team(intval($result['id']), $result['name'], $result['picture'], Color::from($result['mainColor']), Color::from($result['secondColor']), $members); return new Team(intval($result['id']), $result['name'], $result['picture'], Color::from($result['mainColor']), Color::from($result['secondColor']), $members);
} }
public function deleteMember(string $mail, int $teamId) : int { public function deleteMember(string $mail, int $teamId): int {
$result = $this->gateway->getMemberIdByMail($mail)[0]; $memberId = $this->gateway->getMemberIdByMail($mail);
$memberId = intval($result['id']);
$this->gateway->deleteMember($teamId, $memberId); $this->gateway->deleteMember($teamId, $memberId);
return $teamId; return $teamId;
} }

@ -0,0 +1,36 @@
<?php
namespace IQBall\Core\Route;
use IQBall\Core\Http\HttpResponse;
/**
* @template S session
*/
abstract class AbstractAction {
/**
* @var callable(mixed[]): HttpResponse $action action to call
*/
protected $action;
private bool $isAuthRequired;
/**
* @param callable(mixed[]): HttpResponse $action
*/
protected function __construct(callable $action, bool $isAuthRequired) {
$this->action = $action;
$this->isAuthRequired = $isAuthRequired;
}
public function isAuthRequired(): bool {
return $this->isAuthRequired;
}
/**
* @param mixed[] $params
* @param S $session
* @return HttpResponse
*/
public abstract function run(array $params, $session): HttpResponse;
}

@ -1,14 +1,14 @@
<?php <?php
namespace App\Controller; namespace IQBall\Core\Route;
use App\Http\HttpCodes; use IQBall\Core\Http\HttpCodes;
use App\Http\HttpRequest; use IQBall\Core\Http\HttpRequest;
use App\Http\HttpResponse; use IQBall\Core\Http\HttpResponse;
use App\Http\JsonHttpResponse; use IQBall\Core\Http\JsonHttpResponse;
use App\Http\ViewHttpResponse; use IQBall\Core\Http\ViewHttpResponse;
use App\Validation\ValidationFail; use IQBall\Core\Validation\ValidationFail;
use App\Validation\Validator; use IQBall\Core\Validation\Validator;
class Control { class Control {
/** /**
@ -57,6 +57,4 @@ class Control {
return call_user_func_array($run, [$request]); return call_user_func_array($run, [$request]);
} }
} }

@ -1,8 +1,8 @@
<?php <?php
namespace App\Session; namespace IQBall\Core\Session;
use App\Data\Account; use IQBall\Core\Data\Account;
/** /**
* The mutable side of a session handle * The mutable side of a session handle

@ -1,8 +1,8 @@
<?php <?php
namespace App\Session; namespace IQBall\Core\Session;
use App\Data\Account; use IQBall\Core\Data\Account;
/** /**
* A PHP session handle * A PHP session handle

@ -1,8 +1,8 @@
<?php <?php
namespace App\Session; namespace IQBall\Core\Session;
use App\Data\Account; use IQBall\Core\Data\Account;
/** /**
* An immutable session handle * An immutable session handle

@ -1,6 +1,6 @@
<?php <?php
namespace App\Validation; namespace IQBall\Core\Validation;
class ComposedValidator extends Validator { class ComposedValidator extends Validator {
private Validator $first; private Validator $first;

@ -1,6 +1,6 @@
<?php <?php
namespace App\Validation; namespace IQBall\Core\Validation;
/** /**
* An error that concerns a field, with a bound message name * An error that concerns a field, with a bound message name

@ -1,6 +1,6 @@
<?php <?php
namespace App\Validation; namespace IQBall\Core\Validation;
class FunctionValidator extends Validator { class FunctionValidator extends Validator {
/** /**

@ -1,6 +1,6 @@
<?php <?php
namespace App\Validation; namespace IQBall\Core\Validation;
/** /**
* A simple validator that takes a predicate and an error factory * A simple validator that takes a predicate and an error factory

@ -1,6 +1,6 @@
<?php <?php
namespace App\Validation; namespace IQBall\Core\Validation;
/** /**
* Utility class for validation * Utility class for validation

@ -1,6 +1,6 @@
<?php <?php
namespace App\Validation; namespace IQBall\Core\Validation;
use JsonSerializable; use JsonSerializable;

@ -1,6 +1,6 @@
<?php <?php
namespace App\Validation; namespace IQBall\Core\Validation;
abstract class Validator { abstract class Validator {
/** /**

@ -1,6 +1,6 @@
<?php <?php
namespace App\Validation; namespace IQBall\Core\Validation;
/** /**
* A collection of standard validators * A collection of standard validators

@ -1,9 +1,9 @@
<?php <?php
namespace App\Validator; namespace IQBall\Core\Validator;
use App\Data\TacticInfo; use IQBall\Core\Data\TacticInfo;
use App\Validation\ValidationFail; use IQBall\Core\Validation\ValidationFail;
class TacticValidator { class TacticValidator {
public static function validateAccess(?TacticInfo $tactic, int $tacticId, int $ownerId): ?ValidationFail { public static function validateAccess(?TacticInfo $tactic, int $tacticId, int $ownerId): ?ValidationFail {
Loading…
Cancel
Save