diff --git a/Sources/composer.json b/Sources/composer.json index a0ad4bd1..501dee41 100644 --- a/Sources/composer.json +++ b/Sources/composer.json @@ -4,11 +4,16 @@ "psr-4": { "Hearttrack\\": "src/", "App\\": "src/app", + "Enums\\": "src/app/enums", "Data\\": "src/data", "Model\\": "src/data/model", "Repository\\": "src/data/model/repository", "Manager\\": "src/data/model/manager", "Network\\": "src/data/core/network", + "Database\\": "src/data/core/database", + "Database\\Gateway\\": "src/data/core/database/gateway", + "Database\\Mapper\\": "src/data/core/database/mapper", + "Database\\Entity\\": "src/data/core/database/entity", "Console\\": "src/console", "Stub\\": [ "src/data/stub", diff --git a/Sources/public/index.php b/Sources/public/index.php index 292f954c..ef8e6e48 100644 --- a/Sources/public/index.php +++ b/Sources/public/index.php @@ -1,30 +1,22 @@ (); - $app = $appFactory->registerService('',''); - - $appFactory.errorProvider(class:: or port) +use App\AppCreator; +use App\Router\Middleware\LoggingMiddleware; +use App\Router\Request\RequestFactory; +use Controllers\ArgumentControllerResolver; +use Controllers\IArgumentResolver; +$appFactory = new AppCreator(); +$appFactory->registerService(IArgumentResolver::class,ArgumentControllerResolver::class); +// $appFactory->registerService('twig',); - // connexion string +// // Connexion à la base de données +// $databaseContext = DatabaseContext::getInstance(); +$appFactory->AddControllers(); +$app = $appFactory->create(); +$app->use(new LoggingMiddleware()); +// $app->addHttpClient(HttpClient::class); +// je veux pas faire sa pour load les controller avec les anotation +$app->mapControllers(); -// var connectionString = builder.Configuration.GetConnectionString("LolDatabase"); -// builder.Services.AddDbContext(options => -// options.UseSqlite(connectionString), ServiceLifetime.Singleton); - - $app->use(new LoggingMiddleware()); - $app = $appFactory->create(); - $app.addHttpClient(HttpClient::class) - - // je veux pas faire sa pour load les controller avec les anotation - $app->RegisterControllers(); - $app-> - $app->run(); -} +$app->run(RequestFactory::createFromGlobals()); diff --git a/Sources/src/app/App.php b/Sources/src/app/App.php index 767e9fa2..2bd9d8b9 100644 --- a/Sources/src/app/App.php +++ b/Sources/src/app/App.php @@ -1,4 +1,13 @@ appName = $appName; @@ -35,7 +47,8 @@ class App { return $this->version; } - public function run() { + public function run(HttpRequest $request) { + Session::getInstance(); echo "Running {$this->appName} version {$this->version}\n"; } @@ -80,6 +93,39 @@ class App { // return $middlewareClasses; // } + + + + public function mapControllers(): void + { + $classes = $this->container->getAllRegisteredClassNames(); + + foreach ($classes as $class) { + if ($this->isController($class)) { + $this->mapControllerRoutes($class); + } + } + } + private function mapControllerRoutes(string $controllerClass): void + { + $reflectionClass = new \ReflectionClass($controllerClass); + $attributes = $reflectionClass->getAttributes(Route::class); + $prefix = ''; + if(!empty($attributes)){ + $prefix = $attributes[0]->newInstance()->getPath(); + } + foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { + foreach ($method->getAttributes(Route::class) as $attribute) { + $route = $attribute->newInstance(); + $this->router->add($route->method,$prefix . $route->path, [$controllerClass, $method->getName()]); + } + } + } + private function isController(string $class): bool + { + $reflectionClass = new \ReflectionClass($class); + return $reflectionClass->isSubclassOf(BaseController::class); + } } ?> \ No newline at end of file diff --git a/Sources/src/app/AppCreator.php b/Sources/src/app/AppCreator.php index 7d26082c..8e8acaf0 100644 --- a/Sources/src/app/AppCreator.php +++ b/Sources/src/app/AppCreator.php @@ -1,12 +1,63 @@ container = new Container; } + public function registerService(string $serviceId, callable|string $service): self + { + $this->container->set($serviceId, $service); + $this->services[] = $serviceId; + return $this; + } + + public function create() + { + + if (APP_ENV === 'console') { + require_once __DIR__ . '/../src/console/Console.php'; + return; + } elseif (APP_ENV === 'development') { + $app = new App("HeartTrack", 1, $this->container); + return $app; + } + + + } - + function AddControllers($namespacePrefix = 'Controllers', $pathToControllers = __DIR__ . 'controller'): self + { + + $controllerFiles = glob($pathToControllers . '/*.php'); + + foreach ($controllerFiles as $file) { + // Get class name from file name + $class = basename($file, '.php'); + $fullClassName = $namespacePrefix . '\\' . $class; + + if (!class_exists($fullClassName)) { + continue; + } + + // Use reflection to check if class extends BaseController + $reflectionClass = new \ReflectionClass($fullClassName); + if ($reflectionClass->isSubclassOf(BaseController::class)) { + // Register in DI container + $this->container->set($fullClassName, function () use ($fullClassName) { + return new $fullClassName(); + }); + } + } + return $this; + } } diff --git a/Sources/src/app/Container.php b/Sources/src/app/Container.php new file mode 100644 index 00000000..969a7138 --- /dev/null +++ b/Sources/src/app/Container.php @@ -0,0 +1,100 @@ +has($id)) { + $entry = $this->entries[$id]; + + if (is_callable($entry)) { + return $entry($this); + } + + $id = $entry; + } + + return $this->resolve($id); + } + + public function has(string $id): bool + { + return isset($this->entries[$id]); + } + + public function set(string $id, callable|string $concrete): void + { + $this->entries[$id] = $concrete; + } + + public function resolve(string $id) + { + // 1. Inspect the class that we are trying to get from the container + try { + $reflectionClass = new \ReflectionClass($id); + } catch(\ReflectionException $e) { + throw new NotFoundException($e->getMessage(), $e->getCode(), $e); + } + + if (! $reflectionClass->isInstantiable()) { + throw new ContainerException('Class "' . $id . '" is not instantiable'); + } + + // 2. Inspect the constructor of the class + $constructor = $reflectionClass->getConstructor(); + + if (! $constructor) { + return new $id; + } + + // 3. Inspect the constructor parameters (dependencies) + $parameters = $constructor->getParameters(); + + if (! $parameters) { + return new $id; + } + + // 4. If the constructor parameter is a class then try to resolve that class using the container + $dependencies = array_map( + function (\ReflectionParameter $param) use ($id) { + $name = $param->getName(); + $type = $param->getType(); + + if (! $type) { + throw new ContainerException( + 'Failed to resolve class "' . $id . '" because param "' . $name . '" is missing a type hint' + ); + } + + if ($type instanceof \ReflectionUnionType) { + throw new ContainerException( + 'Failed to resolve class "' . $id . '" because of union type for param "' . $name . '"' + ); + } + + if ($type instanceof \ReflectionNamedType && ! $type->isBuiltin()) { + return $this->get($type->getName()); + } + + throw new ContainerException( + 'Failed to resolve class "' . $id . '" because invalid param "' . $name . '"' + ); + }, + $parameters + ); + + return $reflectionClass->newInstanceArgs($dependencies); + } + + public function getAllRegisteredClassNames(): array + { + return array_keys($this->entries); + } +} \ No newline at end of file diff --git a/Sources/src/app/HttpClient.php b/Sources/src/app/HttpClient.php index 50307c0a..9a11ce63 100644 --- a/Sources/src/app/HttpClient.php +++ b/Sources/src/app/HttpClient.php @@ -34,7 +34,7 @@ class MyHttpClient { $context = stream_context_create($options); $response = file_get_contents($url, false, $context); - // You can add error handling here if needed + // error handling return $response; } diff --git a/Sources/src/app/controller/ArgumentControllerResolver.php b/Sources/src/app/controller/ArgumentControllerResolver.php new file mode 100644 index 00000000..e9f7ed45 --- /dev/null +++ b/Sources/src/app/controller/ArgumentControllerResolver.php @@ -0,0 +1,78 @@ +getMessage()); + } + + $args = []; + foreach ($reflectionMethod->getParameters() as $param) { + if (IRequest::class === $param->getType()->getName() || is_subclass_of($param->getType()->getName(), IRequest::class)) { + $args[] = $request; + continue; + } + + $name = $param->getName(); + $value = $this->getFromRequest($name, $request); + + if ($value === null && $param->isDefaultValueAvailable()) { + $value = $param->getDefaultValue(); + } elseif ($value === null) { + throw new \InvalidArgumentException("Missing argument: $name"); + } + + $args[] = $value; + } + + return $args; + } + + /** + * Extracts a value from the request based on a key. + * + * @param string $key The key to look for in the request. + * @param IRequest $req The request object. + * @return mixed The value from the request or null if not found. + */ + public function getFromRequest(string $key, IRequest $req): mixed + { + $body = $req->getBody(); + if (array_key_exists($key, $body)) { + return $body[$key]; + } + + $queryParams = $req->getQueryParameters(); + if (array_key_exists($key, $queryParams)) { + return $queryParams[$key]; + } + + $requestParams = $req->getRequestParameters(); + if (array_key_exists($key, $requestParams)) { + return $requestParams[$key]; + } + + return null; + } +} + diff --git a/Sources/src/app/controller/AthleteController.php b/Sources/src/app/controller/AthleteController.php index b6d4b92c..c14e27ee 100644 --- a/Sources/src/app/controller/AthleteController.php +++ b/Sources/src/app/controller/AthleteController.php @@ -3,10 +3,12 @@ namespace App\Controllers; class AthleteController extends BaseController { + protected $authService; public function __construct() { // Initialize UserController specific configurations or dependencies } + public function index() { diff --git a/Sources/src/app/controller/Controller.php b/Sources/src/app/controller/Controller.php new file mode 100644 index 00000000..9c131f92 --- /dev/null +++ b/Sources/src/app/controller/Controller.php @@ -0,0 +1,86 @@ +DataManager = $container->get('DBDATAMANAGER'); + + } + + #[Route(path: '/', name: 'test', methods:['GET'])] // 8 + public function index(): Response{ + return new Response( $this->Reinit()); + + } + #[Route(path: '/addAthl', name: 'test', methods:['Post'])] // 8 + public function addAthlthe(IRequest $req,string $userName){ + if(Validation::val_string($userName)){ + $resp = $this->$DataManager->coachManager->addAthlthe($userName); + if($resp){ + return new Response($container->get('twig')->render('listAthlet.html.twig',$this->$DataManager->coachManager->getCurrentUser()->listAthlete)); + } + + } + } + + public function Reinit() + { + global $twig; + + $dVue = [ + 'nom' => '', + 'age' => 0, + 'email'=> '', + ]; + return $twig->render('vuephp1.html.twig', [ + 'dVue' => $dVue + ]); + } + #[Route(path: '/hello', methods:['GET'], name: 'hello')] + public function hello(): Response{ + return new Response('Hello'); + } + + #[Route(path: '/hi', methods:['GET'],name: 'hi')] + public function hi(string $name,IRequest $req): Response{ + return new Response($name.$req->getMethod()); + } + + public function ValidationFormulaire(array $dVueEreur) + { + global $twig; // nécessaire pour utiliser variables globales + + //si exception, ca remonte !!! + $nom = $_POST['txtNom']; // txtNom = nom du champ texte dans le formulaire + $age = $_POST['txtAge']; + $email = $_POST['email']; + + \config\Validation::val_form($nom, $age,$email, $dVueEreur); + + $model = new \modeles\Simplemodel(); + $data = $model->get_data(); + + $dVue = [ + 'nom' => $nom, + 'email' => $email, + 'age' => $age, + 'data' => $data, + ]; + + echo $twig->render('vuephp1.html.twig', ['dVue' => $dVue, 'dVueEreur' => $dVueEreur]); + } +}//fin class + + + diff --git a/Sources/src/app/controller/FrontController.php b/Sources/src/app/controller/FrontController.php index 4b1d9925..f20093ca 100644 --- a/Sources/src/app/controller/FrontController.php +++ b/Sources/src/app/controller/FrontController.php @@ -1,44 +1,149 @@ router = $router; + $this->container = $container; + } public function dispatch(IRequest $request) { - $match = $this->router->match($request); - - if ($match) { - $controllerName = $match['controller']; - $actionName = $match['action']; - - // Utilisez l'injection de dépendances pour créer le contrôleur - $controller = $this->createController($controllerName); - - if ($controller) { - // Appeler l'action correspondante - $controller->$actionName(); + try { + $match = $this->router->match($request); + + if (!is_null($match)) { + $controller = $this->getController($match['target']); + $request->addToBody($match['params']); + + + if (!is_callable($controller)){ + // body to key values maybe is better + // $request = array_reduce($params, function ($request, $key) use ($params) { + // return $request->withAttribute($key, $params[$key]); + // }, $request); + // if (is_callable($action)) { + // return $action(); + // } + + // if (is_array($action)) { + // [$className, $method] = $action; + + // if (class_exists($className) && method_exists($className, $method)) { + // $class = new $className(); + // return call_user_func_array([$class, $method], []); + // } + // } + throw new NotImplementedException('Handle when route targer is not a callable not handle'); + } + $argumentResolver = $this->container->get(IArgumentResolver::class); + $arguments = $argumentResolver->getArguments($request, $controller); + $response = call_user_func_array($controller, $arguments); + // should handle responce proprely like if its a HTML, STING, JSON,.... + return $response; } else { - // Gérer l'erreur, le contrôleur n'existe pas - $this->handleError(); + return $this->handleError(404, "Page not found"); } - } else { - // Gérer l'erreur, aucune route correspondante - $this->handleError(); + } catch (NotFoundHttpException $e) { + return $this->handleError(404, $e->getMessage()); + } catch (\Exception $e) { + return $this->handleError(500, "Internal Server Error: " . $e->getMessage()); } } - private function createController($controllerName) { + // public function dispatch(IRequest $request) { + // // si sa match sa me retourn une route + // $match = $this->router->match($request); + // // si j'ai bien récupe une route + // if (!is_null($match)) { + // // $controllerName = $match['controller']; + // // $callable = $match['target']; + // // $request->addAttributes($match['params']); + + + + // // Utilisez l'injection de dépendances pour avoir le contrôleur - en gros par rapport à la chaine de carractère la on demmande est ce que on peut avoir une instantce d'un objet(la callable) + // // je résous les param qui sont dans la request pa rapport à l'appel de fonction(le callable) + // $params = $match->getParams(); + // $request = array_reduce($params, function ($request, $key) use ($params) { + // return $request->withAttribute($key, $params[$key]); + // }, $request); + + // // on va vérifié si dans la request j'ai les finfo nécéssaire pour le callable(controller) + // $arguments = $this->argumentResolver->getArguments($request, $controller); + + // $this->dispatcher->dispatch(new ArgumentEvent($request, $controller, $arguments), 'kernel.arguments'); + + // $response = call_user_func_array($controller, $arguments); + // // if (is_callable($route->getCallable())){ + // // call_user_func_array($controller, $request); + // // } + // // can i a dd a logic to have defined route like new Route('', function()) => so without class + // // so il i can check + // // public function resolve(string $requestUri, string $requestMethod): mixed + // // { + // // $path = explode('?', $requestUri)[0]; + // // $action = $this->routes[$requestMethod][$path] ?? null; + + // // if (is_callable($action)) { + // // return $action(); + // // } + + // // if (is_array($action)) { + // // [$className, $method] = $action; + + // // if (class_exists($className) && method_exists($className, $method)) { + // // $class = new $className(); + // // return call_user_func_array([$class, $method], []); + // // } + // // } + + // // throw new RouteNotFoundException(); + // // } + + // if ($controller && is_callable(array($controller, $route->getParams()))) { + // // Appeler l'action correspondante + // // $controller->$actionName(); + // // should call DI container or Arguments Resolver ($arguments = $this->argumentResolver->getArguments($request, $controller);) + // // $response = call_user_func_array($controller, $arguments); + // // to get correct argument + // // ORR like graphicsArt set it un Request + // // So je mets dans la request en tant que attribu ce que à besoin ma function et du coup dans le controller je fait un $request->getAttribute('name') + // // a peu près comme je fais en js const { email, password } = req.body; + // // ou j'utilise un Argument resolver qui va le le faire + // call_user_func_array($controller, $request); + // } else { + // // Gérer l'erreur, le contrôleur n'existe pas + // $this->handleError(); + // } + // } else { + // // Gérer l'erreur, aucune route correspondante + // $this->handleError(); + // } + // } + + private function getController($controllerName) { // Utilisez un conteneur d'injection de dépendances pour créer le contrôleur - return DependencyContainer::create($controllerName); + return $this->container->get($controllerName); } - private function handleError() {// composant twig - header("HTTP/1.0 404 Not Found"); - echo "Page not found"; + + private function handleError($statusCode, $message) {// composant twig + // http_response_code($statusCode); + $response = new Response($statusCode, $message); + echo $message; + // status,..., body + // return new Responce(404,[notfond],erreur.twig) } + } ?> diff --git a/Sources/src/app/controller/IArgumentResolver.php b/Sources/src/app/controller/IArgumentResolver.php new file mode 100644 index 00000000..81b60255 --- /dev/null +++ b/Sources/src/app/controller/IArgumentResolver.php @@ -0,0 +1,26 @@ +map( 'GET', '/users/[i:id]/', 'UserController#showDetails' ); + +// // map contact form handler using function name string +// $router->map( 'POST', '/contact/', 'handleContactForm' ); class Route { + private string $name; - private string $name; + private string $path; - private array $parrams; + private string $method; // callable|array + + private $callable; - private $callable; + public function __construct(string $path, callable $callable, array $params = null,string $name = null) + { + $this->path = $path; + $this->callable = $callable; + $this->name = $name; + } - public function __construct(array $params, callable $callable, string $name = null) - { - $this->path = $params; - $this->callable = $callable; - $this->name = $name; - } + public function getName(): ?string + { + return $this->name; + } + + public function setName(?string $name): void + { + $this->name = $name; + } + public function getCallable() + { + return $this->callable; + } + + public function getPath(): string + { + return $this->path; + } - + public function setCallable(callable $callable) + { + $this->callable = $callable; + } } diff --git a/Sources/src/app/router/Router.php b/Sources/src/app/router/Router.php index abbcbb5b..a4a60415 100644 --- a/Sources/src/app/router/Router.php +++ b/Sources/src/app/router/Router.php @@ -27,49 +27,58 @@ Envoi de la réponse : Il envoie finalement la réponse HTTP au client en utilis path = $path; - $this->routes = new AltoRouter($this->$path); + $this->routes = new \AltoRouter($this->$path); + } + + // add a new Route to Collection + public function add(string $method,Route $route) { + if (!in_array($method, HttpMethod::values(), true)){ + throw new \InvalidArgumentException("Method not suported"); + } + $this->routes->map($method, $route->getPath(), $route->getCallable(), $route->getName()); + } - public function add(Route $route, $name) {} - - // extrait également les paramètres d'URL de l'URL demandée + // extrait les paramètres d'URL de l'URL demandée public function extractParams(string $path) {} public function get(string $path, callable $callable, $name) { - $this->router->map('GET', $path, $callable, $name); + $this->routes->map('GET', $path, $callable, $name); } public function post(string $path, callable $callable, $name) { - $this->router->map('POST', $path, $callable, $name); + $this->routes->map('POST', $path, $callable, $name); } public function put(string $path, callable $callable, $name) { - $this->router->map('PUT', $path, $callable, $name); + $this->routes->map('PUT', $path, $callable, $name); } - public function match (IRequest $request): ?Route { + /// check if the request can be process + public function match (IRequest $request): ?array { $result = $this->routes->match($request->getRequestUri(), $request->getMethod()); if ($result) { - return new Route($result['params'],$result['target'],$result['name']) ; + // not Route + // retunr $result + return $result; } return null; } - public function - } diff --git a/Sources/src/app/router/Session.php b/Sources/src/app/router/Session.php new file mode 100644 index 00000000..3c97d606 --- /dev/null +++ b/Sources/src/app/router/Session.php @@ -0,0 +1,121 @@ +startSession(); + + return self::$instance; + } + + + /** + * (Re)starts the session. + * + * @return bool TRUE if the session has been initialized, else FALSE. + **/ + + private function startSession() + { + if ( $this->sessionState == self::SESSION_NOT_STARTED ) + { + $this->sessionState = session_start(); + } + + return $this->sessionState; + } + + + /** + * Stores datas in the session. + * Example: $instance->foo = 'bar'; + * + * @param name Name of the datas. + * @param value Your datas. + * @return void + **/ + + public function __set( $name , $value ) + { + $_SESSION[$name] = $value; + } + + + /** + * Gets datas from the session. + * Example: echo $instance->foo; + * + * @param name Name of the datas to get. + * @return mixed Datas stored in session. + **/ + + public function __get( string $name ) + { + if ( isset($_SESSION[$name])) + { + return $_SESSION[$name]; + } + } + + + public function __isset( $name ) + { + return isset($_SESSION[$name]); + } + + + public function __unset( $name ) + { + unset( $_SESSION[$name] ); + } + + + /** + * Destroys the current session. + * + * @return bool TRUE is session has been deleted, else FALSE. + **/ + + public function destroy() + { + if ( $this->sessionState == self::SESSION_STARTED ) + { + $this->sessionState = !session_destroy(); + unset( $_SESSION ); + + return !$this->sessionState; + } + + return FALSE; + } +} diff --git a/Sources/src/app/router/annotation/Get.php b/Sources/src/app/router/annotation/Get.php new file mode 100644 index 00000000..646c2af5 --- /dev/null +++ b/Sources/src/app/router/annotation/Get.php @@ -0,0 +1,18 @@ +path; + } + + public function getName(): ?string{ + return $this->name; + } + public function getMethods(): array | HttpMethod + { + return $this->methods; + } + + +} + diff --git a/Sources/src/app/router/middleware/HttpValidationMiddleware.php b/Sources/src/app/router/middleware/HttpValidationMiddleware.php index c6da37dd..9c03bd0e 100644 --- a/Sources/src/app/router/middleware/HttpValidationMiddleware.php +++ b/Sources/src/app/router/middleware/HttpValidationMiddleware.php @@ -1,4 +1,7 @@ [ - ['callback' => Validator::required(), 'message' => 'Email is required.'], - ['callback' => Validator::email(), 'message' => 'Email must be a valid email address.'] - ], - // Add more rules as needed -]; \ No newline at end of file +// $validationRules = [ +// 'email' => [ +// ['callback' => Validator::required(), 'message' => 'Email is required.'], +// ['callback' => Validator::email(), 'message' => 'Email must be a valid email address.'] +// ], +// // Add more rules as needed +// ]; \ No newline at end of file diff --git a/Sources/src/app/router/middleware/IHttpMiddleware.php b/Sources/src/app/router/middleware/IHttpMiddleware.php index 8273d43d..a8e69938 100644 --- a/Sources/src/app/router/middleware/IHttpMiddleware.php +++ b/Sources/src/app/router/middleware/IHttpMiddleware.php @@ -1,4 +1,8 @@ queryParameters = $query; $this->requestUri = $server['REQUEST_URI'] ?? ''; $this->method = strtoupper($server['REQUEST_METHOD'] ?? 'GET'); $this->headers = $headers; $this->requestParameters = $contentStrategy->getContent(); + $this->body = $body; } public function getQueryParameters(): array { @@ -39,5 +44,12 @@ class HttpRequest implements IRequest { public function getHeaders(): array { return $this->headers; } + + public function getBody(): array{ + return $this->body; + } + public function addToBody(string|array $attributes){ + $this->body[] = $attributes; + } } \ No newline at end of file diff --git a/Sources/src/app/router/request/IRequest.php b/Sources/src/app/router/request/IRequest.php index 336f99af..987558e1 100644 --- a/Sources/src/app/router/request/IRequest.php +++ b/Sources/src/app/router/request/IRequest.php @@ -1,9 +1,14 @@ userGateway = $userGateway; $this->session = &$session; diff --git a/Sources/src/data/core/database/Connection.php b/Sources/src/data/core/database/Connection.php new file mode 100644 index 00000000..e983cb03 --- /dev/null +++ b/Sources/src/data/core/database/Connection.php @@ -0,0 +1,14 @@ +host;port=$this->port;dbname=$this->database;user=$this->username;password=$this->password"; + parent::__construct($dsn); + echo "Successfully connected to the database"; + $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + } +} +?> \ No newline at end of file diff --git a/Sources/src/data/core/database/DatabaseQueryExecutor.php b/Sources/src/data/core/database/DatabaseQueryExecutor.php new file mode 100644 index 00000000..f38f97cb --- /dev/null +++ b/Sources/src/data/core/database/DatabaseQueryExecutor.php @@ -0,0 +1,50 @@ +connection = $dbConnection; + } + + public function executeQuery(string $query, array $parameters = []): bool { + $stmt = $this->connection->prepare($query); + foreach ($parameters as $name => $value) { + $stmt->bindValue($name, $value[0], $value[1]); + } + return $stmt->execute(); + } + + public function executeTransaction(callable $transaction): bool { + try { + $this->connection->beginTransaction(); + $transaction($this); + $this->connection->commit(); + return true; + } catch (PDOException $e) { + $this->connection->rollBack(); + throw new Exception('Database transaction failed: ' . $e->getMessage()); + } + } + + public function fetchAll(string $query, array $parameters = []): array { + $stmt = $this->connection->prepare($query); + foreach ($parameters as $name => $value) { + $stmt->bindValue($name, $value[0], $value[1]); + } + $stmt->execute(); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + public function fetchOne(string $query, array $parameters = []): ?array { + $stmt = $this->connection->prepare($query); + foreach ($parameters as $name => $value) { + $stmt->bindValue($name, $value[0], $value[1]); + } + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + return $result ?: null; + } +} +?> diff --git a/Sources/src/data/core/database/DbContext.php b/Sources/src/data/core/database/DbContext.php new file mode 100644 index 00000000..5baf2b13 --- /dev/null +++ b/Sources/src/data/core/database/DbContext.php @@ -0,0 +1,35 @@ +onConfiguring(); + $this->onModelCreating(); + } + + protected abstract function onConfiguring(); + protected abstract function onModelCreating(); + + protected function isConfigured() { + return $this->databaseConnection !== null; + } + // getInstantce + protected function seedDatabase($sqlFilePath) { + if (!$this->isConfigured()) { + if (file_exists($sqlFilePath)) { + try { + $sql = file_get_contents($sqlFilePath); + $this->databaseConnection->exec($sql); + echo "Database seeded successfully."; + } catch (\PDOException $e) { + echo "Error seeding database: " . $e->getMessage(); + } + } else { + echo "SQL file not found: $sqlFilePath"; + } + } + + } +} diff --git a/Sources/src/data/core/database/DbManager.php b/Sources/src/data/core/database/DbManager.php new file mode 100644 index 00000000..b5c97b85 --- /dev/null +++ b/Sources/src/data/core/database/DbManager.php @@ -0,0 +1,11 @@ +userMgr = new UserRepository($this); + } + +} diff --git a/Sources/src/data/core/database/HeartDbContext.php b/Sources/src/data/core/database/HeartDbContext.php new file mode 100644 index 00000000..bb491243 --- /dev/null +++ b/Sources/src/data/core/database/HeartDbContext.php @@ -0,0 +1,49 @@ +userGateWay = new UserGateway($databaseConnection); + } + + public function onConfiguring() { + if (!$this->isConfigured()) { + echo "!IsConfigured...\n"; + try { + $options = $this->getConnectionOptions(); + $this->databaseConnection = DatabaseConnection::getInstance($options['dsn'], $options['username'], $options['password']); + echo 'Successfully connected to the database'; + } catch (PDOException $e) { + echo 'Error connecting to the database: ' . $e->getMessage(); + } + } + + + } + protected function getConnectionOptions(): array { + // 'dbname' => 'mydb', + // 'user' => 'user', + // 'password' => 'secret', + // 'host' => 'localhost', + // 'driver' => 'pdo_mysql', + return [ + 'dsn' => 'mysql:host=localhost;dbname=your_database', + 'username' => 'your_username', + 'password' => 'your_password' + ]; + } + + + public function onModelCreating() { + if (!$this->isConfigured() && $this->seed) { + $this->seedDatabase(__DIR__ . '/../../../config/heart.sql'); + } + } +} diff --git a/Sources/src/data/core/database/IDatabaseConnection.php b/Sources/src/data/core/database/IDatabaseConnection.php new file mode 100644 index 00000000..37e17c64 --- /dev/null +++ b/Sources/src/data/core/database/IDatabaseConnection.php @@ -0,0 +1,4 @@ +getId(), + $user->getNom(), + $user->getPrenom(), + $user->getEmail(), + $user->getSexe(), + $user->getTaille(), + $user->getPoids(), + $user->getDateNaissance(), + $user->getRole() + ); + } + + /** + * Convert a UserEntity to a User entity. + * + * @param UserEntity $entity + * @return User + */ + public static function toModel(UserEntity $entity): User { + $user = new User( + $entity->id, + $entity->nom, + $entity->prenom, + $entity->email, + // Handle password appropriately + 'defaultPassword', // This is a placeholder. Handle with care. + $entity->sexe, + $entity->taille, + $entity->poids, + $entity->dateNaissance, + $entity->role + ); + + return $user; + } + /** + * Map SQL result to UserEntity object. + * + * @param array $row + * @return UserEntity + */ + public static function fromSql(array $row): UserEntity { + $dateNaissance = new \DateTime($row['date_naissance']); // Adjust the key as per your database column name + $role = new Role(/* parameters based on your Role class constructor */); + + return new UserEntity( + $row['id'], + $row['nom'], + $row['prenom'], + $row['email'], + $row['mot_de_passe'], // Make sure this is handled securely + $row['sexe'], + $row['taille'], + $row['poids'], + $dateNaissance, + $role + ); + } +} diff --git a/Sources/src/data/core/database/entity/AthleteEntity.php b/Sources/src/data/core/database/entity/AthleteEntity.php new file mode 100644 index 00000000..b1003fa0 --- /dev/null +++ b/Sources/src/data/core/database/entity/AthleteEntity.php @@ -0,0 +1,15 @@ +queryExecutor = $queryExecutor; + } + + public function getItemById(int $id): ?UserEntity { + $result = $this->queryExecutor->fetchOne("SELECT * FROM users WHERE id = :id", ['id' => [$id, PDO::PARAM_INT]]); + return $result ? UserMapper::fromSql($result) : null; + } + + public function getItemByEmail(string $email): ?UserEntity { + $result = $this->queryExecutor->fetchOne("SELECT * FROM users WHERE email = :email", ['email' => [$email, PDO::PARAM_STR]]); + return $result ? $this->mapper->mapToUser($result) : null; + } + + public function GetNbItems(): int { + $result = $this->queryExecutor->fetchOne("SELECT COUNT(*) as count FROM users"); + return $result ? (int)$result['count'] : 0; + } + + public function GetItems(int $index, int $count, ?string $orderingPropertyName = null, bool $descending = false): array { + $order = $descending ? 'DESC' : 'ASC'; + $query = "SELECT * FROM users " . ($orderingPropertyName ? "ORDER BY $orderingPropertyName $order " : "") . "LIMIT :index, :count"; + $parameters = ['index' => [$index, PDO::PARAM_INT], 'count' => [$count, PDO::PARAM_INT]]; + $results = $this->queryExecutor->fetchAll($query, $parameters); + return array_map([$this->mapper, 'mapToUser'], $results); + } + + public function GetItemsByName(string $substring, int $index, int $count, ?string $orderingPropertyName = null, bool $descending = false): array { + $order = $descending ? 'DESC' : 'ASC'; + $query = "SELECT * FROM users WHERE CONCAT(first_name, ' ', last_name) LIKE :substring " . ($orderingPropertyName ? "ORDER BY $orderingPropertyName $order " : "") . "LIMIT :index, :count"; + $parameters = ['substring' => ["%$substring%", PDO::PARAM_STR], 'index' => [$index, PDO::PARAM_INT], 'count' => [$count, PDO::PARAM_INT]]; + $results = $this->queryExecutor->fetchAll($query, $parameters); + return array_map([$this->mapper, 'mapToUser'], $results); + } + + public function UpdateItem(UserEntity $oldUser, UserEntity $newUser): void { + // Logique pour mettre à jour un utilisateur + // Utiliser $this->queryExecutor->executeQuery pour exécuter la requête de mise à jour + } + + public function AddItem(UserEntity $user): void { + // Logique pour ajouter un nouvel utilisateur + // Utiliser $this->queryExecutor->executeQuery pour exécuter la requête d'insertion + } + + public function DeleteItem(UserEntity $user): bool { + $success = $this->queryExecutor->executeQuery("DELETE FROM users WHERE id = :id", ['id' => [$user->getId(), PDO::PARAM_INT]]); + return $success; + } +} diff --git a/Sources/src/data/core/database/repository/UserRepository.php b/Sources/src/data/core/database/repository/UserRepository.php new file mode 100644 index 00000000..476d296e --- /dev/null +++ b/Sources/src/data/core/database/repository/UserRepository.php @@ -0,0 +1,66 @@ +parent = $dbContext; + } + + public function getItemById(int $id): ?User { + throw new NotImplementedException(); + } + public function GetItemByName(string $substring, int $index, int $count, ?string $orderingPropertyName = null, bool $descending = false){ + throw new NotImplementedException(); + } + + public function getItemByEmail(string $email): ?User { + throw new NotImplementedException(); + + } + + public function getNbItems(): int { + throw new NotImplementedException(); + } + + public function getItems(int $index, int $count, ?string $orderingPropertyName = null, bool $descending = false): array { + throw new NotImplementedException(); + } + + public function getItemsByName(string $substring, int $index, int $count, ?string $orderingPropertyName = null, bool $descending = false): array { + throw new NotImplementedException(); + } + + public function addItem(User $user): ?User { + // Add a new user to the database + $userEntity = $this->parent->context->userGateWay->addUser($user->toEntity()); + $this->parent->dbContext->userGateWay->saveChanges(); + + return $userEntity->ToModel(); + } + + public function updateItem(User $oldUser, User $newUser): void { + throw new NotImplementedException(); + } + + public function deleteItem(User $user): bool { + // should use name and other things to equals + $usertoDelete = $this->parent->context->userGateWay->GetItemsByName($user->getNom()); + // Where(c => c.Name == item.Name).First(); + if ($usertoDelete != null) + { + $deleted = $this->dbContext->userGateWay->Remove($usertoDelete); + $this->parent->dbContext->saveChanges(); + return true; + } + return false; + } + +} + +?> diff --git a/Sources/src/shared/exception/ContainerException.php b/Sources/src/shared/exception/ContainerException.php new file mode 100644 index 00000000..1ccbb110 --- /dev/null +++ b/Sources/src/shared/exception/ContainerException.php @@ -0,0 +1,8 @@ +