From cdda2e0ebfbdf7e55de283ead32481176c3dcf70 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Thu, 29 Aug 2024 10:16:54 +0200 Subject: [PATCH 01/56] chore: add project file structure --- .gitignore | 1 + README.md | 1 + doc/MCD.loo | 0 public/index.php | 0 src/controllers/controller.php | 0 src/models/model.php | 0 src/views/view.php | 0 7 files changed, 2 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 doc/MCD.loo create mode 100644 public/index.php create mode 100644 src/controllers/controller.php create mode 100644 src/models/model.php create mode 100644 src/views/view.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec23188 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# MAW11-ABE-NUI \ No newline at end of file diff --git a/doc/MCD.loo b/doc/MCD.loo new file mode 100644 index 0000000..e69de29 diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..e69de29 diff --git a/src/controllers/controller.php b/src/controllers/controller.php new file mode 100644 index 0000000..e69de29 diff --git a/src/models/model.php b/src/models/model.php new file mode 100644 index 0000000..e69de29 diff --git a/src/views/view.php b/src/views/view.php new file mode 100644 index 0000000..e69de29 From 0a4a9f11ebce967cf9e7df08e7df661aceae3787 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Thu, 29 Aug 2024 16:08:21 +0200 Subject: [PATCH 02/56] feat: make website functional with initial setup --- public/index.php | 10 ++++++++++ src/views/home.php | 11 +++++++++++ src/views/view.php | 0 3 files changed, 21 insertions(+) create mode 100644 src/views/home.php delete mode 100644 src/views/view.php diff --git a/public/index.php b/public/index.php index e69de29..2f58a5d 100644 --- a/public/index.php +++ b/public/index.php @@ -0,0 +1,10 @@ + + + + + + ExerciseLooper + + +

Exercise Looper

+ + \ No newline at end of file diff --git a/src/views/view.php b/src/views/view.php deleted file mode 100644 index e69de29..0000000 From 6c0bf98e68e54cf1cfb95046f0d595c1391a8db3 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Thu, 29 Aug 2024 16:26:12 +0200 Subject: [PATCH 03/56] feat: add simple router --- public/index.php | 12 +++++++++++- src/views/404.php | 11 +++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/views/404.php diff --git a/public/index.php b/public/index.php index 2f58a5d..5867ea2 100644 --- a/public/index.php +++ b/public/index.php @@ -6,5 +6,15 @@ define('SOURCE_DIR', BASE_DIR.'/src'); define('VIEW_DIR', SOURCE_DIR.'/views'); +$request = $_SERVER['REQUEST_URI']; -require VIEW_DIR.'/home.php'; \ No newline at end of file +switch ($request) { + case '': + case '/': + require VIEW_DIR.'/home.php'; + break; + + default: + http_response_code(404); + require VIEW_DIR.'/404.php'; +} \ No newline at end of file diff --git a/src/views/404.php b/src/views/404.php new file mode 100644 index 0000000..3fae2a9 --- /dev/null +++ b/src/views/404.php @@ -0,0 +1,11 @@ + + + + + + ExerciseLooper + + + 404 Error + + \ No newline at end of file From 85812ef70eebee21c0c694c4d8a8b7eb0c752a7c Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Thu, 29 Aug 2024 16:39:12 +0200 Subject: [PATCH 04/56] refactor: add layout for view pages --- src/layout.php | 13 +++++++++++++ src/views/404.php | 24 +++++++++++++----------- src/views/home.php | 24 +++++++++++++----------- 3 files changed, 39 insertions(+), 22 deletions(-) create mode 100644 src/layout.php diff --git a/src/layout.php b/src/layout.php new file mode 100644 index 0000000..30f7561 --- /dev/null +++ b/src/layout.php @@ -0,0 +1,13 @@ + + + + + + <?=$title?> + + +
+ +
+ + \ No newline at end of file diff --git a/src/views/404.php b/src/views/404.php index 3fae2a9..75c2d2b 100644 --- a/src/views/404.php +++ b/src/views/404.php @@ -1,11 +1,13 @@ - - - - - - ExerciseLooper - - - 404 Error - - \ No newline at end of file + +

404 Error

+ - - - - - ExerciseLooper - - -

Exercise Looper

- - \ No newline at end of file + +

Exercise Looper

+ Date: Tue, 3 Sep 2024 11:06:26 +0200 Subject: [PATCH 05/56] chore: add new packages and create composer.lock --- .gitignore | 3 +- composer.json | 6 + composer.lock | 481 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 composer.json create mode 100644 composer.lock diff --git a/.gitignore b/.gitignore index 2eea525..db27dc8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +vendor \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..140b7af --- /dev/null +++ b/composer.json @@ -0,0 +1,6 @@ +{ + "require": { + "ext-pdo": "*", + "vlucas/phpdotenv": "^5.6" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..2503fa1 --- /dev/null +++ b/composer.lock @@ -0,0 +1,481 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "e4bf1027289ee905411a6d32b3245f2e", + "packages": [ + { + "name": "graham-campbell/result-type", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:45:45+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.3", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:41:07+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", + "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T15:07:36+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-19T12:30:46+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.30.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", + "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-05-31T15:07:36+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.3", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.3", + "symfony/polyfill-ctype": "^1.24", + "symfony/polyfill-mbstring": "^1.24", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2024-07-20T21:52:34+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "ext-pdo": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} From e55ae4a37a745c135024749da8582ddf15390847 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Tue, 3 Sep 2024 11:11:08 +0200 Subject: [PATCH 06/56] chore: add .env.example file --- .env.example | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..01e641d --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +DATABASE_HOST="placeholder" +DATABASE_PORT="placeholder" +DATABASE_NAME="placeholder" +DATABASE_USERNAME="placeholder" +DATABASE_PASSWORD="placeholder" \ No newline at end of file From 122af62a39f67dec123d15e15d78785b2282bde2 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Tue, 3 Sep 2024 12:08:24 +0200 Subject: [PATCH 07/56] feat: add DBConnection class for managing database --- public/index.php | 6 ++++++ src/models/database.php | 48 +++++++++++++++++++++++++++++++++++++++++ src/models/model.php | 0 3 files changed, 54 insertions(+) create mode 100644 src/models/database.php delete mode 100644 src/models/model.php diff --git a/public/index.php b/public/index.php index 5867ea2..f389660 100644 --- a/public/index.php +++ b/public/index.php @@ -4,8 +4,14 @@ define('BASE_DIR', dirname( __FILE__ ).'/..'); define('SOURCE_DIR', BASE_DIR.'/src'); +define('MODEL_DIR', SOURCE_DIR.'/models'); define('VIEW_DIR', SOURCE_DIR.'/views'); +require_once BASE_DIR.'/vendor/autoload.php'; + +$dotenv = Dotenv\Dotenv::createImmutable(BASE_DIR); +$dotenv->load(); + $request = $_SERVER['REQUEST_URI']; switch ($request) { diff --git a/src/models/database.php b/src/models/database.php new file mode 100644 index 0000000..8ce66d5 --- /dev/null +++ b/src/models/database.php @@ -0,0 +1,48 @@ +hostname = $hostname; + $this->database = $database; + $this->username = $username; + $this->password = $password; + + $dsn = 'mysql:dbname='.$database.';host='.$hostname; + try + { + $this->pdo = new PDO($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")); + + // Set Error Mode to Exception + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + catch (PDOException $e) + { + throw new Exception("Could not connect to the database: " . $e->getMessage(), 500); + } + } + + public function closeConnection() + { + $this->pdo = null; + } + + public function getPDO() + { + return $this->pdo; + } +} \ No newline at end of file diff --git a/src/models/model.php b/src/models/model.php deleted file mode 100644 index e69de29..0000000 From 64b3af9b409caf8856a8ff99c401969c6a3fd7de Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Wed, 4 Sep 2024 10:38:26 +0200 Subject: [PATCH 08/56] feat: add query execution --- src/models/database.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/models/database.php b/src/models/database.php index 8ce66d5..c02f0e1 100644 --- a/src/models/database.php +++ b/src/models/database.php @@ -45,4 +45,24 @@ public function getPDO() { return $this->pdo; } + + /** + * Prepares and executes sql queries + * + * @param string $sql The SQL query to execute + * @param array $params The parameters of the SQL query + * @return array The results of the query + * @throws Exception If the query fails. + */ + public function query($sql, $params = []) + { + try { + $statement = $this->pdo->prepare($sql); + $statement->execute($params); + + return $statement->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + throw new Exception("Query failed: " . $e->getMessage(), 500); + } + } } \ No newline at end of file From 220da99b9a58cd845ef7b7a0cff2d5ed0186009f Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Wed, 4 Sep 2024 12:13:56 +0200 Subject: [PATCH 09/56] chore: add PHP_CodeSniffer and configure PSR12 convention --- composer.json | 3 ++ composer.lock | 85 ++++++++++++++++++++++++++++++++++++++++- public/index.php | 16 ++++---- src/layout.php | 2 +- src/models/database.php | 23 +++++------ src/views/404.php | 2 +- src/views/home.php | 2 +- 7 files changed, 107 insertions(+), 26 deletions(-) diff --git a/composer.json b/composer.json index 140b7af..db87899 100644 --- a/composer.json +++ b/composer.json @@ -1,4 +1,7 @@ { + "require-dev": { + "squizlabs/php_codesniffer": "^3.0" + }, "require": { "ext-pdo": "*", "vlucas/phpdotenv": "^5.6" diff --git a/composer.lock b/composer.lock index 2503fa1..a3e5785 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e4bf1027289ee905411a6d32b3245f2e", + "content-hash": "b77736a232e25e4b6a69a072e36780e0", "packages": [ { "name": "graham-campbell/result-type", @@ -467,7 +467,88 @@ "time": "2024-07-20T21:52:34+00:00" } ], - "packages-dev": [], + "packages-dev": [ + { + "name": "squizlabs/php_codesniffer", + "version": "3.10.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/86e5f5dd9a840c46810ebe5ff1885581c42a3017", + "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2024-07-21T23:26:44+00:00" + } + ], "aliases": [], "minimum-stability": "stable", "stability-flags": [], diff --git a/public/index.php b/public/index.php index f389660..1153409 100644 --- a/public/index.php +++ b/public/index.php @@ -2,12 +2,12 @@ session_start(); -define('BASE_DIR', dirname( __FILE__ ).'/..'); -define('SOURCE_DIR', BASE_DIR.'/src'); -define('MODEL_DIR', SOURCE_DIR.'/models'); -define('VIEW_DIR', SOURCE_DIR.'/views'); +define('BASE_DIR', dirname(__FILE__) . '/..'); +define('SOURCE_DIR', BASE_DIR . '/src'); +define('MODEL_DIR', SOURCE_DIR . '/models'); +define('VIEW_DIR', SOURCE_DIR . '/views'); -require_once BASE_DIR.'/vendor/autoload.php'; +require_once BASE_DIR . '/vendor/autoload.php'; $dotenv = Dotenv\Dotenv::createImmutable(BASE_DIR); $dotenv->load(); @@ -17,10 +17,10 @@ switch ($request) { case '': case '/': - require VIEW_DIR.'/home.php'; + include VIEW_DIR . '/home.php'; break; default: http_response_code(404); - require VIEW_DIR.'/404.php'; -} \ No newline at end of file + include VIEW_DIR . '/404.php'; +} diff --git a/src/layout.php b/src/layout.php index 30f7561..46a4bde 100644 --- a/src/layout.php +++ b/src/layout.php @@ -10,4 +10,4 @@ - \ No newline at end of file + diff --git a/src/models/database.php b/src/models/database.php index c02f0e1..14814e1 100644 --- a/src/models/database.php +++ b/src/models/database.php @@ -4,7 +4,7 @@ use PDOException; use Exception; -use \PDO; +use PDO; class DBConnection { @@ -14,7 +14,7 @@ class DBConnection private $password; private $pdo; - + public function __construct($hostname, $database, $username, $password) { $this->hostname = $hostname; @@ -22,16 +22,13 @@ public function __construct($hostname, $database, $username, $password) $this->username = $username; $this->password = $password; - $dsn = 'mysql:dbname='.$database.';host='.$hostname; - try - { + $dsn = 'mysql:dbname=' . $database . ';host=' . $hostname; + try { $this->pdo = new PDO($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")); - + // Set Error Mode to Exception $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } - catch (PDOException $e) - { + } catch (PDOException $e) { throw new Exception("Could not connect to the database: " . $e->getMessage(), 500); } } @@ -40,12 +37,12 @@ public function closeConnection() { $this->pdo = null; } - + public function getPDO() { return $this->pdo; } - + /** * Prepares and executes sql queries * @@ -59,10 +56,10 @@ public function query($sql, $params = []) try { $statement = $this->pdo->prepare($sql); $statement->execute($params); - + return $statement->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { throw new Exception("Query failed: " . $e->getMessage(), 500); } } -} \ No newline at end of file +} diff --git a/src/views/404.php b/src/views/404.php index 75c2d2b..864882f 100644 --- a/src/views/404.php +++ b/src/views/404.php @@ -10,4 +10,4 @@ $content = ob_get_clean(); -require SOURCE_DIR."/layout.php"; \ No newline at end of file +require SOURCE_DIR . "/layout.php"; \ No newline at end of file diff --git a/src/views/home.php b/src/views/home.php index 9f0974c..f7b2637 100644 --- a/src/views/home.php +++ b/src/views/home.php @@ -10,4 +10,4 @@ $content = ob_get_clean(); -require SOURCE_DIR."/layout.php"; \ No newline at end of file +require SOURCE_DIR . "/layout.php"; From 7e3133a2cd51c5976555461cc8e60129b9747936 Mon Sep 17 00:00:00 2001 From: Nazmi Uksmajli Date: Tue, 10 Sep 2024 11:45:26 +0200 Subject: [PATCH 10/56] chore: add gitignore template --- .gitignore | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index db27dc8..0d42335 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,172 @@ +# Cache, temp and personal files .env -vendor \ No newline at end of file +/.htaccess +*.log + +# Cache +/cache/* +!/cache/.htaccess +!/cache/cachefs/index.php +!/cache/deprecated.txt +!/cache/index.php +!/cache/purifier/index.php +!/cache/push/activity +!/cache/push/index.php +!/cache/push/trends +!/cache/sandbox/index.php +!/cache/smarty/cache/index.php +!/cache/smarty/compile/index.php +!/cache/smarty/index.php +!/cache/tcpdf/index.php + +# Download +/download/* +!/download/.htaccess +!/download/index.php + +# Images +/img/* +!/img/.htaccess +!/img/index.php +!/img/404.gif +!/img/bg_500.png +!/img/bg_loader.png +!/img/favicon.ico +!/img/loader.gif +!/img/loadingAnimation.gif +!/img/logo.jpg +!/img/logo.png +!/img/logo_invoice.jpg +!/img/logo_stores.png +!/img/macFFBgHack.png +!/img/prestashop-avatar.png +!/img/prestashop@2x.png +!/img/preston-login-wink@2x.png +!/img/preston-login@2x.png +!/img/questionmark.png +!/img/genders/index.php +!/img/admin/index.php +!/img/c/index.php +!/img/cms/index.php +!/img/co/index.php +!/img/jquery-ui +!/img/l/index.php +!/img/m/index.php +!/img/os/index.php +!/img/p/index.php +!/img/s/index.php +!/img/scenes +!/img/st/index.php +!/img/su/index.php +!/img/t/index.php +!/img/tmp/index.php + +# Upload +/upload/* +!/upload/.htaccess + +/vendor/* +/docs/phpdoc-sf/ +/composer.lock +*.hot-update.js +*.hot-update.json + + +/admin-dev/autoupgrade/* +!/admin-dev/autoupgrade/index.php +!/admin-dev/autoupgrade/backup/index.php + +/admin-dev/backups/* +!/admin-dev/backups/.htaccess + +/admin-dev/import/* +!/admin-dev/import/.htaccess +!/admin-dev/import/index.php + +/admin-dev/export/* +!/admin-dev/export/.htaccess +!/admin-dev/export/index.php + +# Downloaded RTL files +/admin-dev/themes/default/css/bundle/default_rtl.css +/admin-dev/themes/default/css/bundle/shared_rtl.css +/admin-dev/themes/default/css/font_rtl.css +/admin-dev/themes/default/css/overrides_rtl.css +/admin-dev/themes/default/css/vendor/font-awesome/font-awesome_rtl.css +/admin-dev/themes/default/css/vendor/nv.d3_rtl.css +/admin-dev/themes/default/css/vendor/titatoggle-min_rtl.css +/admin-dev/themes/default/public/theme_rtl.css +/admin-dev/themes/new-theme/css/module/drop_rtl.css +/admin-dev/themes/new-theme/css/right-sidebar_rtl.css + +themes/*/cache/* + +# Config + +config/settings.inc.php +config/settings.old.php +config/xml/* +config/themes/* +!config/xml/themes/default.xml +themes/*/config/settings_*.json +app/config/parameters.old.yml +app/config/config.php + +# Themes, modules and overrides + +modules/* +override/* +themes/*/ +!themes/classic +!themes/_core +!themes/_libraries + +# Vendors and dependencies + +bower_components/ +node_modules/ +composer.phar +php-cs-fixer +.grunt/* + +# Translations and emails templates + +translations/* +mails/* +!mails/themes/ +!mails/_partials/ +themes/default-bootstrap/lang/* +themes/default-bootstrap/modules/*/translations/*.php +themes/default-bootstrap/mails/* +!themes/default-bootstrap/mails/en/ +themes/default-bootstrap/modules/*/mails/* +!themes/default-bootstrap/modules/*/mails/en + +# MISC + +*sitemap.xml +/robots.txt + +# Symfony + +/bin/ +/app/Resources/geoip/GeoLite2-City.mmdb +/app/Resources/translations/* +!/app/Resources/translations/default +/app/config/parameters.yml +/app/config/parameters.php +/build/ +/phpunit.xml +/var/* +!/var/cache +/var/cache/* +!var/cache/.gitkeep +!/var/logs +/var/logs/* +!var/logs/.gitkeep +!/var/sessions +/var/sessions/* +!var/sessions/.gitkeep +!var/SymfonyRequirements.php +/vendor/ +/web/bundles/ From 332a069b7404f03f8fc9d3f80be26f5771205a6d Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Tue, 10 Sep 2024 11:56:53 +0200 Subject: [PATCH 11/56] chore: add phpunit framework --- .gitignore | 1 + composer.json | 3 +- composer.lock | 1785 ++++++++++++++++- .../{database.php => DatabaseConnection.php} | 2 +- tests/DatabaseConnectionTest.php | 24 + 5 files changed, 1791 insertions(+), 24 deletions(-) rename src/models/{database.php => DatabaseConnection.php} (98%) create mode 100644 tests/DatabaseConnectionTest.php diff --git a/.gitignore b/.gitignore index 0d42335..1daa64a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Cache, temp and personal files .env +.vscode /.htaccess *.log diff --git a/composer.json b/composer.json index db87899..42f4543 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,7 @@ { "require-dev": { - "squizlabs/php_codesniffer": "^3.0" + "squizlabs/php_codesniffer": "^3.0", + "phpunit/phpunit": "^9.6" }, "require": { "ext-pdo": "*", diff --git a/composer.lock b/composer.lock index a3e5785..9fc8233 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b77736a232e25e4b6a69a072e36780e0", + "content-hash": "ad73e87473f3936c17a26c77efeba743", "packages": [ { "name": "graham-campbell/result-type", @@ -145,20 +145,20 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -204,7 +204,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -220,24 +220,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -284,7 +284,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -300,24 +300,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -364,7 +364,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" }, "funding": [ { @@ -380,7 +380,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "vlucas/phpdotenv", @@ -468,6 +468,1697 @@ } ], "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2024-06-12T14:39:25+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.1.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + }, + "time": "2024-07-01T20:03:41+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.32", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.6" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:23:01+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "49d7820565836236411f5dc002d16dd689cde42f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", + "reference": "49d7820565836236411f5dc002d16dd689cde42f", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.5.0 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-07-10T11:45:39+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:30:58+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:33:00+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:35:11+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, { "name": "squizlabs/php_codesniffer", "version": "3.10.2", @@ -547,6 +2238,56 @@ } ], "time": "2024-07-21T23:26:44+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" } ], "aliases": [], diff --git a/src/models/database.php b/src/models/DatabaseConnection.php similarity index 98% rename from src/models/database.php rename to src/models/DatabaseConnection.php index 14814e1..0cf9427 100644 --- a/src/models/database.php +++ b/src/models/DatabaseConnection.php @@ -6,7 +6,7 @@ use Exception; use PDO; -class DBConnection +class DatabaseConnection { private $hostname; private $database; diff --git a/tests/DatabaseConnectionTest.php b/tests/DatabaseConnectionTest.php new file mode 100644 index 0000000..33ab2fe --- /dev/null +++ b/tests/DatabaseConnectionTest.php @@ -0,0 +1,24 @@ +load(); + +final class DatabaseConnectionTest extends TestCase +{ + public function testGreetsWithName(): void + { + $database = new DatabaseConnection($_ENV['DATABASE_HOST'], $_ENV['DATABASE_NAME'], $_ENV['DATABASE_USERNAME'], $_ENV['DATABASE_PASSWORD']); + + $result = $database->query('Alice'); + + $this->assertSame('Hello, Alice!', $result); + } +} From 3043426f73011f4ef1997436fbfac750532cc892 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Wed, 11 Sep 2024 10:34:22 +0200 Subject: [PATCH 12/56] chore: added README template --- README.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ec23188..b4c50d0 100644 --- a/README.md +++ b/README.md @@ -1 +1,68 @@ -# MAW11-ABE-NUI \ No newline at end of file +# MAW11-ABE-NUI + +## Description + +This project is designed to .... and the main features are ... + +## Getting Started + +### Prerequisites + +List all dependencies and their version needed by the project as : + +- DataBase Engine (MySql, PostgreSQL, MSSQL,...) +- IDE used (PhpStorm, Visual Studio Code, IntelliJ,...) +- Package manager (Nuget, Composer, npm, ...) +- OS supported (W2k22, Debian12,...) +- Virtualization (Docker, .Net, .JDK, .JRE) + +### Configuration + +How to set up the database? +How do you set the sensitive data? + +## Deployment + +### On dev environment + +How to get dependencies and build? +How to run the tests? + +### On integration environment + +How to deploy the application outside the dev environment. + +## Directory structure + +- Tip: try the tree bash command + +```shell +├───Docs +├───Shopping //classes and packages +│ ├───bin //the binary to deploy on the end-user environment +│ │ └───Debug +│ └───obj +│ └───Debug +└───TestShopping //test classes + ├───bin + │ └───Debug + └───obj + └───Debug +``` + +## Collaborate + +- Take time to read some readme and find the way you would like to help other developers collaborate with you. + +- They need to know: + - How to propose a new feature (issue, pull request) + - [How to commit](https://www.conventionalcommits.org/en/v1.0.0/) + - [How to use your workflow](https://nvie.com/posts/a-successful-git-branching-model/) + +## License + +- [Choose the license adapted to your project](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository). + +## Contact + +- How to get in contact with you? Discord, Trello, Issue? From 5b9df80cf3a7c9ed753768a65b6eaedd37402f15 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Wed, 11 Sep 2024 11:32:50 +0200 Subject: [PATCH 13/56] test --- dev | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dev diff --git a/dev b/dev new file mode 100644 index 0000000..e69de29 From bc8a726c0accb6443cf79ab8ed291ec1a2d97fed Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Tue, 24 Sep 2024 11:03:44 +0200 Subject: [PATCH 14/56] feat: Added home page HTML --- public/img/logo.png | Bin 0 -> 3019 bytes public/index.php | 2 ++ src/views/home.php | 21 ++++++++++++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 public/img/logo.png diff --git a/public/img/logo.png b/public/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fb855c1a31fcab09ad6664c66a1ff532a8890ac2 GIT binary patch literal 3019 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 -

Exercise Looper

+
+
+

+

Exercise
Looper

+
+
+ + Date: Tue, 24 Sep 2024 13:23:32 +0200 Subject: [PATCH 15/56] doc: conceptualized CDM --- doc/MCD.loo | Bin 0 -> 34588 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/MCD.loo b/doc/MCD.loo index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c05936a4082b9f3eb94522729313b35cc2ed5940 100644 GIT binary patch literal 34588 zcmeI5&5ImG6u@iaX0s+H#_w-kqwyntBz|iYS=mG+=A`i?f^oBAAg;Qu_;H$p2>uD) zauQF92+@Owz4-+}g5be}ISC5tiiqOD@%_5`&A#dM%b
#BRTP&f4=<2v|$Na`W@esu2YJPi%tI>1H= zD=RB!#J2AmjfNwX@kN8RJ@8AxsLtfW*=HEw12yD9ZHTj!ylk*5RA=2iWN2523&ZkxxR$8NODFvkh5 zF_j0dUksdAzKG$M82)Tt#^^FTBTpSRJWM^(@d#cS%Yf#O4L{#}@8=w)XD8)QOe?*= z*w<~#zym;hM8c_4r&{udu6h9ubTBV}G9CMp0P?nh!p;9=EJ$IfGT56rcGGTd-_qgN zA6c585xGy8ALA4Dptd1s%MJ!Tx7F0Cb^E%wvb{nlTr986e9@AL#*AbiHt*ZAYplrx zeyj6NYc~5nIJEG_^1^I@mRw%BB~n2PCeYG}JM%RCVZryRrHmIo?)=N2GJ(m)tQ?1J zA(v&>sA3^fK?{;zwL}piqT{unt6@+r#m?K%i)ot|91Yd7q%9Oj6V9DHap0>z9{y>z zh%*9}g>!|j*Emb?GJdE_>EHl)0ZR{KN0-V~k%$fb%{1_>klE4HL0N}caEopxlM&~N zvhCCdT2JipxbyGji$Ac%`vdzImya$lObUJ21GiN)#RpNi15)_7@cX{Rze*g&T-3Og zbK*g`mtq2$xtzVkZV0EkpJxf{@mm=ldOk@rN z37I6Po^;D{J7d|smX8ATJD`q%PNc;&c{DG$j#J=8&^j#r@pqS|R&z(k;$=<_G<9^S zgKL%X!#Smc17(g5bji2zkc@xJG7c_4XthRbAO?zQXjNMN#Vc2C(w191M9agBC4N?K zA5R9KvyeBS{ozyXeW0{)imeWf6W_`!Rw%vDnBi!CzNg#!N0*ofJ$GAn4PPg6BlM?H zqgz_LeJH$#vE;p#d6x=WkQO~!qKFXj_8dm99+38{9c$d5JzD`Ly|U7X4IkZN+T6>J zeWQzM7VqV0sYaH0en?m8D(NyGGmIVGFDxjx8dtO3#2?(SzTW1L}hp4YJxPkpUxUKU@E=Y`aDZL8>cC|!GHbZcH02@AT`rEz_0UU%dT zEVh6AHBx(Soa5<6h$qJMg(TH3;z2a*TbW*tOWEtiSi(Iq<~eP*kwLikdXo7sQ*-VG zc}nC(_i}nx%!?(`V;B#4leFI2#-R3LG%S~A8g=7RthQ&xM)bVY54(15*0m&y_ws0v zWYx7K>JXirJmlV#u`ld%VsGu*vqjr&@eu6}GmcD)bn6>O=qn(NAzlu#9Q}XnFl%Pl zN~5c3)acgCb_|90FqXWtGVfMf)0&x;Mr^pZ=P-J8ebTFScR%CaIK$}H%s&42y_33T zX7OI0md-SI92#s76ltSdGrLaMQf@V_x+bvp(&b%wqm$=ldp{OD%$gY+-l%42Jcx!J zlm))yQrAZkhg&l{rzY;D8Ux85j4&cVLwYV-q`hhf8V_O*UTm31oJ%liRRpSJSZG@# zJ7-_^Hsykwk@pIJmNlc7WLpB>u}1gM13>)(?a5-AJen8WmL+XL>#($UzTS3$t{hps zmp?-tT&s*9&M6%nDD!8aOTJC>S<9ED<%{xeDWEOVT8M#S8k*(>@3-~8oqOkn+IowJ zX#HekHsc;&*8jZCx3{S3+i+|`h=gpYIJQhb(Wb3wM*tCTw7@nRTm3c~Pb?feynOVf zMXw5~bQAgQ%6ip~SFo+hf%m6&EUxVpd;U^N@=7N=9-b5$rU~fouiy*|Va#c}T&-HW z;8!7=s;B47@OMsm0JY%Fb{t!mX&J#h^tYx|AlUBv0gWf8_Xw~_(YL&_{ zX_QJLju;o%xtkk){21;mf0bOR+Bn55e?tBnFv~Nu$1%&pMYVQG1ugKi{F>8zq_vHV z_gq>T1@Ys7rHmIo?)-achZ&Er$@|-CT7N#W)s3|5W2F%n?yVw>R?SG$tww zG|Q_b;)wk$ALcgBoqsRkFb~Sy zEElud1wM#|eIN$)p%k7KWBEnmF!r&lChn!0tR}I9ZTfP>jC?7H8|U-sWiv0H$d6$( z?v_kX+HGbKd-1|dD=P6SRhwTkl2@+gzgzuO{Tqvy`8Lqhzd@$s;osaUoHFCsFt|0* zZXo$HUJkJw(Zx{ri+0PEtTuu7r%|jV{Ua>%ffZgPisOFKp33c#3R;lOJDZNkLsrkr zM+X)qEam5t?X-jx5fa9?VY88Hwqr$>d>bo`xNv$o)PFQQllrtOV_XhtoKi`|5&Jow zuj%n+O1^pu*zdS{O7XZgC+O3h#q;fH&D~Dr@zZjE4ri?3u~knm@OE)#u5NTTJ?8@g ze4GN{0~;G9bSmSYH6^F|D&U>rwhpJd(QlA4Y^}?-%>Y|Zcjt+XwE=zJ#-}=4>C*Gd3KnSeY#DT;6p0u3paK{q<9;@rRD-YCHRU0o&32xlC)dkw75o;9~Gvv%x8XPuLkC;l(@0(dQh UZgl=#;QcXI_X1j32HoiY11P3$3;+NC literal 0 HcmV?d00001 From 6b77b95d3c42a91ca0edb1e502d8650b4ffe48ca Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Tue, 24 Sep 2024 13:24:35 +0200 Subject: [PATCH 16/56] chore: removed unnecessary file --- dev | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 dev diff --git a/dev b/dev deleted file mode 100644 index e69de29..0000000 From 5f21dd597846167ad8dff1b3c554f14ca0f1c15e Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 24 Sep 2024 19:36:24 +0200 Subject: [PATCH 17/56] doc: add associations --- doc/MCD.loo | Bin 34588 -> 48824 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/MCD.loo b/doc/MCD.loo index c05936a4082b9f3eb94522729313b35cc2ed5940..1ee238e5c61213c9b6851a127fb4932900c1e696 100644 GIT binary patch delta 1534 zcmZ{hZA@EL7{{OI7R=~Mwa~aCC8I;9k--XOPIT12IJZ; zd>NbGJjTQX4Q5|-iMn`yj<|2hw`~m(sk!7R?J6?Sb=4}qFZmV4KUPJd z#Ll3&TlOZRn%%Y~)3!NhTf=jD+wUCQc4YfH(&wI_2t3a$Pd2MH!*b_jyYqsE5hbtwiK44#6~)G2FN(Y)di*aA z4JwyAUQ_U8ac`&z#hnxS!u!X0p?Y}pmK#rU;O^|eU75i9a|Rxm)&rX+IPlk_GO*|i zRiLOlqc>TZO$PmC?u}GX>OcLLh@3LXK#sE<$o7uzy*59nh@rP0Vo@RiU<8jheC$kr z;N)ZgRKo4Y(MO-;7bB2k!ezP77Zv7PxseBOo1aL1^QW*UmGh|r4;+IqL|_KyU<5|R zyTkbk_UfN>1zo%^`Aa-ZSpDprfpG}1GXnKc4nAP+rbdsDnYW`$vvv(4}hnXCq?!3n2%}{>0bkn8d*)OLOH8BO0{J(nl)`(rqF!UPUoVF zR2wU@G_$LPrLwA6Ks9j(x4~+;CtWBMv(w?2jTU3piZZrkH+J((E6c+wWu+;0Nl6#_ zA-07~aVxDcsXV^3z{}P));{Wu*PvCJHw#7ME?SCzT-eU59js24gMN%v;ST9a7g{`T z7w?@P!ovOR=wfxVTvW-KM=bQrBv7+!W-BPqZI!%Xg7jb zNjOeSijkug6a*Rv+`M!48a)PqK@a9b^sr(SCdC?^?Ow^Aeuv-ZcfRNIJNZ9i`o1_P zH^Kq(H2wm5<%EsA%@rVHX9v)|U80CX?J6DimH`9|`J%Ye;J{4ENwdC> zKwsS@z^$Ds{RoT!c{;3^_w5{WeP0F8o=|C|liAD>i}gjAt%WVNew3O2w8cIs%tm`E zV3XnvsNcPa>?QSPa2 z>C?6PHr@?i-WWvvBP)=-aaE+Y@l%lX-89$sHr6ZB*aR!KkzWr()_%tXbGI2>xMewL z33flf`q?~R*V;9)Cw+27ml$iKaLg6$XHRvcyakJJBth$ From 3cd9bc0d756330b6754588e190d2657f4c529fdd Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 24 Sep 2024 19:39:14 +0200 Subject: [PATCH 18/56] doc: conceptualize LDM --- doc/MLD.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 doc/MLD.md diff --git a/doc/MLD.md b/doc/MLD.md new file mode 100644 index 0000000..d19f170 --- /dev/null +++ b/doc/MLD.md @@ -0,0 +1,37 @@ +```mermaid +--- +title: MAW11-ABE-NUI +--- +classDiagram + note "Project: MAW11-ABE-NUI\nTitle: Full MLD\nAuthor: Arthur Bottemanne\nVersion: 1.0v 09/24/2024" + exercises <|-- fields + field_types <|-- fields + fields <|-- answers + class exercises{ + pk(id) + id Int + Status Text + Title Text + } + class fields { + pk(id) + fk(field_types_id, exercises_id) + id Int + title Text + field_types_id Int + exercises_id Int + } + class field_types { + pk(id) + id Int + type Text + } + class answers { + pk(id) + fk(field_id) + id Int + answer_date Date + contents Text + field_id Int + } +``` \ No newline at end of file From 717d3851685a04136cf9ff36d0a023ea0d4820fb Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 24 Sep 2024 20:08:36 +0200 Subject: [PATCH 19/56] doc: fixed minor LDM issues --- doc/MLD.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/MLD.md b/doc/MLD.md index d19f170..4b314a3 100644 --- a/doc/MLD.md +++ b/doc/MLD.md @@ -10,8 +10,8 @@ classDiagram class exercises{ pk(id) id Int - Status Text - Title Text + title Text + exercise_status Enum["building", "answering", "closed"] } class fields { pk(id) From 9b7613637bda3326e88fabbfecdcc2ee2b213831 Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 24 Sep 2024 20:10:16 +0200 Subject: [PATCH 20/56] feat: changed DatabaseConnection class into singleton --- src/models/DatabaseConnection.php | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/models/DatabaseConnection.php b/src/models/DatabaseConnection.php index 0cf9427..e1f6c3b 100644 --- a/src/models/DatabaseConnection.php +++ b/src/models/DatabaseConnection.php @@ -8,6 +8,7 @@ class DatabaseConnection { + private static $instance = null; private $hostname; private $database; private $username; @@ -15,7 +16,7 @@ class DatabaseConnection private $pdo; - public function __construct($hostname, $database, $username, $password) + private function __construct($hostname, $database, $username, $password) { $this->hostname = $hostname; $this->database = $database; @@ -32,6 +33,14 @@ public function __construct($hostname, $database, $username, $password) throw new Exception("Could not connect to the database: " . $e->getMessage(), 500); } } + + public static function getInstance($hostname, $database, $username, $password) + { + if (self::$instance === null) { + self::$instance = new self($hostname, $database, $username, $password); + } + return self::$instance; + } public function closeConnection() { @@ -46,8 +55,8 @@ public function getPDO() /** * Prepares and executes sql queries * - * @param string $sql The SQL query to execute - * @param array $params The parameters of the SQL query + * @param string $sql The SQL query to execute + * @param array $params The parameters of the SQL query * @return array The results of the query * @throws Exception If the query fails. */ @@ -62,4 +71,14 @@ public function query($sql, $params = []) throw new Exception("Query failed: " . $e->getMessage(), 500); } } + + // Prevent cloning the instance + private function __clone() + { + } + + // Prevent unserializing the instance + private function __wakeup() + { + } } From f9853a11701a5e1ff27b9ee2c5543d1dfaf79170 Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 24 Sep 2024 20:31:55 +0200 Subject: [PATCH 21/56] refactor: more appropriate class and folder names --- public/index.php | 2 +- .../Database.php} | 14 ++--------- tests/DatabaseConnectionTest.php | 24 ------------------- tests/DatabaseTest.php | 22 +++++++++++++++++ 4 files changed, 25 insertions(+), 37 deletions(-) rename src/{models/DatabaseConnection.php => Models/Database.php} (90%) delete mode 100644 tests/DatabaseConnectionTest.php create mode 100644 tests/DatabaseTest.php diff --git a/public/index.php b/public/index.php index 1153409..92e27a6 100644 --- a/public/index.php +++ b/public/index.php @@ -4,7 +4,7 @@ define('BASE_DIR', dirname(__FILE__) . '/..'); define('SOURCE_DIR', BASE_DIR . '/src'); -define('MODEL_DIR', SOURCE_DIR . '/models'); +define('MODEL_DIR', SOURCE_DIR . '/Models'); define('VIEW_DIR', SOURCE_DIR . '/views'); require_once BASE_DIR . '/vendor/autoload.php'; diff --git a/src/models/DatabaseConnection.php b/src/Models/Database.php similarity index 90% rename from src/models/DatabaseConnection.php rename to src/Models/Database.php index e1f6c3b..5a636e4 100644 --- a/src/models/DatabaseConnection.php +++ b/src/Models/Database.php @@ -1,12 +1,12 @@ getMessage(), 500); } } - - // Prevent cloning the instance - private function __clone() - { - } - - // Prevent unserializing the instance - private function __wakeup() - { - } } diff --git a/tests/DatabaseConnectionTest.php b/tests/DatabaseConnectionTest.php deleted file mode 100644 index 33ab2fe..0000000 --- a/tests/DatabaseConnectionTest.php +++ /dev/null @@ -1,24 +0,0 @@ -load(); - -final class DatabaseConnectionTest extends TestCase -{ - public function testGreetsWithName(): void - { - $database = new DatabaseConnection($_ENV['DATABASE_HOST'], $_ENV['DATABASE_NAME'], $_ENV['DATABASE_USERNAME'], $_ENV['DATABASE_PASSWORD']); - - $result = $database->query('Alice'); - - $this->assertSame('Hello, Alice!', $result); - } -} diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php new file mode 100644 index 0000000..a03fc05 --- /dev/null +++ b/tests/DatabaseTest.php @@ -0,0 +1,22 @@ +load(); + +final class DatabaseTest extends TestCase +{ + public function testDatabaseNotNull(): void + { + $database = Database::getInstance('localhost', 'mydb', 'user', 'password'); + + $this->assertNotNull($database); + } +} From 56914505521d71d5acdfac018f183136b03c1b11 Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 24 Sep 2024 20:32:42 +0200 Subject: [PATCH 22/56] feat: Implemented add exercise method --- src/Models/Exercise.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/Models/Exercise.php diff --git a/src/Models/Exercise.php b/src/Models/Exercise.php new file mode 100644 index 0000000..739b228 --- /dev/null +++ b/src/Models/Exercise.php @@ -0,0 +1,15 @@ +query("INSERT INTO exercises (title, exercise_status) VALUES (:title, :exercise_status)", ["title" => $title, "exercise_status" => $exercise_status]); + } +} From d50c987a06c955c5bf1f5d6956496ae127b4ebff Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 24 Sep 2024 21:13:21 +0200 Subject: [PATCH 23/56] fix: database parameters use ENV values --- src/Models/Exercise.php | 4 ++-- tests/DatabaseTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Models/Exercise.php b/src/Models/Exercise.php index 739b228..4c136fd 100644 --- a/src/Models/Exercise.php +++ b/src/Models/Exercise.php @@ -6,9 +6,9 @@ class Exercise { - public static function addExercises($title, $exercise_status = "building") + public static function addExercise($title, $exercise_status = "building") { - $db = Database::getInstance('localhost', 'mydb', 'user', 'password'); + $db = Database::getInstance($_ENV["DATABASE_HOST"], $_ENV["DATABASE_NAME"], $_ENV["DATABASE_USERNAME"], $_ENV["DATABASE_PASSWORD"]); $db->query("INSERT INTO exercises (title, exercise_status) VALUES (:title, :exercise_status)", ["title" => $title, "exercise_status" => $exercise_status]); } diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php index a03fc05..729c0c5 100644 --- a/tests/DatabaseTest.php +++ b/tests/DatabaseTest.php @@ -15,7 +15,7 @@ final class DatabaseTest extends TestCase { public function testDatabaseNotNull(): void { - $database = Database::getInstance('localhost', 'mydb', 'user', 'password'); + $database = Database::getInstance($_ENV["DATABASE_HOST"], $_ENV["DATABASE_NAME"], $_ENV["DATABASE_USERNAME"], $_ENV["DATABASE_PASSWORD"]); $this->assertNotNull($database); } From fb5bcb950e08585c1dd0debf931248df22652d74 Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Wed, 25 Sep 2024 19:51:25 +0200 Subject: [PATCH 24/56] feat: add exercises controller --- src/Controllers/ExercisesController.php | 17 +++++++++++++++++ src/controllers/controller.php | 0 2 files changed, 17 insertions(+) create mode 100644 src/Controllers/ExercisesController.php delete mode 100644 src/controllers/controller.php diff --git a/src/Controllers/ExercisesController.php b/src/Controllers/ExercisesController.php new file mode 100644 index 0000000..5f1b9c1 --- /dev/null +++ b/src/Controllers/ExercisesController.php @@ -0,0 +1,17 @@ + Date: Wed, 25 Sep 2024 19:55:04 +0200 Subject: [PATCH 25/56] refactor: applied PSR-12 rules --- src/Controllers/ExercisesController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Controllers/ExercisesController.php b/src/Controllers/ExercisesController.php index 5f1b9c1..8ca7e0b 100644 --- a/src/Controllers/ExercisesController.php +++ b/src/Controllers/ExercisesController.php @@ -12,6 +12,6 @@ public static function create() Exercise::addExercise($name); - require_once(VIEW_DIR . '/home.php'); + include_once VIEW_DIR . '/home.php'; } -} \ No newline at end of file +} From 3c712da1f3781d556a3f8e903a01ab4e5e7105d7 Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Wed, 25 Sep 2024 22:29:46 +0200 Subject: [PATCH 26/56] fix: update LDM and CDM --- doc/MCD.loo | Bin 48824 -> 61968 bytes doc/MLD.md | 9 +++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/MCD.loo b/doc/MCD.loo index 1ee238e5c61213c9b6851a127fb4932900c1e696..0f6ff9893dbfe54012ef934d4fa004142bb6e84f 100644 GIT binary patch delta 914 zcmaJ<(Myw26u)OKvn@?mV?J1Hl`p=uG7*&M3ydi0VVmEkZaTO5eXdxu<@QiSH7Ka3 z{5+5pn4`j%D6AV1MnV69{RcioUccanw}!7Qp;lpOiIbJ*Kl|W zhfbDSj(*Yv#6EK<(#Ct6XDN+{WtwcrQAS#K;tm>-&u{1sGO#VdUduZ=Oxc~B^m5+k zmWw6LO_~gpI|HO~plrx@54-A`E|L&efF6es-?0-Ho)+ZgCqJB?Y*%__^= z(K7Mp2ktg+k4T9kCg{hW!5S0i#RZYWRL8Ll+|+c7!p~oeOR7Z*BVX9xcd zlcE@iIKJ&FDm#VB+WE_++cdDDuztI#KJ|jfrHaX;h$4z9ijpv7`PvN0iB~~q=P>@J z6p#j#M1dH9+8o delta 251 zcmbR6gn7qbrVTP&i~*Zvx#U7O@6VqkI$2wRTZ+epA(5ekA(cUap@`unLjglRLmoph zkk2&vf}!{1eg%ceQ|{Dk4sCVkX0~WxoIJ5Jc{9_*OA2s~)g~(r-apI?3`L9#4Ch!F z7+!8}+?B~Hn8_C>@yqU+0|O9%*^?cYsZZ9AahaTUU$7nEl=b=`d XYF2GlIP!*ZbHQ^1md!P9elP<75(!u; diff --git a/doc/MLD.md b/doc/MLD.md index 4b314a3..0bb4a7f 100644 --- a/doc/MLD.md +++ b/doc/MLD.md @@ -28,10 +28,15 @@ classDiagram } class answers { pk(id) - fk(field_id) + fk(field_id, fulfillment_id) id Int - answer_date Date contents Text field_id Int + fulfillment_id Int + } + class fulfillments { + pk(id) + id Int + fulfillment Date } ``` \ No newline at end of file From 101b98e87cacd18cdceb7f80738e890cabbe1326 Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Wed, 25 Sep 2024 22:32:31 +0200 Subject: [PATCH 27/56] fix: updated LDM and CDM version --- doc/MCD.loo | Bin 61968 -> 61968 bytes doc/MLD.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/MCD.loo b/doc/MCD.loo index 0f6ff9893dbfe54012ef934d4fa004142bb6e84f..6149a0cbb191b4b2f9353c88ab656ad4652e9968 100644 GIT binary patch delta 895 zcmZ{iYe*Df6o&VkS)y`C(bXh3*{%p}IOLd>nue4Ws~LzEl`dt2c|lDolY*^46fIm& zNQI%YL>NKQsZ62GTP1=Ok!DgUXt}%2>e|KrRcD*|nF9xgInR0D^M2pdPxAGX{QWQi z`@_F67_XJl7P_0q$dIEP_5>Z~uxMEphhc(`Vdsh%hPA6X4oaeBOp3JV(Ho&K2#l5S z``QYID`PZ?p=gpQ-$)Y6H;_bk6iF1WCyBt=&kUPWG%P!ru%C&9U5_U$Wh-Ic%|2D} z*G^Jgm8qd0G6+4BPH2RM(C2#yElDM`eprUHSxyiG1gHy5Me~t1_HVzMgBd)=Wo!Gz z=aSlqVjA#A3@N1X_Iwh`l@HL8$4DHlzZneW$oQ;;Q1^MwxV4gcNfl%~af$99IMK5fyQr*&NL{#PxFBa^-+>*!J6q1#jj#a;G=S{J%+F4Su6Sr-ti zi`H$@iBuBUyNdsh7JxV?<9sLmL;j;a`GP{Xywh%F?_1h;<~8MQ9Hd)GAD}bvyreMt zHW}uJop}0FkQ#c?Xeo~m-&3-xF-lhSMX!Djn;?b0amrabWd+e?1f$CgZ>A)$xVoUo zZG=*HIF$hew*=L0Vm#FI7#cicgGUc}9xDjb5(G^f;G;)m21uOl1*>8Ni(+n2Bsiy7 zA;D{eWN$bXL5j3wDV<;dm0-88fhMmJTfAEsm}VtNm^FZJMx@eX=B$l@aZUo$oB29@Qnlj delta 877 zcmZ{hZ%7ky7{~kF?TvcmszXK{B`F~xa^P8NW{Z$4H4{wxGjLd~u*_LuNrgfoMpkZ5 zNr{kUQb~~cWSP-2CrV;b6e(I6WVzjLO)c!5^-S-j4IUWx{rtY)=TGezUpvOPMhMt% znqV-RXv5@lFVLFcy!W6^KrSS}JuGHqR9m_Kf=wHThAoSYsSM*7S zE8=yFS29@?$B{)?ELpf?$l}l{vItN3LKd5JEMp^KfmFgSrVy5vM3{fg6oc>t8~#Zn z$EaK#eVs#SX*QwZS%mg&A+%&Op(7PGD9>}D*P{1zeb;s#xm1^dY|WG|Vcm7;>dFG>jYoza7@o+2%&oT$Mv3ch@dUal>r zQAR4FSyOWrH*1)4HkZf0l^Vn5>THIsm-J?K*W__czt3|}HEILSF@$w>x?;XU%CKW@ zd!B0)Dc;c|UAHN+@Ro&ZYox@E21>Nn>!TLkbD`&f?ooe#8;>z9!km3_+Yk;s(WSC> z@>q;B3_G5tG7RmZI|V0Ce_q2G4<;AhkT(1|PU<2!dCE zYA-P=X{zW{Wv6O@eX11%p8`go2%l7$ErInuH&`_ZGPPKzrod^<3MqaG%zhIUL7Ea0 xN;?aq5*&^wXz)we=uc!28Bic4Ai~5?naY5 Date: Thu, 26 Sep 2024 14:05:32 +0200 Subject: [PATCH 28/56] feat: add css template --- public/css/create_style.css | 76 +++++ public/css/milligram.min.css | 547 +++++++++++++++++++++++++++++++++++ 2 files changed, 623 insertions(+) create mode 100644 public/css/create_style.css create mode 100644 public/css/milligram.min.css diff --git a/public/css/create_style.css b/public/css/create_style.css new file mode 100644 index 0000000..4baf3ab --- /dev/null +++ b/public/css/create_style.css @@ -0,0 +1,76 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: "Arial", sans-serif; + background-color: white; +} + +header { + background-color: #f4a261; + color: white; + text-align: center; + padding: 30px 0; +} + +.logo-container { + margin-bottom: 20px; +} + +.logo { + width: 50px; + cursor: pointer; +} + +h1 { + font-size: 36px; + margin-top: 20px; +} + +main { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 50px; +} + +.exercise-form { + display: flex; + flex-direction: column; + align-items: center; + width: 300px; +} + +label { + font-size: 18px; + margin-bottom: 10px; +} + +input[type="text"] { + width: 100%; + padding: 10px; + font-size: 16px; + margin-bottom: 20px; + border: 1px solid #ccc; + border-radius: 4px; +} + +.btn { + font-size: 18px; + padding: 10px 20px; + border: none; + border-radius: 5px; + color: white; + cursor: pointer; +} + +.purple { + background-color: #9161d6; +} + +.btn:hover { + opacity: 0.9; +} diff --git a/public/css/milligram.min.css b/public/css/milligram.min.css new file mode 100644 index 0000000..d82d53f --- /dev/null +++ b/public/css/milligram.min.css @@ -0,0 +1,547 @@ +*, +*:after, +*:before { + box-sizing: inherit; +} +html { + box-sizing: border-box; + font-size: 62.5%; +} +body { + color: #606c76; + font-family: "Roboto", "Helvetica Neue", "Helvetica", "Arial", sans-serif; + font-size: 1.6em; + font-weight: 300; + letter-spacing: 0.01em; + line-height: 1.6; +} +blockquote { + border-left: 0.3rem solid #d1d1d1; + margin-left: 0; + margin-right: 0; + padding: 1rem 1.5rem; +} +blockquote *:last-child { + margin-bottom: 0; +} +.button, +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + background-color: #9b4dca; + border: 0.1rem solid #9b4dca; + border-radius: 0.4rem; + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 1.1rem; + font-weight: 700; + height: 3.8rem; + letter-spacing: 0.1rem; + line-height: 3.8rem; + padding: 0 3rem; + text-align: center; + text-decoration: none; + text-transform: uppercase; + white-space: nowrap; +} +.button:focus, +.button:hover, +button:focus, +button:hover, +input[type="button"]:focus, +input[type="button"]:hover, +input[type="reset"]:focus, +input[type="reset"]:hover, +input[type="submit"]:focus, +input[type="submit"]:hover { + background-color: #606c76; + border-color: #606c76; + color: #fff; + outline: 0; +} +.button[disabled], +button[disabled], +input[type="button"][disabled], +input[type="reset"][disabled], +input[type="submit"][disabled] { + cursor: default; + opacity: 0.5; +} +.button[disabled]:focus, +.button[disabled]:hover, +button[disabled]:focus, +button[disabled]:hover, +input[type="button"][disabled]:focus, +input[type="button"][disabled]:hover, +input[type="reset"][disabled]:focus, +input[type="reset"][disabled]:hover, +input[type="submit"][disabled]:focus, +input[type="submit"][disabled]:hover { + background-color: #9b4dca; + border-color: #9b4dca; +} +.button.button-outline, +button.button-outline, +input[type="button"].button-outline, +input[type="reset"].button-outline, +input[type="submit"].button-outline { + background-color: transparent; + color: #9b4dca; +} +.button.button-outline:focus, +.button.button-outline:hover, +button.button-outline:focus, +button.button-outline:hover, +input[type="button"].button-outline:focus, +input[type="button"].button-outline:hover, +input[type="reset"].button-outline:focus, +input[type="reset"].button-outline:hover, +input[type="submit"].button-outline:focus, +input[type="submit"].button-outline:hover { + background-color: transparent; + border-color: #606c76; + color: #606c76; +} +.button.button-outline[disabled]:focus, +.button.button-outline[disabled]:hover, +button.button-outline[disabled]:focus, +button.button-outline[disabled]:hover, +input[type="button"].button-outline[disabled]:focus, +input[type="button"].button-outline[disabled]:hover, +input[type="reset"].button-outline[disabled]:focus, +input[type="reset"].button-outline[disabled]:hover, +input[type="submit"].button-outline[disabled]:focus, +input[type="submit"].button-outline[disabled]:hover { + border-color: inherit; + color: #9b4dca; +} +.button.button-clear, +button.button-clear, +input[type="button"].button-clear, +input[type="reset"].button-clear, +input[type="submit"].button-clear { + background-color: transparent; + border-color: transparent; + color: #9b4dca; +} +.button.button-clear:focus, +.button.button-clear:hover, +button.button-clear:focus, +button.button-clear:hover, +input[type="button"].button-clear:focus, +input[type="button"].button-clear:hover, +input[type="reset"].button-clear:focus, +input[type="reset"].button-clear:hover, +input[type="submit"].button-clear:focus, +input[type="submit"].button-clear:hover { + background-color: transparent; + border-color: transparent; + color: #606c76; +} +.button.button-clear[disabled]:focus, +.button.button-clear[disabled]:hover, +button.button-clear[disabled]:focus, +button.button-clear[disabled]:hover, +input[type="button"].button-clear[disabled]:focus, +input[type="button"].button-clear[disabled]:hover, +input[type="reset"].button-clear[disabled]:focus, +input[type="reset"].button-clear[disabled]:hover, +input[type="submit"].button-clear[disabled]:focus, +input[type="submit"].button-clear[disabled]:hover { + color: #9b4dca; +} +code { + background: #f4f5f6; + border-radius: 0.4rem; + font-size: 86%; + margin: 0 0.2rem; + padding: 0.2rem 0.5rem; + white-space: nowrap; +} +pre { + background: #f4f5f6; + border-left: 0.3rem solid #9b4dca; + overflow-y: hidden; +} +pre > code { + border-radius: 0; + display: block; + padding: 1rem 1.5rem; + white-space: pre; +} +hr { + border: 0; + border-top: 0.1rem solid #f4f5f6; + margin: 3rem 0; +} +input[type="color"], +input[type="date"], +input[type="datetime"], +input[type="datetime-local"], +input[type="email"], +input[type="month"], +input[type="number"], +input[type="password"], +input[type="search"], +input[type="tel"], +input[type="text"], +input[type="url"], +input[type="week"], +input:not([type]), +textarea, +select { + -webkit-appearance: none; + background-color: transparent; + border: 0.1rem solid #d1d1d1; + border-radius: 0.4rem; + box-shadow: none; + box-sizing: inherit; + height: 3.8rem; + padding: 0.6rem 1rem 0.7rem; + width: 100%; +} +input[type="color"]:focus, +input[type="date"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="email"]:focus, +input[type="month"]:focus, +input[type="number"]:focus, +input[type="password"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="text"]:focus, +input[type="url"]:focus, +input[type="week"]:focus, +input:not([type]):focus, +textarea:focus, +select:focus { + border-color: #9b4dca; + outline: 0; +} +select { + background: url('data:image/svg+xml;utf8,') + center right no-repeat; + padding-right: 3rem; +} +select:focus { + background-image: url('data:image/svg+xml;utf8,'); +} +select[multiple] { + background: none; + height: auto; +} +textarea { + min-height: 6.5rem; +} +label, +legend { + display: block; + font-size: 1.6rem; + font-weight: 700; + margin-bottom: 0.5rem; +} +fieldset { + border-width: 0; + padding: 0; +} +input[type="checkbox"], +input[type="radio"] { + display: inline; +} +.label-inline { + display: inline-block; + font-weight: normal; + margin-left: 0.5rem; +} +.container { + margin: 0 auto; + max-width: 112rem; + padding: 0 2rem; + position: relative; + width: 100%; +} +.row { + display: flex; + flex-direction: column; + padding: 0; + width: 100%; +} +.row.row-no-padding { + padding: 0; +} +.row.row-no-padding > .column { + padding: 0; +} +.row.row-wrap { + flex-wrap: wrap; +} +.row.row-top { + align-items: flex-start; +} +.row.row-bottom { + align-items: flex-end; +} +.row.row-center { + align-items: center; +} +.row.row-stretch { + align-items: stretch; +} +.row.row-baseline { + align-items: baseline; +} +.row .column { + display: block; + flex: 1 1 auto; + margin-left: 0; + max-width: 100%; + width: 100%; +} +.row .column.column-offset-10 { + margin-left: 10%; +} +.row .column.column-offset-20 { + margin-left: 20%; +} +.row .column.column-offset-25 { + margin-left: 25%; +} +.row .column.column-offset-33, +.row .column.column-offset-34 { + margin-left: 33.3333%; +} +.row .column.column-offset-40 { + margin-left: 40%; +} +.row .column.column-offset-50 { + margin-left: 50%; +} +.row .column.column-offset-60 { + margin-left: 60%; +} +.row .column.column-offset-66, +.row .column.column-offset-67 { + margin-left: 66.6666%; +} +.row .column.column-offset-75 { + margin-left: 75%; +} +.row .column.column-offset-80 { + margin-left: 80%; +} +.row .column.column-offset-90 { + margin-left: 90%; +} +.row .column.column-10 { + flex: 0 0 10%; + max-width: 10%; +} +.row .column.column-20 { + flex: 0 0 20%; + max-width: 20%; +} +.row .column.column-25 { + flex: 0 0 25%; + max-width: 25%; +} +.row .column.column-33, +.row .column.column-34 { + flex: 0 0 33.3333%; + max-width: 33.3333%; +} +.row .column.column-40 { + flex: 0 0 40%; + max-width: 40%; +} +.row .column.column-50 { + flex: 0 0 50%; + max-width: 50%; +} +.row .column.column-60 { + flex: 0 0 60%; + max-width: 60%; +} +.row .column.column-66, +.row .column.column-67 { + flex: 0 0 66.6666%; + max-width: 66.6666%; +} +.row .column.column-75 { + flex: 0 0 75%; + max-width: 75%; +} +.row .column.column-80 { + flex: 0 0 80%; + max-width: 80%; +} +.row .column.column-90 { + flex: 0 0 90%; + max-width: 90%; +} +.row .column .column-top { + align-self: flex-start; +} +.row .column .column-bottom { + align-self: flex-end; +} +.row .column .column-center { + align-self: center; +} +@media (min-width: 40rem) { + .row { + flex-direction: row; + margin-left: -1rem; + width: calc(100% + 2rem); + } + .row .column { + margin-bottom: inherit; + padding: 0 1rem; + } +} +a { + color: #9b4dca; + text-decoration: none; +} +a:focus, +a:hover { + color: #606c76; +} +dl, +ol, +ul { + list-style: none; + margin-top: 0; + padding-left: 0; +} +dl dl, +dl ol, +dl ul, +ol dl, +ol ol, +ol ul, +ul dl, +ul ol, +ul ul { + font-size: 90%; + margin: 1.5rem 0 1.5rem 3rem; +} +ol { + list-style: decimal inside; +} +ul { + list-style: circle inside; +} +.button, +button, +dd, +dt, +li { + margin-bottom: 1rem; +} +fieldset, +input, +select, +textarea { + margin-bottom: 1.5rem; +} +blockquote, +dl, +figure, +form, +ol, +p, +pre, +table, +ul { + margin-bottom: 2.5rem; +} +table { + border-spacing: 0; + display: block; + overflow-x: auto; + text-align: left; + width: 100%; +} +td, +th { + border-bottom: 0.1rem solid #e1e1e1; + padding: 1.2rem 1.5rem; +} +td:first-child, +th:first-child { + padding-left: 0; +} +td:last-child, +th:last-child { + padding-right: 0; +} +@media (min-width: 40rem) { + table { + display: table; + overflow-x: initial; + } +} +b, +strong { + font-weight: bold; +} +p { + margin-top: 0; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 300; + letter-spacing: -0.1rem; + margin-bottom: 2rem; + margin-top: 0; +} +h1 { + font-size: 4.6rem; + line-height: 1.2; +} +h2 { + font-size: 3.6rem; + line-height: 1.25; +} +h3 { + font-size: 2.8rem; + line-height: 1.3; +} +h4 { + font-size: 2.2rem; + letter-spacing: -0.08rem; + line-height: 1.35; +} +h5 { + font-size: 1.8rem; + letter-spacing: -0.05rem; + line-height: 1.5; +} +h6 { + font-size: 1.6rem; + letter-spacing: 0; + line-height: 1.4; +} +img { + max-width: 100%; +} +.clearfix:after { + clear: both; + content: " "; + display: table; +} +.float-left { + float: left; +} +.float-right { + float: right; +} + +/*# sourceMappingURL=milligram.min.css.map */ From cc0d82403dde63d26f15c7757c52dca435d70020 Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 1 Oct 2024 11:03:58 +0200 Subject: [PATCH 29/56] feat: add URI routes --- public/index.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/public/index.php b/public/index.php index 2d0ed28..5243153 100644 --- a/public/index.php +++ b/public/index.php @@ -21,7 +21,15 @@ case '/': include VIEW_DIR . '/home.php'; break; - + case '/exercises/new': + include VIEW_DIR . '/NewExercise.php'; + break; + case '/exercises/answering': + include VIEW_DIR . '/AnsweringExercises.php'; + break; + case '/exercises': + include VIEW_DIR . '/Exercises.php'; + break; default: http_response_code(404); include VIEW_DIR . '/404.php'; From 18e716674580c4b597176cf0361f315be2fb74f5 Mon Sep 17 00:00:00 2001 From: Nazmi Uksmajli Date: Wed, 2 Oct 2024 10:53:09 +0200 Subject: [PATCH 30/56] Frontend create an exercise --- public/index.php | 7 ++++++- src/layout.php | 2 ++ src/views/create.php | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/views/create.php diff --git a/public/index.php b/public/index.php index 92e27a6..13608a6 100644 --- a/public/index.php +++ b/public/index.php @@ -19,7 +19,12 @@ case '/': include VIEW_DIR . '/home.php'; break; - + case '/exercises/new': + include VIEW_DIR . '/create.php'; + break; + case '/exercises/answering': + include VIEW_DIR . '/Take.php'; + break; default: http_response_code(404); include VIEW_DIR . '/404.php'; diff --git a/src/layout.php b/src/layout.php index 46a4bde..9dcffdb 100644 --- a/src/layout.php +++ b/src/layout.php @@ -4,6 +4,8 @@ <?=$title?> + +
diff --git a/src/views/create.php b/src/views/create.php new file mode 100644 index 0000000..192f163 --- /dev/null +++ b/src/views/create.php @@ -0,0 +1,34 @@ +'; +ob_start(); + +?> + + +
+
+ + + + +
+

New Exercise

+
+
+
+ + + + +
+
+ + + Date: Wed, 2 Oct 2024 11:52:48 +0200 Subject: [PATCH 31/56] feat: add exercise controller route --- public/index.php | 12 ++++++++- src/Controllers/ExercisesController.php | 2 +- src/views/create.php | 34 ++++++++++++------------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/public/index.php b/public/index.php index 13608a6..f626218 100644 --- a/public/index.php +++ b/public/index.php @@ -5,7 +5,9 @@ define('BASE_DIR', dirname(__FILE__) . '/..'); define('SOURCE_DIR', BASE_DIR . '/src'); define('MODEL_DIR', SOURCE_DIR . '/Models'); -define('VIEW_DIR', SOURCE_DIR . '/views'); +define('VIEW_DIR', SOURCE_DIR . '/Views'); +define('CONTROLLER_DIR', SOURCE_DIR . '/Controllers'); + require_once BASE_DIR . '/vendor/autoload.php'; @@ -14,11 +16,19 @@ $request = $_SERVER['REQUEST_URI']; +use App\Controllers\ExercisesController; + switch ($request) { case '': case '/': include VIEW_DIR . '/home.php'; break; + case '/exercises': + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + require CONTROLLER_DIR . '/ExercisesController.php'; + + ExercisesController::create(); + } case '/exercises/new': include VIEW_DIR . '/create.php'; break; diff --git a/src/Controllers/ExercisesController.php b/src/Controllers/ExercisesController.php index 8ca7e0b..9d569dd 100644 --- a/src/Controllers/ExercisesController.php +++ b/src/Controllers/ExercisesController.php @@ -8,7 +8,7 @@ class ExercisesController { public static function create() { - $name = $_POST["exercise_name"]; + $name = $_POST["title"]; Exercise::addExercise($name); diff --git a/src/views/create.php b/src/views/create.php index 192f163..06a4b43 100644 --- a/src/views/create.php +++ b/src/views/create.php @@ -8,23 +8,23 @@ ?> -
-
- - - - -
-

New Exercise

-
-
-
- - - - -
-
+
+
+ + + + +
+

New Exercise

+
+
+
+ + + + +
+
Date: Wed, 2 Oct 2024 11:53:10 +0200 Subject: [PATCH 32/56] chore: add autoload folder --- composer.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/composer.json b/composer.json index 42f4543..15b5010 100644 --- a/composer.json +++ b/composer.json @@ -6,5 +6,10 @@ "require": { "ext-pdo": "*", "vlucas/phpdotenv": "^5.6" + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } } } From 57618043dbe2f2b8bba2c0c1461d8228ba67776d Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Tue, 8 Oct 2024 15:25:49 +0200 Subject: [PATCH 33/56] refactor: add Handler, Router and Controller related classes --- public/index.php | 40 +++++++++++------------ src/Controllers/Controller.php | 23 ++++++++++++++ src/Controllers/ExercisesController.php | 2 +- src/Handler.php | 36 +++++++++++++++++++++ src/Route.php | 42 +++++++++++++++++++++++++ src/Router.php | 38 ++++++++++++++++++++++ src/views/home.php | 4 +-- src/{ => views}/layout.php | 0 8 files changed, 160 insertions(+), 25 deletions(-) create mode 100644 src/Controllers/Controller.php create mode 100644 src/Handler.php create mode 100644 src/Route.php create mode 100644 src/Router.php rename src/{ => views}/layout.php (100%) diff --git a/public/index.php b/public/index.php index f626218..ac72f5f 100644 --- a/public/index.php +++ b/public/index.php @@ -14,28 +14,24 @@ $dotenv = Dotenv\Dotenv::createImmutable(BASE_DIR); $dotenv->load(); -$request = $_SERVER['REQUEST_URI']; - +use App\Route; +use App\Router; +use App\Controllers\Controller; use App\Controllers\ExercisesController; -switch ($request) { - case '': - case '/': - include VIEW_DIR . '/home.php'; - break; - case '/exercises': - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - require CONTROLLER_DIR . '/ExercisesController.php'; - - ExercisesController::create(); - } - case '/exercises/new': - include VIEW_DIR . '/create.php'; - break; - case '/exercises/answering': - include VIEW_DIR . '/Take.php'; - break; - default: - http_response_code(404); - include VIEW_DIR . '/404.php'; +$route = $_SERVER['REQUEST_URI']; +$method = $_SERVER["REQUEST_METHOD"]; + +if (!empty($_SERVER["QUERY_STRING"])) { + $route = substr($route, 0, strlen($_SERVER["REQUEST_URI"]) - strlen($_SERVER["QUERY_STRING"]) - 1); } + +include_once SOURCE_DIR . '/Router.php'; +$router = new Router([$route, $method]); + +$router->addRoute(new Route('GET', '/', [Controller::class, '/home.php'])); +$router->addRoute(new Route('GET', '/exercises/new', [Controller::class, '/create.php'])); +$router->addRoute(new Route('GET', '/exercises/answering', [Controller::class, '/Take.php'])); +$router->addRoute(new Route('POST', '/exercise/new', [ExercisesController::class, 'create'])); + +$router->matchRoute(); diff --git a/src/Controllers/Controller.php b/src/Controllers/Controller.php new file mode 100644 index 0000000..48eb6a9 --- /dev/null +++ b/src/Controllers/Controller.php @@ -0,0 +1,23 @@ +variables = $variables; + } + + public function getVariables(): array + { + return $this->variables; + } + + public static function view(string $path): void + { + require_once VIEW_DIR . $path; + } +} diff --git a/src/Controllers/ExercisesController.php b/src/Controllers/ExercisesController.php index 9d569dd..a38ab28 100644 --- a/src/Controllers/ExercisesController.php +++ b/src/Controllers/ExercisesController.php @@ -4,7 +4,7 @@ use App\Models\Exercise; -class ExercisesController +class ExercisesController extends Controller { public static function create() { diff --git a/src/Handler.php b/src/Handler.php new file mode 100644 index 0000000..fc937a5 --- /dev/null +++ b/src/Handler.php @@ -0,0 +1,36 @@ +controller = $routeData[0]; + $this->method = $routeData[1]; + } + + public function handle() + { + if (class_exists($this->controller)) { + $controllerInstance = new $this->controller; + + if ($this->controller == Controller::class) { + if (file_exists(VIEW_DIR . $this->method)) { + call_user_func([$this->controller, "view"], $this->method); + return; + } + } + + if (method_exists($controllerInstance, $this->method)) { + call_user_func([$controllerInstance, $this->method]); + } + } + } +} diff --git a/src/Route.php b/src/Route.php new file mode 100644 index 0000000..d8476bf --- /dev/null +++ b/src/Route.php @@ -0,0 +1,42 @@ +method = strtoupper($method); + $this->path = rtrim($path, '/'); + $this->handler = $handler; + } + + public function getMethod() + { + return $this->method; + } + + public function getPath() + { + return $this->path; + } + + public function getHandler() + { + return $this->handler; + } + + public function matchesMethod($method) + { + return strtoupper($method) === $this->method; + } + + public function matchesPath($path) + { + return rtrim($path, '/') === $this->path; + } +} diff --git a/src/Router.php b/src/Router.php new file mode 100644 index 0000000..d67efd3 --- /dev/null +++ b/src/Router.php @@ -0,0 +1,38 @@ +routes = []; + $this->routeRequest = $request; + } + + public function addRoute($route) + { + $this->routes[] = $route; + } + + public function matchRoute() + { + $routeRequest = $this->routeRequest; + + foreach ($this->routes as $route) { + if ($route->matchesPath($routeRequest[0])) { + if ($route->matchesMethod($routeRequest[1])) { + $handler = new Handler($route->getHandler()); + $handler->handle(); + return; + } + } + } + throw new Exception('Route not found'); + } +} diff --git a/src/views/home.php b/src/views/home.php index f7b2637..ae77305 100644 --- a/src/views/home.php +++ b/src/views/home.php @@ -5,9 +5,9 @@ ob_start(); ?> -

Exercise Looper

+

Exercise Looper

Date: Tue, 8 Oct 2024 15:30:11 +0200 Subject: [PATCH 34/56] fix: fixed layout require path --- src/views/404.php | 4 ++-- src/views/create.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/404.php b/src/views/404.php index 864882f..563931f 100644 --- a/src/views/404.php +++ b/src/views/404.php @@ -5,9 +5,9 @@ ob_start(); ?> -

404 Error

+

404 Error

Date: Tue, 8 Oct 2024 16:48:52 +0200 Subject: [PATCH 35/56] feat: linked backend to the frontend and improved model classes --- public/index.php | 3 +- src/Controllers/ExercisesController.php | 4 +- src/Models/Database.php | 4 +- src/Models/Exercise.php | 15 ------- src/Models/Exercises.php | 11 +++++ src/Models/Model.php | 56 +++++++++++++++++++++++++ src/views/create.php | 2 +- 7 files changed, 75 insertions(+), 20 deletions(-) delete mode 100644 src/Models/Exercise.php create mode 100644 src/Models/Exercises.php create mode 100644 src/Models/Model.php diff --git a/public/index.php b/public/index.php index ac72f5f..202cb14 100644 --- a/public/index.php +++ b/public/index.php @@ -32,6 +32,7 @@ $router->addRoute(new Route('GET', '/', [Controller::class, '/home.php'])); $router->addRoute(new Route('GET', '/exercises/new', [Controller::class, '/create.php'])); $router->addRoute(new Route('GET', '/exercises/answering', [Controller::class, '/Take.php'])); -$router->addRoute(new Route('POST', '/exercise/new', [ExercisesController::class, 'create'])); +$router->addRoute(new Route('POST', '/exercises/new', [ExercisesController::class, 'create'])); $router->matchRoute(); + diff --git a/src/Controllers/ExercisesController.php b/src/Controllers/ExercisesController.php index a38ab28..5229def 100644 --- a/src/Controllers/ExercisesController.php +++ b/src/Controllers/ExercisesController.php @@ -2,7 +2,7 @@ namespace App\Controllers; -use App\Models\Exercise; +use App\Models\Exercises; class ExercisesController extends Controller { @@ -10,7 +10,7 @@ public static function create() { $name = $_POST["title"]; - Exercise::addExercise($name); + Exercises::addExercise($name); include_once VIEW_DIR . '/home.php'; } diff --git a/src/Models/Database.php b/src/Models/Database.php index 5a636e4..0d99d66 100644 --- a/src/Models/Database.php +++ b/src/Models/Database.php @@ -6,6 +6,8 @@ use Exception; use PDO; +use App\Models\Model; + class Database { private static $instance = null; @@ -33,7 +35,7 @@ private function __construct($hostname, $database, $username, $password) throw new Exception("Could not connect to the database: " . $e->getMessage(), 500); } } - + public static function getInstance($hostname, $database, $username, $password) { if (self::$instance === null) { diff --git a/src/Models/Exercise.php b/src/Models/Exercise.php deleted file mode 100644 index 4c136fd..0000000 --- a/src/Models/Exercise.php +++ /dev/null @@ -1,15 +0,0 @@ -query("INSERT INTO exercises (title, exercise_status) VALUES (:title, :exercise_status)", ["title" => $title, "exercise_status" => $exercise_status]); - } -} diff --git a/src/Models/Exercises.php b/src/Models/Exercises.php new file mode 100644 index 0000000..3fb1a38 --- /dev/null +++ b/src/Models/Exercises.php @@ -0,0 +1,11 @@ + $title, "exercise_status" => $exercise_status]); + } +} diff --git a/src/Models/Model.php b/src/Models/Model.php new file mode 100644 index 0000000..c0a46ef --- /dev/null +++ b/src/Models/Model.php @@ -0,0 +1,56 @@ + $value) { + $this->$name = $value; + } + } + + public static function findAll() + { + $tableName = static::tableName(); + $sql = "SELECT * FROM $tableName"; + return static::getDatabaseInstance()->query($sql); + } + + public static function findBy($column, $value) + { + $tableName = static::tableName(); + $sql = "SELECT * FROM $tableName WHERE $column = :value"; + return static::getDatabaseInstance()->query($sql, [':value' => $value]); + } + + protected static function insert($columnNames, $SQLParameters) + { + $tableName = static::tableName(); + + $sql = "INSERT INTO $tableName (" . join(',', $columnNames) . ") VALUES (" . join(',', array_map(function ($item) { + return ':' . $item; + }, $columnNames)) . ")"; + + return static::getDatabaseInstance()->query($sql, $SQLParameters); + } + + protected static function tableName() + { + $class_name = strtolower(get_called_class()); + preg_match('/(\\\\)?(\\w+?)$/', $class_name, $matches); + return $matches[2]; + } + + protected static function getDatabaseInstance() + { + static::$db = Database::getInstance($_ENV["DATABASE_HOST"], $_ENV["DATABASE_NAME"], $_ENV["DATABASE_USERNAME"], $_ENV["DATABASE_PASSWORD"]); + + return static::$db; + } +} diff --git a/src/views/create.php b/src/views/create.php index 74df5e8..2a35cbf 100644 --- a/src/views/create.php +++ b/src/views/create.php @@ -18,7 +18,7 @@

New Exercise

-
+ From 389b86a877e5fef8a1fd10cc45e8bcdfe97f4e99 Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 8 Oct 2024 19:48:05 +0200 Subject: [PATCH 36/56] refactor: capitalized files for coherent naming convention --- public/index.php | 6 +++--- src/views/create.php | 4 ++-- src/views/home.php | 40 ++++++++++++++++++++-------------------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/public/index.php b/public/index.php index 647dda4..62803e1 100644 --- a/public/index.php +++ b/public/index.php @@ -31,9 +31,9 @@ include_once SOURCE_DIR . '/Router.php'; $router = new Router([$route, $method]); -$router->addRoute(new Route('GET', '/', [Controller::class, '/home.php'])); -$router->addRoute(new Route('GET', '/exercises/new', [Controller::class, '/create.php'])); -$router->addRoute(new Route('GET', '/exercises/answering', [Controller::class, '/Take.php'])); +$router->addRoute(new Route('GET', '/', [Controller::class, '/Home.php'])); +$router->addRoute(new Route('GET', '/exercises/new', [Controller::class, '/Create.php'])); +$router->addRoute(new Route('GET', '/exercises/answering', [Controller::class, '/TakeExercise.php'])); $router->addRoute(new Route('POST', '/exercises/new', [ExercisesController::class, 'create'])); $router->matchRoute(); diff --git a/src/views/create.php b/src/views/create.php index 2a35cbf..679946b 100644 --- a/src/views/create.php +++ b/src/views/create.php @@ -2,7 +2,7 @@ //initialize page variables $title = "ExerciseLooper"; -$style = ''; +$style = ''; ob_start(); ?> @@ -31,4 +31,4 @@ $content = ob_get_clean(); -require VIEW_DIR . "/layout.php"; +require VIEW_DIR . "/Layout.php"; diff --git a/src/views/home.php b/src/views/home.php index dfe8c9d..c3eb6fc 100644 --- a/src/views/home.php +++ b/src/views/home.php @@ -5,28 +5,28 @@ ob_start(); ?> -
-
-

-

Exercise
Looper

-
-
+
+
+

+

Exercise
Looper

+
+
- + Date: Tue, 8 Oct 2024 20:19:18 +0200 Subject: [PATCH 37/56] feat: add frontend to /exercises/answering page --- public/css/Fulfillment.css | 123 +++++++++++++++++++++++++++++++++++++ src/views/TakeExercise.php | 63 +++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 public/css/Fulfillment.css create mode 100644 src/views/TakeExercise.php diff --git a/public/css/Fulfillment.css b/public/css/Fulfillment.css new file mode 100644 index 0000000..245ae68 --- /dev/null +++ b/public/css/Fulfillment.css @@ -0,0 +1,123 @@ +header.answering { + background-color: #9b4dca; +} + +header.heading { + height: 5rem; + color: white; + display: block; + left: 0; + max-width: 100%; + position: fixed; + right: 0; + top: 0; + width: 100%; + z-index: 1; +} + +header.heading section { + display: flex; + height: 5rem; +} + +.container { + margin: 0 auto; + max-width: 112rem; + padding: 0 2rem; + position: relative; + width: 100%; +} + +.container img { + width: 50px; + height: 50px; +} + +main.container { + margin-top: 5rem; +} + +main { + display: block; +} + +.ansering-list { + padding-top: 2rem; +} + +blockquote, +dl, +figure, +form, +ol, +p, +pre, +table, +ul { + margin-bottom: 2.5rem; +} + +ul { + list-style: circle inside; +} + +dl, +ol, +ul { + list-style: none; + margin-top: 0; + padding-left: 0; +} + +@media (min-width: 40rem) { + .row { + flex-direction: row; + margin-left: -1rem; + width: calc(100% + 2rem); + } +} + +.row { + display: flex; + flex-direction: column; + padding: 0; + width: 100%; +} + +.button, +button, +dd, +dt, +li { + margin-bottom: 1rem; +} + +@media (min-width: 40rem) { + .row .column { + margin-bottom: inherit; + padding: 0 1rem; + } +} + +.row .column { + display: block; + flex: 1 1 auto; + margin-left: 0; + max-width: 100%; + width: 100%; +} + +.card { + background-color: #eee; + border-radius: 0.4rem; + text-align: center; + padding: 1rem; +} + +.card .title { + margin: 1rem 0; +} + +.card a.button { + display: block; +} diff --git a/src/views/TakeExercise.php b/src/views/TakeExercise.php new file mode 100644 index 0000000..291f0ec --- /dev/null +++ b/src/views/TakeExercise.php @@ -0,0 +1,63 @@ +'; + +ob_start(); +?> + +
+
+ + +
+
+ +
+ +
+ + + Date: Tue, 8 Oct 2024 20:36:34 +0200 Subject: [PATCH 38/56] feat: fetch and display exercises --- public/index.php | 2 +- src/Controllers/ExercisesController.php | 7 ++++ src/Models/Exercises.php | 5 +++ src/views/TakeExercise.php | 44 +++++-------------------- 4 files changed, 21 insertions(+), 37 deletions(-) diff --git a/public/index.php b/public/index.php index 62803e1..a76d847 100644 --- a/public/index.php +++ b/public/index.php @@ -33,7 +33,7 @@ $router->addRoute(new Route('GET', '/', [Controller::class, '/Home.php'])); $router->addRoute(new Route('GET', '/exercises/new', [Controller::class, '/Create.php'])); -$router->addRoute(new Route('GET', '/exercises/answering', [Controller::class, '/TakeExercise.php'])); +$router->addRoute(new Route('GET', '/exercises/answering', [ExercisesController::class, 'showAnswering'])); $router->addRoute(new Route('POST', '/exercises/new', [ExercisesController::class, 'create'])); $router->matchRoute(); diff --git a/src/Controllers/ExercisesController.php b/src/Controllers/ExercisesController.php index 5229def..ffc1394 100644 --- a/src/Controllers/ExercisesController.php +++ b/src/Controllers/ExercisesController.php @@ -14,4 +14,11 @@ public static function create() include_once VIEW_DIR . '/home.php'; } + + public static function showAnswering() + { + $exercises = Exercises::findAllByStatus("answering"); + + include_once VIEW_DIR . "/TakeExercise.php"; + } } diff --git a/src/Models/Exercises.php b/src/Models/Exercises.php index 3fb1a38..863b44a 100644 --- a/src/Models/Exercises.php +++ b/src/Models/Exercises.php @@ -8,4 +8,9 @@ public static function addExercise($title, $exercise_status = "building") { parent::insert(["title", "exercise_status"], ["title" => $title, "exercise_status" => $exercise_status]); } + + public static function findAllByStatus($status) + { + return parent::findBy("exercise_status", $status); + } } diff --git a/src/views/TakeExercise.php b/src/views/TakeExercise.php index 291f0ec..c204b82 100644 --- a/src/views/TakeExercise.php +++ b/src/views/TakeExercise.php @@ -16,42 +16,14 @@
From 9295056c295035a011c37917509c04f4ff43d96e Mon Sep 17 00:00:00 2001 From: Nazmi Uksmajli Date: Tue, 8 Oct 2024 21:05:26 +0200 Subject: [PATCH 39/56] feat: page exercise Fields --- public/css/Fields.css | 101 ++++++++++++++++++++++++++++++++++++++++++ public/index.php | 20 +++++++++ src/views/Fields.php | 49 ++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 public/css/Fields.css create mode 100644 src/views/Fields.php diff --git a/public/css/Fields.css b/public/css/Fields.css new file mode 100644 index 0000000..c166810 --- /dev/null +++ b/public/css/Fields.css @@ -0,0 +1,101 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + background-color: #f9f9f9; +} + +.header { + background-color: #FFA500; /* Orange background */ + padding: 20px; + display: flex; + justify-content: center; + align-items: center; +} + +.header-content { + display: flex; + align-items: center; +} + +.logo { + width: 40px; + margin-right: 10px; +} + +.header-content span { + font-size: 1.5em; + color: white; +} + +.container { + display: flex; + justify-content: space-between; + padding: 40px; +} + +.left-panel, .right-panel { + width: 45%; +} + +.left-panel h2, .right-panel h2 { + font-size: 2.5em; + color: #555; + margin-bottom: 20px; +} + +table { + width: 100%; + border-collapse: collapse; + margin-bottom: 20px; +} + +th, td { + padding: 10px; + text-align: left; + border-bottom: 1px solid #ddd; +} + +th { + font-weight: bold; +} + +form { + display: flex; + flex-direction: column; +} + +label { + font-size: 1.2em; + margin-bottom: 10px; +} + +input[type="text"], select { + padding: 10px; + font-size: 1em; + margin-bottom: 20px; + border: 1px solid #ddd; + border-radius: 5px; +} + +.btn { + padding: 15px; + font-size: 1em; + color: white; + border: none; + cursor: pointer; + border-radius: 5px; + transition: background-color 0.3s ease; +} + +.purple { + background-color: #8A2BE2; +} + +.purple:hover { + opacity: 0.9; +} \ No newline at end of file diff --git a/public/index.php b/public/index.php index 647dda4..89c8d4a 100644 --- a/public/index.php +++ b/public/index.php @@ -21,11 +21,31 @@ use App\Controllers\Controller; use App\Controllers\ExercisesController; +<<<<<<< Updated upstream $route = $_SERVER['REQUEST_URI']; $method = $_SERVER["REQUEST_METHOD"]; if (!empty($_SERVER["QUERY_STRING"])) { $route = substr($route, 0, strlen($_SERVER["REQUEST_URI"]) - strlen($_SERVER["QUERY_STRING"]) - 1); +======= +switch ($request) { + case '': + case '/': + include VIEW_DIR . '/home.php'; + break; + case '/exercises/new': + include VIEW_DIR . '/create.php'; + break; + case '/exercises/answering': + include VIEW_DIR . '/Take.php'; + break; + default: + http_response_code(404); + include VIEW_DIR . '/404.php'; + case '/exercises/NewField': + include VIEW_DIR . '/Fields.php'; + break; +>>>>>>> Stashed changes } include_once SOURCE_DIR . '/Router.php'; diff --git a/src/views/Fields.php b/src/views/Fields.php new file mode 100644 index 0000000..8035925 --- /dev/null +++ b/src/views/Fields.php @@ -0,0 +1,49 @@ + + + + + + New Exercise + + + +
+
+ + New exercise +
+
+ +
+
+

Fields

+ + + + + + + + + +
LabelValue kind
+ +
+ +
+

New Field

+ + + + + + + + + +
+
+ + From 243fa0d005d74d9dce18157b6ad4f94d59db762c Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 8 Oct 2024 21:19:43 +0200 Subject: [PATCH 40/56] feat: implemented Field backend class --- src/Models/Fields.php | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/Models/Fields.php diff --git a/src/Models/Fields.php b/src/Models/Fields.php new file mode 100644 index 0000000..d222599 --- /dev/null +++ b/src/Models/Fields.php @@ -0,0 +1,11 @@ + $title, "field_types" => $field_types, "exercises_id" => $exercises_id]); + } +} From f424eae8235a24fd6003a938f476efe35f4f1b10 Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 8 Oct 2024 22:22:04 +0200 Subject: [PATCH 41/56] refactor: changed method name to function to be more explicit --- src/Handler.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Handler.php b/src/Handler.php index fc937a5..456bfcd 100644 --- a/src/Handler.php +++ b/src/Handler.php @@ -8,28 +8,28 @@ class Handler { private $controller; - private $method; + private $function; public function __construct($routeData) { $this->controller = $routeData[0]; - $this->method = $routeData[1]; + $this->function = $routeData[1]; } - public function handle() + public function handle($args = []) { if (class_exists($this->controller)) { $controllerInstance = new $this->controller; if ($this->controller == Controller::class) { - if (file_exists(VIEW_DIR . $this->method)) { - call_user_func([$this->controller, "view"], $this->method); + if (file_exists(VIEW_DIR . $this->function)) { + call_user_func([$this->controller, "view"], $this->function); return; } } - if (method_exists($controllerInstance, $this->method)) { - call_user_func([$controllerInstance, $this->method]); + if (method_exists($controllerInstance, $this->function)) { + call_user_func([$controllerInstance, $this->function], $args); } } } From 979f22975c1919538af1227bbf9130f20bdfbef1 Mon Sep 17 00:00:00 2001 From: Arthur Bottemanne Date: Tue, 8 Oct 2024 23:41:43 +0200 Subject: [PATCH 42/56] feat: modified classes to allow for dynamic ids in url --- public/index.php | 21 +-------------- src/Controllers/Controller.php | 18 +++++++++++-- src/Handler.php | 2 +- src/Router.php | 48 ++++++++++++++++++++++++++++++++-- 4 files changed, 64 insertions(+), 25 deletions(-) diff --git a/public/index.php b/public/index.php index 89c8d4a..62dca2e 100644 --- a/public/index.php +++ b/public/index.php @@ -21,31 +21,11 @@ use App\Controllers\Controller; use App\Controllers\ExercisesController; -<<<<<<< Updated upstream $route = $_SERVER['REQUEST_URI']; $method = $_SERVER["REQUEST_METHOD"]; if (!empty($_SERVER["QUERY_STRING"])) { $route = substr($route, 0, strlen($_SERVER["REQUEST_URI"]) - strlen($_SERVER["QUERY_STRING"]) - 1); -======= -switch ($request) { - case '': - case '/': - include VIEW_DIR . '/home.php'; - break; - case '/exercises/new': - include VIEW_DIR . '/create.php'; - break; - case '/exercises/answering': - include VIEW_DIR . '/Take.php'; - break; - default: - http_response_code(404); - include VIEW_DIR . '/404.php'; - case '/exercises/NewField': - include VIEW_DIR . '/Fields.php'; - break; ->>>>>>> Stashed changes } include_once SOURCE_DIR . '/Router.php'; @@ -54,6 +34,7 @@ $router->addRoute(new Route('GET', '/', [Controller::class, '/home.php'])); $router->addRoute(new Route('GET', '/exercises/new', [Controller::class, '/create.php'])); $router->addRoute(new Route('GET', '/exercises/answering', [Controller::class, '/Take.php'])); +$router->addRoute(new Route('GET', '/exercises/{exerciseId}/fields', [Controller::class, '/Fields.php'])); $router->addRoute(new Route('POST', '/exercises/new', [ExercisesController::class, 'create'])); $router->matchRoute(); diff --git a/src/Controllers/Controller.php b/src/Controllers/Controller.php index 48eb6a9..aa97346 100644 --- a/src/Controllers/Controller.php +++ b/src/Controllers/Controller.php @@ -2,6 +2,8 @@ namespace App\Controllers; +use App\Models\Exercises; + class Controller { protected array $variables; @@ -16,8 +18,20 @@ public function getVariables(): array return $this->variables; } - public static function view(string $path): void + public static function view($parameters) { - require_once VIEW_DIR . $path; + $idsArray = $parameters[1]; + + $data = []; + + if ($idsArray != []) { + foreach ($idsArray as $key => $id) { + if (str_contains($key, "exercise")) { + $data["exercise"] = Exercises::findBy("id", $id)[0]; + } + } + } + + require_once VIEW_DIR . $parameters[0]; } } diff --git a/src/Handler.php b/src/Handler.php index 456bfcd..5e0fa74 100644 --- a/src/Handler.php +++ b/src/Handler.php @@ -23,7 +23,7 @@ public function handle($args = []) if ($this->controller == Controller::class) { if (file_exists(VIEW_DIR . $this->function)) { - call_user_func([$this->controller, "view"], $this->function); + call_user_func([$this->controller, "view"], [$this->function, $args]); return; } } diff --git a/src/Router.php b/src/Router.php index d67efd3..b08f5b8 100644 --- a/src/Router.php +++ b/src/Router.php @@ -25,12 +25,56 @@ public function matchRoute() $routeRequest = $this->routeRequest; foreach ($this->routes as $route) { - if ($route->matchesPath($routeRequest[0])) { - if ($route->matchesMethod($routeRequest[1])) { + + if ($route->matchesMethod($routeRequest[1])) { + + if ($route->matchesPath($routeRequest[0])) { + $handler = new Handler($route->getHandler()); + $handler->handle(); + return; } + + $routeArray = explode("/", $route->getPath()); + $routeArray = array_filter($routeArray); + + $requestRouteArray = explode("/", $routeRequest[0]); + $requestRouteArray = array_filter($requestRouteArray); + + if (count($requestRouteArray) == count($routeArray)) { + + $parameters = []; + $pathRequestIsValid = false; + + foreach ($requestRouteArray as $key => $pathSegment) { + + if ($pathSegment != $routeArray[$key]) { + + if (preg_match('/{(.*?)}/', $routeArray[$key], $matches)) { + + $parameters[$matches[1]] = $pathSegment; + $pathRequestIsValid = true; + + } else { + + $pathRequestIsValid = false; + break; + + } + } + } + + if ($pathRequestIsValid) { + + $handler = new Handler($route->getHandler()); + + $handler->handle($parameters); + + return; + } + } } } throw new Exception('Route not found'); From 33544b6484648d5e22cd92317af5dca6eaa70f26 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Wed, 9 Oct 2024 12:07:59 +0200 Subject: [PATCH 43/56] refactor: change file capitalization to be coherent --- src/views/{create.php => Create.php} | 0 src/views/{home.php => Home.php} | 0 src/views/{layout.php => Layout.php} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/views/{create.php => Create.php} (100%) rename src/views/{home.php => Home.php} (100%) rename src/views/{layout.php => Layout.php} (100%) diff --git a/src/views/create.php b/src/views/Create.php similarity index 100% rename from src/views/create.php rename to src/views/Create.php diff --git a/src/views/home.php b/src/views/Home.php similarity index 100% rename from src/views/home.php rename to src/views/Home.php diff --git a/src/views/layout.php b/src/views/Layout.php similarity index 100% rename from src/views/layout.php rename to src/views/Layout.php From 42bdc62a470a202723eb37907dc242333e90373f Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Wed, 9 Oct 2024 13:29:09 +0200 Subject: [PATCH 44/56] fix: updated style --- public/css/Home.css | 53 +++++++++++++++++++++++++++++++++++++++++++++ src/views/Home.php | 1 + 2 files changed, 54 insertions(+) create mode 100644 public/css/Home.css diff --git a/public/css/Home.css b/public/css/Home.css new file mode 100644 index 0000000..375b44d --- /dev/null +++ b/public/css/Home.css @@ -0,0 +1,53 @@ +body { + margin: 0px; +} + +header { + background-color: #9b4dca; +} + +header.dashboard .container { + text-align: center; + font-size: 3rem; + color: white; + padding: 2rem; +} + +p { + margin-top: 0; +} + +blockquote, +dl, +figure, +form, +ol, +p, +pre, +table, +ul { + margin-bottom: 2.5rem; +} + +header.dashboard .container h1 { + letter-spacing: 1.1rem; +} + +h1 { + font-size: 4.6rem; + line-height: 1.2; +} + +div.dashboard { + margin-top: 2rem; +} + +.managing { + border-color: #e0a458; + background-color: #e0a458; +} + +.results { + border-color: #419d78; + background-color: #419d78; +} diff --git a/src/views/Home.php b/src/views/Home.php index c3eb6fc..6bc4e84 100644 --- a/src/views/Home.php +++ b/src/views/Home.php @@ -2,6 +2,7 @@ //initialize page variables $title = "ExerciseLooper"; +$style = ''; ob_start(); ?> From be87c1fe38733b3dc94f4839d329318877de6022 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Thu, 10 Oct 2024 09:25:25 +0200 Subject: [PATCH 45/56] feat: add update method for exercise and model classes --- src/Models/Exercises.php | 5 +++++ src/Models/Model.php | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/Models/Exercises.php b/src/Models/Exercises.php index 3fb1a38..2f7b6e9 100644 --- a/src/Models/Exercises.php +++ b/src/Models/Exercises.php @@ -8,4 +8,9 @@ public static function addExercise($title, $exercise_status = "building") { parent::insert(["title", "exercise_status"], ["title" => $title, "exercise_status" => $exercise_status]); } + + public static function updateStatus($id, $exercise_status) + { + parent::update(["exercise_status"], "id", ["exercise_status" => $exercise_status, "id" => $id]); + } } diff --git a/src/Models/Model.php b/src/Models/Model.php index c0a46ef..77f3064 100644 --- a/src/Models/Model.php +++ b/src/Models/Model.php @@ -40,6 +40,19 @@ protected static function insert($columnNames, $SQLParameters) return static::getDatabaseInstance()->query($sql, $SQLParameters); } + protected static function update($columnNames, $columnCondition, $SQLParameters) + { + $tableName = static::tableName(); + + // $columnName = :$columnName, + + $sql = "UPDATE $tableName SET " . join(',', array_map(function ($value) { + return $value . " = :" . $value; + }, $columnNames)) . " WHERE " . $columnCondition . " = :" . $columnCondition; + + return static::getDatabaseInstance()->query($sql, $SQLParameters); + } + protected static function tableName() { $class_name = strtolower(get_called_class()); From a379af8d097e335722ff8736832f56e4a616fca8 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Thu, 10 Oct 2024 10:25:53 +0200 Subject: [PATCH 46/56] fix: change form action url --- public/index.php | 3 +-- src/views/create.php | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/public/index.php b/public/index.php index a76d847..9a2946c 100644 --- a/public/index.php +++ b/public/index.php @@ -32,9 +32,8 @@ $router = new Router([$route, $method]); $router->addRoute(new Route('GET', '/', [Controller::class, '/Home.php'])); +$router->addRoute(new Route('POST', '/exercises', [ExercisesController::class, 'create'])); $router->addRoute(new Route('GET', '/exercises/new', [Controller::class, '/Create.php'])); $router->addRoute(new Route('GET', '/exercises/answering', [ExercisesController::class, 'showAnswering'])); -$router->addRoute(new Route('POST', '/exercises/new', [ExercisesController::class, 'create'])); $router->matchRoute(); - diff --git a/src/views/create.php b/src/views/create.php index 679946b..19932b4 100644 --- a/src/views/create.php +++ b/src/views/create.php @@ -7,18 +7,17 @@ ?> -
-

New Exercise

+
-
+ @@ -26,7 +25,6 @@
- Date: Thu, 10 Oct 2024 10:26:43 +0200 Subject: [PATCH 47/56] feat: returns row when insert --- src/Controllers/ExercisesController.php | 4 ++-- src/Models/Database.php | 8 ++++++++ src/Models/Exercises.php | 2 +- src/Models/Model.php | 8 +++++++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Controllers/ExercisesController.php b/src/Controllers/ExercisesController.php index ffc1394..7416e20 100644 --- a/src/Controllers/ExercisesController.php +++ b/src/Controllers/ExercisesController.php @@ -10,9 +10,9 @@ public static function create() { $name = $_POST["title"]; - Exercises::addExercise($name); + $exerciseData = Exercises::addExercise($name)[0]; - include_once VIEW_DIR . '/home.php'; + header("Location: /exercises/" . $exerciseData['id'] . "/fields"); } public static function showAnswering() diff --git a/src/Models/Database.php b/src/Models/Database.php index 0d99d66..01a8803 100644 --- a/src/Models/Database.php +++ b/src/Models/Database.php @@ -73,4 +73,12 @@ public function query($sql, $params = []) throw new Exception("Query failed: " . $e->getMessage(), 500); } } + + public function getLastInsertedRow($tableName) + { + $sql = "SELECT * FROM $tableName WHERE id = :id"; + $lastInsertedId = $this->pdo->lastInsertId(); + + return $this->query($sql, ["id" => $lastInsertedId]); + } } diff --git a/src/Models/Exercises.php b/src/Models/Exercises.php index 863b44a..bc83020 100644 --- a/src/Models/Exercises.php +++ b/src/Models/Exercises.php @@ -6,7 +6,7 @@ class Exercises extends Model { public static function addExercise($title, $exercise_status = "building") { - parent::insert(["title", "exercise_status"], ["title" => $title, "exercise_status" => $exercise_status]); + return parent::insert(["title", "exercise_status"], ["title" => $title, "exercise_status" => $exercise_status]); } public static function findAllByStatus($status) diff --git a/src/Models/Model.php b/src/Models/Model.php index c0a46ef..2be3bc7 100644 --- a/src/Models/Model.php +++ b/src/Models/Model.php @@ -37,7 +37,13 @@ protected static function insert($columnNames, $SQLParameters) return ':' . $item; }, $columnNames)) . ")"; - return static::getDatabaseInstance()->query($sql, $SQLParameters); + static::getDatabaseInstance()->query($sql, $SQLParameters); + + $results = static::getDatabaseInstance()->getLastInsertedRow($tableName); + + error_log(print_r($results, true)); + + return $results; } protected static function tableName() From d1c82c6be762c5f407d33bcd49cdaac6c274ed88 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Thu, 10 Oct 2024 11:09:23 +0200 Subject: [PATCH 48/56] fix: modified frontend to resemble looper website --- public/css/Fields.css | 259 ++++++++++++++++++++++++++--------- public/css/milligram.min.css | 1 + src/views/Fields.php | 89 +++++++----- 3 files changed, 253 insertions(+), 96 deletions(-) diff --git a/public/css/Fields.css b/public/css/Fields.css index c166810..34fdb34 100644 --- a/public/css/Fields.css +++ b/public/css/Fields.css @@ -1,101 +1,238 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; +header.managing { + background-color: #e0a458; } -body { - font-family: Arial, sans-serif; - background-color: #f9f9f9; +header.heading { + height: 5rem; + color: white; + display: block; + left: 0; + max-width: 100%; + position: fixed; + right: 0; + top: 0; + width: 100%; + z-index: 1; } -.header { - background-color: #FFA500; /* Orange background */ - padding: 20px; +header.heading section { display: flex; - justify-content: center; - align-items: center; + height: 5rem; } -.header-content { - display: flex; - align-items: center; +.container { + margin: 0 auto; + max-width: 112rem; + padding: 0 2rem; + position: relative; + width: 100%; +} + +a { + color: #9b4dca; + text-decoration: none; +} + +header.heading img { + height: 100%; } -.logo { - width: 40px; - margin-right: 10px; +img { + max-width: 100%; + border-style: none; } -.header-content span { - font-size: 1.5em; +header.heading .exercise-label { + margin: auto; + margin-left: 2rem; +} + +header.heading .exercise-label a { color: white; } +header.heading .exercise-title, +header.heading .exercise-label a { + font-weight: bold; +} + +main.container { + margin-top: 5rem; +} + .container { - display: flex; - justify-content: space-between; - padding: 40px; + margin: 0 auto; + max-width: 112rem; + padding: 0 2rem; + position: relative; + width: 100%; +} + +main { + display: block; +} + +.row .column { + display: block; + flex: 1 1 auto; + margin-left: 0; + max-width: 100%; + width: 100%; } -.left-panel, .right-panel { - width: 45%; +h1 { + font-size: 4.6rem; + line-height: 1.2; } -.left-panel h2, .right-panel h2 { - font-size: 2.5em; - color: #555; - margin-bottom: 20px; +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 300; + letter-spacing: -0.1rem; + margin-bottom: 2rem; + margin-top: 0; } table { + border-spacing: 0; width: 100%; - border-collapse: collapse; - margin-bottom: 20px; } -th, td { - padding: 10px; - text-align: left; - border-bottom: 1px solid #ddd; +blockquote, +dl, +figure, +form, +ol, +p, +pre, +table, +ul { + margin-bottom: 2.5rem; } +table { + display: table; + border-collapse: separate; + box-sizing: border-box; + text-indent: initial; + unicode-bidi: isolate; + line-height: normal; + font-weight: normal; + font-size: medium; + font-style: normal; + color: -internal-quirk-inherit; + text-align: start; + border-spacing: 2px; + border-color: gray; + white-space: normal; + font-variant: normal; +} + +td:first-child, +th:first-child { + padding-left: 0; +} + +td, th { - font-weight: bold; + border-bottom: 0.1rem solid #e1e1e1; + padding: 1.2rem 1.5rem; + text-align: left; } -form { - display: flex; - flex-direction: column; +.button, +button, +dd, +dt, +li { + margin-bottom: 1rem; } -label { - font-size: 1.2em; - margin-bottom: 10px; +.button, +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + background-color: #9b4dca; + border: 0.1rem solid #9b4dca; + border-radius: 0.4rem; + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 1.1rem; + font-weight: 700; + height: 3.8rem; + letter-spacing: 0.1rem; + line-height: 3.8rem; + padding: 0 3rem; + text-align: center; + text-decoration: none; + text-transform: uppercase; + white-space: nowrap; +} + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; } -input[type="text"], select { - padding: 10px; - font-size: 1em; - margin-bottom: 20px; - border: 1px solid #ddd; - border-radius: 5px; +fieldset, +input, +select, +textarea { + margin-bottom: 1.5rem; } -.btn { - padding: 15px; - font-size: 1em; - color: white; - border: none; - cursor: pointer; - border-radius: 5px; - transition: background-color 0.3s ease; +div.field, +div.actions { + margin-bottom: 10px; } -.purple { - background-color: #8A2BE2; +label { + display: block; +} + +label, +legend { + display: block; + font-size: 1.6rem; + font-weight: 700; + margin-bottom: 0.5rem; +} + +input[type="email"], +input[type="number"], +input[type="password"], +input[type="search"], +input[type="tel"], +input[type="text"], +input[type="url"], +textarea, +select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: transparent; + border: 0.1rem solid #d1d1d1; + border-radius: 0.4rem; + box-shadow: none; + box-sizing: inherit; + height: 3.8rem; + padding: 0.6rem 1rem; + width: 100%; } -.purple:hover { - opacity: 0.9; -} \ No newline at end of file +div.field, +div.actions { + margin-bottom: 10px; +} diff --git a/public/css/milligram.min.css b/public/css/milligram.min.css index d82d53f..c151554 100644 --- a/public/css/milligram.min.css +++ b/public/css/milligram.min.css @@ -14,6 +14,7 @@ body { font-weight: 300; letter-spacing: 0.01em; line-height: 1.6; + margin: 0px; } blockquote { border-left: 0.3rem solid #d1d1d1; diff --git a/src/views/Fields.php b/src/views/Fields.php index 8035925..d1a24e1 100644 --- a/src/views/Fields.php +++ b/src/views/Fields.php @@ -1,49 +1,68 @@ - - - - - - New Exercise - - - -
-
- - New exercise -
-
- -
-
-

Fields

- +'; + +ob_start(); +?> + + +
+
+ + Exercise: teetatea +
+
+ +
+
+
+

Fields

+
+ +
Label Value kind
- -
-
-

New Field

-
- - + Complete and be ready for answers - - + +
+

New Field

+ - +
+ + +
+ +
+ + +
+ +
+ +
-
+
- - +
+ + Date: Thu, 10 Oct 2024 11:45:15 +0200 Subject: [PATCH 49/56] feat: added fontawesome to layout --- src/views/layout.php | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/views/layout.php b/src/views/layout.php index 9dcffdb..728d946 100644 --- a/src/views/layout.php +++ b/src/views/layout.php @@ -1,15 +1,21 @@ - - - - <?=$title?> - - - - -
- -
- - + + + + + <?= $title ?> + + + + + + +
+ +
+ + + \ No newline at end of file From 18c75f98f3a993b256608351594d12703e36bdb0 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Thu, 10 Oct 2024 14:47:01 +0200 Subject: [PATCH 50/56] feat: add dynamic data to page --- public/index.php | 2 +- src/Controllers/ExercisesController.php | 5 +++++ src/views/Fields.php | 9 +++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/public/index.php b/public/index.php index 62dca2e..77944ca 100644 --- a/public/index.php +++ b/public/index.php @@ -36,6 +36,6 @@ $router->addRoute(new Route('GET', '/exercises/answering', [Controller::class, '/Take.php'])); $router->addRoute(new Route('GET', '/exercises/{exerciseId}/fields', [Controller::class, '/Fields.php'])); $router->addRoute(new Route('POST', '/exercises/new', [ExercisesController::class, 'create'])); +$router->addRoute(new Route('POST', '/exercises/{exerciseId}/status', [ExercisesController::class, 'updateStatus'])); $router->matchRoute(); - diff --git a/src/Controllers/ExercisesController.php b/src/Controllers/ExercisesController.php index 5229def..22d2dc5 100644 --- a/src/Controllers/ExercisesController.php +++ b/src/Controllers/ExercisesController.php @@ -14,4 +14,9 @@ public static function create() include_once VIEW_DIR . '/home.php'; } + + public static function updateStatus($parameters) + { + error_log(print_r($parameters, true)); + } } diff --git a/src/views/Fields.php b/src/views/Fields.php index d1a24e1..177828a 100644 --- a/src/views/Fields.php +++ b/src/views/Fields.php @@ -4,6 +4,8 @@ $title = "ExerciseLooper"; $style = ''; +$exerciseData = $data["exercise"]; + ob_start(); ?> @@ -11,7 +13,7 @@
- Exercise: teetatea + Exercise: /fields">
@@ -32,13 +34,12 @@ - Complete and be ready for answers + /status=answering"> Complete and be ready for answers

New Field

-
- + /fields" accept-charset="UTF-8" method="post">
From 1d4c75af912dee324871f477683531fda363b8d4 Mon Sep 17 00:00:00 2001 From: Arthur-Bottemanne Date: Fri, 11 Oct 2024 11:11:12 +0200 Subject: [PATCH 51/56] fix: merge errors --- public/index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/index.php b/public/index.php index 77944ca..c51ef3b 100644 --- a/public/index.php +++ b/public/index.php @@ -33,9 +33,9 @@ $router->addRoute(new Route('GET', '/', [Controller::class, '/home.php'])); $router->addRoute(new Route('GET', '/exercises/new', [Controller::class, '/create.php'])); -$router->addRoute(new Route('GET', '/exercises/answering', [Controller::class, '/Take.php'])); +$router->addRoute(new Route('GET', '/exercises/answering', [ExercisesController::class, 'ShowAnswering'])); $router->addRoute(new Route('GET', '/exercises/{exerciseId}/fields', [Controller::class, '/Fields.php'])); -$router->addRoute(new Route('POST', '/exercises/new', [ExercisesController::class, 'create'])); +$router->addRoute(new Route('POST', '/exercises', [ExercisesController::class, 'create'])); $router->addRoute(new Route('POST', '/exercises/{exerciseId}/status', [ExercisesController::class, 'updateStatus'])); $router->matchRoute(); From 138925e0fd77742dcb27ff163652e51de2bdbf3b Mon Sep 17 00:00:00 2001 From: Nazmi Uksmajli Date: Thu, 21 Nov 2024 09:23:15 +0100 Subject: [PATCH 52/56] creation of the new_exercise page and debug --- .DS_Store | Bin 0 -> 8196 bytes ExerciseLooper-Features.md | 24 ++++++ exercise-looper-specs.md | 81 ++++++++++++++++++ ...fae1e6cba523d7cd28e0cd49a04707ccbef56e.png | Bin 0 -> 3019 bytes public/css/create_style.css | 6 +- public/css/new_exercise.css | 63 ++++++++++++++ src/views/new_exercise_fields.php | 47 ++++++++++ 7 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 .DS_Store create mode 100644 ExerciseLooper-Features.md create mode 100644 exercise-looper-specs.md create mode 100644 logo-84d7d70645fbe179ce04c983a5fae1e6cba523d7cd28e0cd49a04707ccbef56e.png create mode 100644 public/css/new_exercise.css create mode 100644 src/views/new_exercise_fields.php diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d1fa6621758414b34ee7d55ce61f3278ccbd2c3f GIT binary patch literal 8196 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 + + + + + New Field + + + +
+
+

Exercise: de

+
+
+

Fields

+ +
+
+

New Field

+ + + + + + + + + + "; + echo "Label: " . htmlspecialchars($label) . "
"; + echo "Value Kind: " . htmlspecialchars($valueKind); +} +?> + +
+
+ + From 4bf9ef2539ad1bd2cba4db24dec74303003c9993 Mon Sep 17 00:00:00 2001 From: Nazmi Uksmajli Date: Wed, 18 Dec 2024 12:06:45 +0100 Subject: [PATCH 53/56] fix code --- public/index.php | 11 ++- src/Controllers/Controller.php | 27 ++++--- src/Controllers/ExercisesController.php | 71 ++++++++++++++++--- src/Models/ExerciseHelper.php | 47 +++++++++++++ src/Models/Exercises.php | 93 +++++++++++++++++++++++-- src/{ => Router}/Handler.php | 0 src/{ => Router}/Route.php | 0 src/{ => Router}/Router.php | 9 ++- 8 files changed, 225 insertions(+), 33 deletions(-) create mode 100644 src/Models/ExerciseHelper.php rename src/{ => Router}/Handler.php (100%) rename src/{ => Router}/Route.php (100%) rename src/{ => Router}/Router.php (86%) diff --git a/public/index.php b/public/index.php index c51ef3b..1484601 100644 --- a/public/index.php +++ b/public/index.php @@ -10,7 +10,6 @@ define('VIEW_DIR', SOURCE_DIR . '/Views'); define('CONTROLLER_DIR', SOURCE_DIR . '/Controllers'); - require_once BASE_DIR . '/vendor/autoload.php'; $dotenv = Dotenv\Dotenv::createImmutable(BASE_DIR); @@ -31,11 +30,17 @@ include_once SOURCE_DIR . '/Router.php'; $router = new Router([$route, $method]); +// Ajout des routes $router->addRoute(new Route('GET', '/', [Controller::class, '/home.php'])); +$router->addRoute(new Route('GET', '/exercises', [ExercisesController::class, 'index'])); // Ajout de cette ligne $router->addRoute(new Route('GET', '/exercises/new', [Controller::class, '/create.php'])); -$router->addRoute(new Route('GET', '/exercises/answering', [ExercisesController::class, 'ShowAnswering'])); -$router->addRoute(new Route('GET', '/exercises/{exerciseId}/fields', [Controller::class, '/Fields.php'])); +$router->addRoute(new Route('GET', '/exercises/answering', [ExercisesController::class, 'showAnswering'])); +$router->addRoute(new Route('GET', '/exercises/{exerciseId}/fields', [Controller::class, '/fields.php'])); $router->addRoute(new Route('POST', '/exercises', [ExercisesController::class, 'create'])); $router->addRoute(new Route('POST', '/exercises/{exerciseId}/status', [ExercisesController::class, 'updateStatus'])); $router->matchRoute(); + +ini_set('display_errors', 1); +ini_set('display_startup_errors', 1); +error_reporting(E_ALL); diff --git a/src/Controllers/Controller.php b/src/Controllers/Controller.php index aa97346..bacb2b2 100644 --- a/src/Controllers/Controller.php +++ b/src/Controllers/Controller.php @@ -18,20 +18,25 @@ public function getVariables(): array return $this->variables; } - public static function view($parameters) + // Fusion des deux méthodes view en une seule méthode polymorphe + public static function view($viewPath, $params = []) { - $idsArray = $parameters[1]; - - $data = []; - - if ($idsArray != []) { - foreach ($idsArray as $key => $id) { - if (str_contains($key, "exercise")) { - $data["exercise"] = Exercises::findBy("id", $id)[0]; + if (is_array($viewPath)) { + $idsArray = $viewPath[1]; + $data = []; + + if ($idsArray != []) { + foreach ($idsArray as $key => $id) { + if (str_contains($key, "exercise")) { + $data["exercise"] = Exercises::findBy("id", $id)[0]; + } } } - } - require_once VIEW_DIR . $parameters[0]; + require_once VIEW_DIR . $viewPath[0]; + } else { + extract($params); + include(VIEW_DIR . '/' . $viewPath . '.php'); + } } } diff --git a/src/Controllers/ExercisesController.php b/src/Controllers/ExercisesController.php index 13ab40c..e7eed67 100644 --- a/src/Controllers/ExercisesController.php +++ b/src/Controllers/ExercisesController.php @@ -2,28 +2,79 @@ namespace App\Controllers; -use App\Models\Exercises; +use App\Models\Exercise; +use App\Models\ExerciseHelper; +use App\Router\Router; + class ExercisesController extends Controller { - public static function create() + protected ExerciseHelper $exerciseHelper; + + public function __construct() + { + parent::__construct(); + $this->router = new Router(); + $this->exerciseHelper = new ExerciseHelper(); + } + public function index(): void + { + $exercises = $this->exerciseHelper->get(); + var_dump($exercises); // Affiche les exercices récupérés pour vérifier + $this->view('exercises/index', [ + 'exercises' => $exercises, + 'router' => $this->router, + ]); + } + + public function create(): void { - $name = $_POST["title"]; + $params['router'] = $this->router; + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $exercise = new Exercise(['title' => $_POST['title']]); + if ($exerciseId = $this->exerciseHelper->save($exercise)) { + $this->router->redirect('fields_index', ['exercise' => $exerciseId]); + } + $params["error"] = "Le titre est déjà utilisé. Veuillez en choisir un autre."; + } + $this->view('exercises/new', $params); + } + + public function state(int $exerciseId, string $query): void + { + parse_str($query, $params); + + $exercise = $this->exerciseHelper->get($exerciseId); + $exercise->setState($params['state']); - $exerciseData = Exercises::addExercise($name)[0]; + $this->exerciseHelper->save($exercise); - header("Location: /exercises/" . $exerciseData['id'] . "/fields"); + $this->router->redirect('exercises_index'); + } + + public function answering(): void + { + $this->view('exercises/answering', [ + 'exercises' => $this->exerciseHelper->get(), + 'router' => $this->router, + ]); } - public static function showAnswering() + public function results(int $exerciseId): void { - $exercises = Exercises::findAllByStatus("answering"); + $exercise = $this->exerciseHelper->get($exerciseId); - include_once VIEW_DIR . "/TakeExercise.php"; + $this->view('exercises/results', [ + 'exercise' => $exercise, + 'fields' => $exercise->getFields(), + 'fulfillments' => $exercise->getFulfillments(), + 'router' => $this->router, + ]); } - public static function updateStatus($parameters) + public function delete(int $exerciseId): void { - error_log(print_r($parameters, true)); + $this->exerciseHelper->delete($exerciseId); + $this->router->redirect('exercises_index'); } } diff --git a/src/Models/ExerciseHelper.php b/src/Models/ExerciseHelper.php new file mode 100644 index 0000000..0b74e38 --- /dev/null +++ b/src/Models/ExerciseHelper.php @@ -0,0 +1,47 @@ +id)) { + $columnNames = ['title', 'state']; // Ajoute d'autres colonnes si nécessaire + $parameters = [ + 'title' => $exercise->title, + 'state' => $exercise->state, + 'id' => $exercise->id + ]; + return self::update($columnNames, 'id', $parameters); + } else { + $columnNames = ['title', 'state']; // Ajoute d'autres colonnes si nécessaire + $parameters = [ + 'title' => $exercise->title, + 'state' => $exercise->state + ]; + return self::insert($columnNames, $parameters); + } + } + + public function delete($id) + { + $tableName = self::tableName(); + $sql = "DELETE FROM $tableName WHERE id = :id"; + return self::getDatabaseInstance()->query($sql, [':id' => $id]); + } +} diff --git a/src/Models/Exercises.php b/src/Models/Exercises.php index c120710..a82bac9 100644 --- a/src/Models/Exercises.php +++ b/src/Models/Exercises.php @@ -2,20 +2,99 @@ namespace App\Models; -class Exercises extends Model +use App\Database\Query; +use PDOException; + +class Exercise extends Model { - public static function addExercise($title, $exercise_status = "building") + protected int $id; + protected string $title; + protected string $state = 'building'; + protected Query $query; + + public function __construct(array $params = []) + { + $this->query = new Query(); + if (array_key_exists('title', $params)) { + $this->title = $params['title']; + } + parent::__construct($params); // Appel au constructeur parent après initialisation + } + + public function getId(): ?int + { + return $this->id ?? null; + } + + public function getTitle(): string + { + return $this->title; + } + + public function setTitle(string $title): void + { + $this->title = $title; + } + + public function getState(): string + { + return $this->state; + } + + public function setState(string $state): void + { + $this->state = $state; + } + + public function getFields(int $fieldId = null): array|Field + { + if (is_null($fieldId)) { + return $this->query->select('fields', Field::class, 'exercises_id = :id', [':id' => $this->id]); + } else { + return $this->query->select( + 'fields', + Field::class, + 'id = :field_id AND exercises_id = :exercises_id', + ['field_id' => $fieldId, 'exercises_id' => $this->id], + true + ); + } + } + + public function createField(Field $field): int { - return parent::insert(["title", "exercise_status"], ["title" => $title, "exercise_status" => $exercise_status]); + try { + return $this->query->insert('fields', Field::class, [ + 'label' => $field->getLabel(), + 'value_kind' => $field->getValueKind(), + 'exercises_id' => $this->id, + ]); + } catch (PDOException $e) { + error_log($e); + return false; + } } - public static function findAllByStatus($status) + public function deleteField(int $fieldId): void { - return parent::findBy("exercise_status", $status); + foreach ($this->getFulfillments() as $fulfillment) { + $fulfillment->delete(); + } + $this->query->delete('fields', Field::class, 'id = :id', ['id' => $fieldId]); } - public static function updateStatus($id, $exercise_status) + public function getFulfillments(int $fulfillment = null): array|Fulfillment { - parent::update(["exercise_status"], "id", ["exercise_status" => $exercise_status, "id" => $id]); + if (is_null($fulfillment)) { + return $this->query->select('fulfillments', Fulfillment::class, 'exercises_id = :id', [':id' => $this->id]); + } else { + return $this->query->select( + 'fulfillments', + Fulfillment::class, + 'id = :field_id AND exercises_id = :exercises_id', + ['field_id' => $fulfillment, 'exercises_id' => $this->id], + true + ); + } } } diff --git a/src/Handler.php b/src/Router/Handler.php similarity index 100% rename from src/Handler.php rename to src/Router/Handler.php diff --git a/src/Route.php b/src/Router/Route.php similarity index 100% rename from src/Route.php rename to src/Router/Route.php diff --git a/src/Router.php b/src/Router/Router.php similarity index 86% rename from src/Router.php rename to src/Router/Router.php index b08f5b8..aa389a9 100644 --- a/src/Router.php +++ b/src/Router/Router.php @@ -3,8 +3,7 @@ namespace App; use Exception; - -class Router + class Router { private $routes; private $routeRequest; @@ -24,8 +23,14 @@ public function matchRoute() { $routeRequest = $this->routeRequest; + // Debugging: Affiche la route demandée et la méthode + echo 'Requested route: ' . $routeRequest[0] . ' with method: ' . $routeRequest[1] . PHP_EOL; + foreach ($this->routes as $route) { + // Debugging: Affiche chaque route vérifiée + echo 'Checking route: ' . $route->getPath() . PHP_EOL; + if ($route->matchesMethod($routeRequest[1])) { if ($route->matchesPath($routeRequest[0])) { From b0693d45dbdc6de4b846060158339bf4c7517f0a Mon Sep 17 00:00:00 2001 From: Nazmi Uksmajli Date: Fri, 20 Dec 2024 23:51:09 +0100 Subject: [PATCH 54/56] last version --- .DS_Store | Bin 8196 -> 6148 bytes Dockerfile | 20 + ExerciseLooper-Features.md | 24 - README.md | 108 +- compose.yml | 19 + composer.json | 35 +- composer.lock | 613 +-------- doc/MCD.loo | Bin 61968 -> 0 bytes doc/MLD.md | 42 - exercise-looper-specs.md | 81 -- mcd.md | 29 + modelisation/ClassUML.drawio | 1 + modelisation/ClassUML.png | Bin 0 -> 83504 bytes modelisation/MCD.drawio | 1 + modelisation/MCD.png | Bin 0 -> 21461 bytes modelisation/MLD.mwb | Bin 0 -> 10496 bytes modelisation/MLD.png | Bin 0 -> 20531 bytes modelisation/Nazmi-php.code-workspace | 11 + modelisation/createDatabase.sql | 57 + modelisation/sequenceExerciseFields.puml | 50 + public/assets/fa-solid-900.woff2 | Bin 0 -> 75440 bytes .../assets/logo.png | Bin public/const.php | 6 + public/const.php.exemple | 6 + public/css/Fields.css | 476 +++---- public/css/Fulfillment.css | 246 ++-- public/css/Home.css | 106 +- public/css/create_style.css | 154 ++- public/css/milligram.min.css | 1096 ++++++++--------- public/css/new_exercise.css | 63 - public/index.php | 93 +- src/Controllers/Controller.php | 74 +- src/Controllers/ExerciseController.php | 103 ++ src/Controllers/ExercisesController.php | 80 -- src/Controllers/FieldsController.php | 41 + src/Controllers/FulfillmentController.php | 46 + src/Controllers/HomeController.php | 15 + src/Database/DBConnection.php | 66 + src/Database/Query.php | 57 + src/Models/Database.php | 84 -- src/Models/{Exercises.php => Exercise.php} | 211 ++-- src/Models/ExerciseHelper.php | 137 ++- src/Models/Field.php | 84 ++ src/Models/Fields.php | 11 - src/Models/FieldsHasFulfillments.php | 15 + src/Models/Fulfillment.php | 116 ++ src/Models/Model.php | 75 -- src/Router/Handler.php | 36 - src/Router/Route.php | 120 +- src/Router/Router.php | 193 +-- src/views/404.php | 13 - src/views/Create.php | 32 - src/views/Fields.php | 69 -- src/views/Home.php | 33 - src/views/Layout.php | 21 - src/views/TakeExercise.php | 35 - src/views/new_exercise_fields.php | 47 - templates/exercises/answering.php | 23 + templates/exercises/index.php | 125 ++ templates/exercises/new.php | 17 + templates/exercises/results.php | 60 + templates/fields/edit.php | 57 + templates/fields/index.php | 81 ++ templates/fields/results.php | 36 + templates/fulfillments/edit.php | 41 + templates/fulfillments/new.php | 35 + templates/fulfillments/results.php | 18 + templates/layout.php | 42 + templates/layoutold.php | 42 + templates/site/index.php | 17 + tests/DatabaseTest.php | 22 - 71 files changed, 2935 insertions(+), 2832 deletions(-) create mode 100644 Dockerfile delete mode 100644 ExerciseLooper-Features.md create mode 100644 compose.yml delete mode 100644 doc/MCD.loo delete mode 100644 doc/MLD.md delete mode 100644 exercise-looper-specs.md create mode 100644 mcd.md create mode 100644 modelisation/ClassUML.drawio create mode 100644 modelisation/ClassUML.png create mode 100644 modelisation/MCD.drawio create mode 100644 modelisation/MCD.png create mode 100644 modelisation/MLD.mwb create mode 100644 modelisation/MLD.png create mode 100644 modelisation/Nazmi-php.code-workspace create mode 100644 modelisation/createDatabase.sql create mode 100644 modelisation/sequenceExerciseFields.puml create mode 100644 public/assets/fa-solid-900.woff2 rename logo-84d7d70645fbe179ce04c983a5fae1e6cba523d7cd28e0cd49a04707ccbef56e.png => public/assets/logo.png (100%) create mode 100644 public/const.php create mode 100644 public/const.php.exemple delete mode 100644 public/css/new_exercise.css create mode 100644 src/Controllers/ExerciseController.php delete mode 100644 src/Controllers/ExercisesController.php create mode 100644 src/Controllers/FieldsController.php create mode 100644 src/Controllers/FulfillmentController.php create mode 100644 src/Controllers/HomeController.php create mode 100644 src/Database/DBConnection.php create mode 100644 src/Database/Query.php delete mode 100644 src/Models/Database.php rename src/Models/{Exercises.php => Exercise.php} (94%) create mode 100644 src/Models/Field.php delete mode 100644 src/Models/Fields.php create mode 100644 src/Models/FieldsHasFulfillments.php create mode 100644 src/Models/Fulfillment.php delete mode 100644 src/Models/Model.php delete mode 100644 src/Router/Handler.php delete mode 100644 src/views/404.php delete mode 100644 src/views/Create.php delete mode 100644 src/views/Fields.php delete mode 100644 src/views/Home.php delete mode 100644 src/views/Layout.php delete mode 100644 src/views/TakeExercise.php delete mode 100644 src/views/new_exercise_fields.php create mode 100644 templates/exercises/answering.php create mode 100644 templates/exercises/index.php create mode 100644 templates/exercises/new.php create mode 100644 templates/exercises/results.php create mode 100644 templates/fields/edit.php create mode 100644 templates/fields/index.php create mode 100644 templates/fields/results.php create mode 100644 templates/fulfillments/edit.php create mode 100644 templates/fulfillments/new.php create mode 100644 templates/fulfillments/results.php create mode 100644 templates/layout.php create mode 100644 templates/layoutold.php create mode 100644 templates/site/index.php delete mode 100644 tests/DatabaseTest.php diff --git a/.DS_Store b/.DS_Store index d1fa6621758414b34ee7d55ce61f3278ccbd2c3f..0c4e8e00223b1d926541d341af9c0617f6d76be4 100644 GIT binary patch literal 6148 zcmeHKJx{|h5PeRosvlJ9$b|9>D)A3O)ea0SFe5?JAQjREwRB)X`9rL1`~@cd54^K2 za7ijQgy2r{J;%A{_})cv48V*wvqNA2pv@*2b?9nL?n|p!OG-I4x$DV#d`iFssIzgCKT~F$H5Y&O2u=m$)O=W@MHD zuSM-Pm|9j^^Ni;pwu4hlaKubbh&xsl^#ewOD_Nmt-sn7t>Ts=@tzvH1onc*FN%R;n zY#G(50;<5jRDgT7Si9#?y(*vzr~)4f$oC;;6HGjo4(+Fd&K?1XO%7XQUw#&a6MIZN zmJWG`W*n91sKy^LjH7cr_Hl{F(xIcn_``?sPd5I9V(im7e{92H5{K$l0aajIfrh&r z$o@aseg5Aj>6I#=3j8SrOmloT9x)}qw^ovqy*6gMU=x$L(%}b%9lI4XR<`1OHftP@ Xr9(_SmJT^W(;opVgF02fgM7=FF(Bh`f10kjDTDH7MMblJDKbY(-D1eX=T0nkX&Fv7C9YT6a4s?;lR zLfkm;7dY}y_%EEs^V%M2((X7kfi2mOW53U9_p$5rb%{vSyUkUiB_eW=85ibJOi0|% zc`QwtmYYBUK9Qu1B5G5G-i=w?2CINoz$#!BunJfOu7d)2XLE8cdGG69TUrII0{^7~ zd_K6yj5UKpjq2z?p+W%QG@4aH9dUr-xCUzmhZ+?XK6UgU3{{vALufksT{Z{S3=TDF zItfiDVMZ1vLJ?|o@LXk1qNdT7RspNPxB?uz-=GkY`zf_@@q31+^dOO8kjMa5{KeWu zuAHlc+GJ(|UPvbe=Jx`#L`}BKTZy}qt%6?GGPC#*w30aMRI5M5RB3uQ6_uDV{>8X@Sa8dVzXK~a!TDbL4rX4>{qqZW%%?KthpT}ua_Uf{mHZ#R- z=nkjsl#dn``+d*bSavtP!Lr-m@v6)2`r77TP_gacn}Q_Oa61GXZ zD5H{mRK^)>D&9~X%JPe&PL2HHoWU~1VoPclZ26oRwYBem2x5~};0hEdX`f3R|L/documents/Github/´folder) + + + + + + + +create database dans modelisation +composer installe dsns la racine du proijet +ce qui me reste a faire ce qui foncrionne +php -S localhost:4444 -t public + diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..67826c0 --- /dev/null +++ b/compose.yml @@ -0,0 +1,19 @@ +version: "3.9" + +services: + mariadb: + image: mariadb:10.11 # Utilise l'image officielle MariaDB + container_name: looper-db + environment: + MYSQL_ROOT_PASSWORD: root_password # Mot de passe root + MYSQL_DATABASE: my_database # Base de données initiale + MYSQL_USER: my_user # Utilisateur + MYSQL_PASSWORD: my_password # Mot de passe utilisateur + volumes: + - db_data:/var/lib/mysql # Persist les données + ports: + - "3308:3306" # Mappe le port 3306 + restart: always + +volumes: + db_data: \ No newline at end of file diff --git a/composer.json b/composer.json index 15b5010..d496036 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,20 @@ -{ - "require-dev": { - "squizlabs/php_codesniffer": "^3.0", - "phpunit/phpunit": "^9.6" - }, - "require": { - "ext-pdo": "*", - "vlucas/phpdotenv": "^5.6" - }, - "autoload": { - "psr-4": { - "App\\": "src/" - } - } -} +{ + "name": "nui/maw11", + "type": "project", + "minimum-stability": "stable", + "require": { + "ext-pdo": "*" + }, + "autoload": { + "psr-4": { + "App\\": "src/", + "Controllers\\": "src/Controllers", + "Router\\": "src/Router", + "Database\\": "src/Database/", + "Models\\": "src/Models/" + } + }, + "require-dev": { + "phpunit/phpunit": "^9" + } + } \ No newline at end of file diff --git a/composer.lock b/composer.lock index 9fc8233..6c16a37 100644 --- a/composer.lock +++ b/composer.lock @@ -4,496 +4,35 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ad73e87473f3936c17a26c77efeba743", - "packages": [ - { - "name": "graham-campbell/result-type", - "version": "v1.1.3", - "source": { - "type": "git", - "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" - }, - "type": "library", - "autoload": { - "psr-4": { - "GrahamCampbell\\ResultType\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - } - ], - "description": "An Implementation Of The Result Type", - "keywords": [ - "Graham Campbell", - "GrahamCampbell", - "Result Type", - "Result-Type", - "result" - ], - "support": { - "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", - "type": "tidelift" - } - ], - "time": "2024-07-20T21:45:45+00:00" - }, - { - "name": "phpoption/phpoption", - "version": "1.9.3", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - }, - "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpOption\\": "src/PhpOption/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Johannes M. Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh" - }, - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - } - ], - "description": "Option Type for PHP", - "keywords": [ - "language", - "option", - "php", - "type" - ], - "support": { - "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", - "type": "tidelift" - } - ], - "time": "2024-07-20T21:41:07+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.31.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.31.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "vlucas/phpdotenv", - "version": "v5.6.1", - "source": { - "type": "git", - "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", - "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.3", - "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3", - "symfony/polyfill-ctype": "^1.24", - "symfony/polyfill-mbstring": "^1.24", - "symfony/polyfill-php80": "^1.24" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "ext-filter": "*", - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" - }, - "suggest": { - "ext-filter": "Required to use the boolean validator." - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - }, - "branch-alias": { - "dev-master": "5.6-dev" - } - }, - "autoload": { - "psr-4": { - "Dotenv\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Vance Lucas", - "email": "vance@vancelucas.com", - "homepage": "https://github.com/vlucas" - } - ], - "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", - "keywords": [ - "dotenv", - "env", - "environment" - ], - "support": { - "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", - "type": "tidelift" - } - ], - "time": "2024-07-20T21:52:34+00:00" - } - ], + "content-hash": "198f7269e4a8dd66ef9df72dfca4caf3", + "packages": [], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "2.0.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -520,7 +59,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -536,20 +75,20 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -588,7 +127,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -596,20 +135,20 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nikic/php-parser", - "version": "v5.1.0", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", "shasum": "" }, "require": { @@ -652,9 +191,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" }, - "time": "2024-07-01T20:03:41+00:00" + "time": "2024-10-08T18:51:32+00:00" }, { "name": "phar-io/manifest", @@ -1095,16 +634,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.20", + "version": "9.6.22", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "49d7820565836236411f5dc002d16dd689cde42f" + "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/49d7820565836236411f5dc002d16dd689cde42f", - "reference": "49d7820565836236411f5dc002d16dd689cde42f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", + "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", "shasum": "" }, "require": { @@ -1115,11 +654,11 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.12.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.31", + "phpunit/php-code-coverage": "^9.2.32", "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.4", @@ -1178,7 +717,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.20" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" }, "funding": [ { @@ -1194,7 +733,7 @@ "type": "tidelift" } ], - "time": "2024-07-10T11:45:39+00:00" + "time": "2024-12-05T13:48:26+00:00" }, { "name": "sebastian/cli-parser", @@ -2159,86 +1698,6 @@ ], "time": "2020-09-28T06:39:44+00:00" }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.10.2", - "source": { - "type": "git", - "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/86e5f5dd9a840c46810ebe5ff1885581c42a3017", - "reference": "86e5f5dd9a840c46810ebe5ff1885581c42a3017", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" - }, - "bin": [ - "bin/phpcbf", - "bin/phpcs" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "Former lead" - }, - { - "name": "Juliette Reinders Folmer", - "role": "Current lead" - }, - { - "name": "Contributors", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", - "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", - "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", - "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" - }, - "funding": [ - { - "url": "https://github.com/PHPCSStandards", - "type": "github" - }, - { - "url": "https://github.com/jrfnl", - "type": "github" - }, - { - "url": "https://opencollective.com/php_codesniffer", - "type": "open_collective" - } - ], - "time": "2024-07-21T23:26:44+00:00" - }, { "name": "theseer/tokenizer", "version": "1.2.3", @@ -2299,5 +1758,5 @@ "ext-pdo": "*" }, "platform-dev": [], - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.2.0" } diff --git a/doc/MCD.loo b/doc/MCD.loo deleted file mode 100644 index 6149a0cbb191b4b2f9353c88ab656ad4652e9968..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61968 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 \ No newline at end of file diff --git a/modelisation/ClassUML.png b/modelisation/ClassUML.png new file mode 100644 index 0000000000000000000000000000000000000000..5469311eff2670101c277dffd516703538487f69 GIT binary patch literal 83504 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%0DeN&r4^ut_RYL2u>2gL~YoHi! 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

lo2y2c3AXjP01(A_-qIK_(GKKR^BGzM?? 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 \ No newline at end of file diff --git a/modelisation/MCD.png b/modelisation/MCD.png new file mode 100644 index 0000000000000000000000000000000000000000..7baafd5dad6cbdb651e1e7fb82b2e9de8980f2cf GIT binary patch literal 21461 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 Du1q(n literal 0 HcmV?d00001 diff --git a/modelisation/MLD.mwb b/modelisation/MLD.mwb new file mode 100644 index 0000000000000000000000000000000000000000..3d253095b986c55edc75d3d80a444f1ac8a47c63 GIT binary patch literal 10496 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 B5d8oE literal 0 HcmV?d00001 diff --git a/modelisation/MLD.png b/modelisation/MLD.png new file mode 100644 index 0000000000000000000000000000000000000000..43de2525292a11bfc2b0001480c1200bab2c16e2 GIT binary patch literal 20531 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 Q&;<};im@=NG<1vp2MgV$R{#J2 literal 0 HcmV?d00001 diff --git a/modelisation/Nazmi-php.code-workspace b/modelisation/Nazmi-php.code-workspace new file mode 100644 index 0000000..794dcc9 --- /dev/null +++ b/modelisation/Nazmi-php.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": ".." + }, + { + "path": "../../MAW1-BPT-SMD" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/modelisation/createDatabase.sql b/modelisation/createDatabase.sql new file mode 100644 index 0000000..5210eca --- /dev/null +++ b/modelisation/createDatabase.sql @@ -0,0 +1,57 @@ +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET NAMES utf8 */; +/*!50503 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +DROP DATABASE IF EXISTS `looper`; +CREATE DATABASE IF NOT EXISTS `looper` /*!40100 DEFAULT CHARACTER SET utf8mb3 */; +USE `looper`; + +CREATE TABLE IF NOT EXISTS `exercises` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `title` varchar(45) NOT NULL, + `state` varchar(45) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `title_UNIQUE` (`title`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; + +CREATE TABLE IF NOT EXISTS `fields` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `label` varchar(45) NOT NULL, + `value_kind` varchar(45) NOT NULL, + `exercises_id` int(11) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `label` (`label`,`exercises_id`) USING BTREE, + KEY `fk_fields_exercises_idx` (`exercises_id`), + CONSTRAINT `fk_fields_exercises` FOREIGN KEY (`exercises_id`) REFERENCES `exercises` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; + +CREATE TABLE IF NOT EXISTS `fields_has_fulfillments` ( + `fields_id` int(11) NOT NULL, + `fulfillments_id` int(11) NOT NULL, + `value` varchar(255) DEFAULT NULL, + UNIQUE KEY `fields_id` (`fields_id`,`fulfillments_id`), + KEY `fk_fields` (`fields_id`) USING BTREE, + KEY `fk_fulfillments` (`fulfillments_id`), + CONSTRAINT `fk_fields` FOREIGN KEY (`fields_id`) REFERENCES `fields` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT `fk_fulfillments` FOREIGN KEY (`fulfillments_id`) REFERENCES `fulfillments` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; + +CREATE TABLE IF NOT EXISTS `fulfillments` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `exercises_id` int(11) NOT NULL, + `date` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `fk_fulfillments_exercises` (`exercises_id`), + CONSTRAINT `fk_fulfillments_exercises` FOREIGN KEY (`exercises_id`) REFERENCES `exercises` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; + +/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */; +/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; +/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */; diff --git a/modelisation/sequenceExerciseFields.puml b/modelisation/sequenceExerciseFields.puml new file mode 100644 index 0000000..96d415c --- /dev/null +++ b/modelisation/sequenceExerciseFields.puml @@ -0,0 +1,50 @@ +@startuml +'https://plantuml.com/sequence-diagram + +actor User order 1 +participant Exercise order 2 +participant Field order 2 +participant ExerciseHelper order 4 +participant Query order 5 +participant DBConnection order 6 +actor Database order 7 + +group Create Exercise + User -> Exercise : Create a new exercise + Exercise -> ExerciseHelper : Create() + ExerciseHelper -> Query : insert() + Query -> DBConnection : execute() + DBConnection -> Database : Create the exercise +end + +group Delete Exercise +User -> Exercise : Delete the exercise +Exercise -> ExerciseHelper : Delete() +ExerciseHelper -> Query : delete() +Query -> DBConnection : execute() +DBConnection -> Database : Delete the exercise +end + +group Create Field +User -> Field : Create a new field +Field -> Exercise : CreateField() +Exercise -> Query : insert() +Query -> DBConnection : execute() +DBConnection -> Database : Create the field +end + +group Edit Field +User -> Field : Edit the field +Field -> Exercise : setValueKind(), setLabel() +Exercise --> Query : saveField() +Query -> DBConnection : execute() +DBConnection -> Database : Edit the field +end +group Delete Field +Field -> Exercise : Delete the field +Exercise -> ExerciseHelper : deleteField() +ExerciseHelper -> Query : deleteField() +Query -> DBConnection : execute() +DBConnection -> Database : Delete the field +end +@enduml \ No newline at end of file diff --git a/public/assets/fa-solid-900.woff2 b/public/assets/fa-solid-900.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e30fb671128fdf2f4fc6d7b1c96c0736bc547e58 GIT binary patch literal 75440 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 DKZR3$ literal 0 HcmV?d00001 diff --git a/logo-84d7d70645fbe179ce04c983a5fae1e6cba523d7cd28e0cd49a04707ccbef56e.png b/public/assets/logo.png similarity index 100% rename from logo-84d7d70645fbe179ce04c983a5fae1e6cba523d7cd28e0cd49a04707ccbef56e.png rename to public/assets/logo.png diff --git a/public/const.php b/public/const.php new file mode 100644 index 0000000..373324c --- /dev/null +++ b/public/const.php @@ -0,0 +1,6 @@ + code { - border-radius: 0; - display: block; - padding: 1rem 1.5rem; - white-space: pre; -} -hr { - border: 0; - border-top: 0.1rem solid #f4f5f6; - margin: 3rem 0; -} -input[type="color"], -input[type="date"], -input[type="datetime"], -input[type="datetime-local"], -input[type="email"], -input[type="month"], -input[type="number"], -input[type="password"], -input[type="search"], -input[type="tel"], -input[type="text"], -input[type="url"], -input[type="week"], -input:not([type]), -textarea, -select { - -webkit-appearance: none; - background-color: transparent; - border: 0.1rem solid #d1d1d1; - border-radius: 0.4rem; - box-shadow: none; - box-sizing: inherit; - height: 3.8rem; - padding: 0.6rem 1rem 0.7rem; - width: 100%; -} -input[type="color"]:focus, -input[type="date"]:focus, -input[type="datetime"]:focus, -input[type="datetime-local"]:focus, -input[type="email"]:focus, -input[type="month"]:focus, -input[type="number"]:focus, -input[type="password"]:focus, -input[type="search"]:focus, -input[type="tel"]:focus, -input[type="text"]:focus, -input[type="url"]:focus, -input[type="week"]:focus, -input:not([type]):focus, -textarea:focus, -select:focus { - border-color: #9b4dca; - outline: 0; -} -select { - background: url('data:image/svg+xml;utf8,') - center right no-repeat; - padding-right: 3rem; -} -select:focus { - background-image: url('data:image/svg+xml;utf8,'); -} -select[multiple] { - background: none; - height: auto; -} -textarea { - min-height: 6.5rem; -} -label, -legend { - display: block; - font-size: 1.6rem; - font-weight: 700; - margin-bottom: 0.5rem; -} -fieldset { - border-width: 0; - padding: 0; -} -input[type="checkbox"], -input[type="radio"] { - display: inline; -} -.label-inline { - display: inline-block; - font-weight: normal; - margin-left: 0.5rem; -} -.container { - margin: 0 auto; - max-width: 112rem; - padding: 0 2rem; - position: relative; - width: 100%; -} -.row { - display: flex; - flex-direction: column; - padding: 0; - width: 100%; -} -.row.row-no-padding { - padding: 0; -} -.row.row-no-padding > .column { - padding: 0; -} -.row.row-wrap { - flex-wrap: wrap; -} -.row.row-top { - align-items: flex-start; -} -.row.row-bottom { - align-items: flex-end; -} -.row.row-center { - align-items: center; -} -.row.row-stretch { - align-items: stretch; -} -.row.row-baseline { - align-items: baseline; -} -.row .column { - display: block; - flex: 1 1 auto; - margin-left: 0; - max-width: 100%; - width: 100%; -} -.row .column.column-offset-10 { - margin-left: 10%; -} -.row .column.column-offset-20 { - margin-left: 20%; -} -.row .column.column-offset-25 { - margin-left: 25%; -} -.row .column.column-offset-33, -.row .column.column-offset-34 { - margin-left: 33.3333%; -} -.row .column.column-offset-40 { - margin-left: 40%; -} -.row .column.column-offset-50 { - margin-left: 50%; -} -.row .column.column-offset-60 { - margin-left: 60%; -} -.row .column.column-offset-66, -.row .column.column-offset-67 { - margin-left: 66.6666%; -} -.row .column.column-offset-75 { - margin-left: 75%; -} -.row .column.column-offset-80 { - margin-left: 80%; -} -.row .column.column-offset-90 { - margin-left: 90%; -} -.row .column.column-10 { - flex: 0 0 10%; - max-width: 10%; -} -.row .column.column-20 { - flex: 0 0 20%; - max-width: 20%; -} -.row .column.column-25 { - flex: 0 0 25%; - max-width: 25%; -} -.row .column.column-33, -.row .column.column-34 { - flex: 0 0 33.3333%; - max-width: 33.3333%; -} -.row .column.column-40 { - flex: 0 0 40%; - max-width: 40%; -} -.row .column.column-50 { - flex: 0 0 50%; - max-width: 50%; -} -.row .column.column-60 { - flex: 0 0 60%; - max-width: 60%; -} -.row .column.column-66, -.row .column.column-67 { - flex: 0 0 66.6666%; - max-width: 66.6666%; -} -.row .column.column-75 { - flex: 0 0 75%; - max-width: 75%; -} -.row .column.column-80 { - flex: 0 0 80%; - max-width: 80%; -} -.row .column.column-90 { - flex: 0 0 90%; - max-width: 90%; -} -.row .column .column-top { - align-self: flex-start; -} -.row .column .column-bottom { - align-self: flex-end; -} -.row .column .column-center { - align-self: center; -} -@media (min-width: 40rem) { - .row { - flex-direction: row; - margin-left: -1rem; - width: calc(100% + 2rem); - } - .row .column { - margin-bottom: inherit; - padding: 0 1rem; - } -} -a { - color: #9b4dca; - text-decoration: none; -} -a:focus, -a:hover { - color: #606c76; -} -dl, -ol, -ul { - list-style: none; - margin-top: 0; - padding-left: 0; -} -dl dl, -dl ol, -dl ul, -ol dl, -ol ol, -ol ul, -ul dl, -ul ol, -ul ul { - font-size: 90%; - margin: 1.5rem 0 1.5rem 3rem; -} -ol { - list-style: decimal inside; -} -ul { - list-style: circle inside; -} -.button, -button, -dd, -dt, -li { - margin-bottom: 1rem; -} -fieldset, -input, -select, -textarea { - margin-bottom: 1.5rem; -} -blockquote, -dl, -figure, -form, -ol, -p, -pre, -table, -ul { - margin-bottom: 2.5rem; -} -table { - border-spacing: 0; - display: block; - overflow-x: auto; - text-align: left; - width: 100%; -} -td, -th { - border-bottom: 0.1rem solid #e1e1e1; - padding: 1.2rem 1.5rem; -} -td:first-child, -th:first-child { - padding-left: 0; -} -td:last-child, -th:last-child { - padding-right: 0; -} -@media (min-width: 40rem) { - table { - display: table; - overflow-x: initial; - } -} -b, -strong { - font-weight: bold; -} -p { - margin-top: 0; -} -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: 300; - letter-spacing: -0.1rem; - margin-bottom: 2rem; - margin-top: 0; -} -h1 { - font-size: 4.6rem; - line-height: 1.2; -} -h2 { - font-size: 3.6rem; - line-height: 1.25; -} -h3 { - font-size: 2.8rem; - line-height: 1.3; -} -h4 { - font-size: 2.2rem; - letter-spacing: -0.08rem; - line-height: 1.35; -} -h5 { - font-size: 1.8rem; - letter-spacing: -0.05rem; - line-height: 1.5; -} -h6 { - font-size: 1.6rem; - letter-spacing: 0; - line-height: 1.4; -} -img { - max-width: 100%; -} -.clearfix:after { - clear: both; - content: " "; - display: table; -} -.float-left { - float: left; -} -.float-right { - float: right; -} - -/*# sourceMappingURL=milligram.min.css.map */ +*, +*:after, +*:before { + box-sizing: inherit; +} +html { + box-sizing: border-box; + font-size: 62.5%; +} +body { + color: #606c76; + font-family: "Roboto", "Helvetica Neue", "Helvetica", "Arial", sans-serif; + font-size: 1.6em; + font-weight: 300; + letter-spacing: 0.01em; + line-height: 1.6; + margin: 0px; +} +blockquote { + border-left: 0.3rem solid #d1d1d1; + margin-left: 0; + margin-right: 0; + padding: 1rem 1.5rem; +} +blockquote *:last-child { + margin-bottom: 0; +} +.button, +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + background-color: #9b4dca; + border: 0.1rem solid #9b4dca; + border-radius: 0.4rem; + color: #fff; + cursor: pointer; + display: inline-block; + font-size: 1.1rem; + font-weight: 700; + height: 3.8rem; + letter-spacing: 0.1rem; + line-height: 3.8rem; + padding: 0 3rem; + text-align: center; + text-decoration: none; + text-transform: uppercase; + white-space: nowrap; +} +.button:focus, +.button:hover, +button:focus, +button:hover, +input[type="button"]:focus, +input[type="button"]:hover, +input[type="reset"]:focus, +input[type="reset"]:hover, +input[type="submit"]:focus, +input[type="submit"]:hover { + background-color: #606c76; + border-color: #606c76; + color: #fff; + outline: 0; +} +.button[disabled], +button[disabled], +input[type="button"][disabled], +input[type="reset"][disabled], +input[type="submit"][disabled] { + cursor: default; + opacity: 0.5; +} +.button[disabled]:focus, +.button[disabled]:hover, +button[disabled]:focus, +button[disabled]:hover, +input[type="button"][disabled]:focus, +input[type="button"][disabled]:hover, +input[type="reset"][disabled]:focus, +input[type="reset"][disabled]:hover, +input[type="submit"][disabled]:focus, +input[type="submit"][disabled]:hover { + background-color: #9b4dca; + border-color: #9b4dca; +} +.button.button-outline, +button.button-outline, +input[type="button"].button-outline, +input[type="reset"].button-outline, +input[type="submit"].button-outline { + background-color: transparent; + color: #9b4dca; +} +.button.button-outline:focus, +.button.button-outline:hover, +button.button-outline:focus, +button.button-outline:hover, +input[type="button"].button-outline:focus, +input[type="button"].button-outline:hover, +input[type="reset"].button-outline:focus, +input[type="reset"].button-outline:hover, +input[type="submit"].button-outline:focus, +input[type="submit"].button-outline:hover { + background-color: transparent; + border-color: #606c76; + color: #606c76; +} +.button.button-outline[disabled]:focus, +.button.button-outline[disabled]:hover, +button.button-outline[disabled]:focus, +button.button-outline[disabled]:hover, +input[type="button"].button-outline[disabled]:focus, +input[type="button"].button-outline[disabled]:hover, +input[type="reset"].button-outline[disabled]:focus, +input[type="reset"].button-outline[disabled]:hover, +input[type="submit"].button-outline[disabled]:focus, +input[type="submit"].button-outline[disabled]:hover { + border-color: inherit; + color: #9b4dca; +} +.button.button-clear, +button.button-clear, +input[type="button"].button-clear, +input[type="reset"].button-clear, +input[type="submit"].button-clear { + background-color: transparent; + border-color: transparent; + color: #9b4dca; +} +.button.button-clear:focus, +.button.button-clear:hover, +button.button-clear:focus, +button.button-clear:hover, +input[type="button"].button-clear:focus, +input[type="button"].button-clear:hover, +input[type="reset"].button-clear:focus, +input[type="reset"].button-clear:hover, +input[type="submit"].button-clear:focus, +input[type="submit"].button-clear:hover { + background-color: transparent; + border-color: transparent; + color: #606c76; +} +.button.button-clear[disabled]:focus, +.button.button-clear[disabled]:hover, +button.button-clear[disabled]:focus, +button.button-clear[disabled]:hover, +input[type="button"].button-clear[disabled]:focus, +input[type="button"].button-clear[disabled]:hover, +input[type="reset"].button-clear[disabled]:focus, +input[type="reset"].button-clear[disabled]:hover, +input[type="submit"].button-clear[disabled]:focus, +input[type="submit"].button-clear[disabled]:hover { + color: #9b4dca; +} +code { + background: #f4f5f6; + border-radius: 0.4rem; + font-size: 86%; + margin: 0 0.2rem; + padding: 0.2rem 0.5rem; + white-space: nowrap; +} +pre { + background: #f4f5f6; + border-left: 0.3rem solid #9b4dca; + overflow-y: hidden; +} +pre > code { + border-radius: 0; + display: block; + padding: 1rem 1.5rem; + white-space: pre; +} +hr { + border: 0; + border-top: 0.1rem solid #f4f5f6; + margin: 3rem 0; +} +input[type="color"], +input[type="date"], +input[type="datetime"], +input[type="datetime-local"], +input[type="email"], +input[type="month"], +input[type="number"], +input[type="password"], +input[type="search"], +input[type="tel"], +input[type="text"], +input[type="url"], +input[type="week"], +input:not([type]), +textarea, +select { + -webkit-appearance: none; + background-color: transparent; + border: 0.1rem solid #d1d1d1; + border-radius: 0.4rem; + box-shadow: none; + box-sizing: inherit; + height: 3.8rem; + padding: 0.6rem 1rem 0.7rem; + width: 100%; +} +input[type="color"]:focus, +input[type="date"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="email"]:focus, +input[type="month"]:focus, +input[type="number"]:focus, +input[type="password"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="text"]:focus, +input[type="url"]:focus, +input[type="week"]:focus, +input:not([type]):focus, +textarea:focus, +select:focus { + border-color: #9b4dca; + outline: 0; +} +select { + background: url('data:image/svg+xml;utf8,') + center right no-repeat; + padding-right: 3rem; +} +select:focus { + background-image: url('data:image/svg+xml;utf8,'); +} +select[multiple] { + background: none; + height: auto; +} +textarea { + min-height: 6.5rem; +} +label, +legend { + display: block; + font-size: 1.6rem; + font-weight: 700; + margin-bottom: 0.5rem; +} +fieldset { + border-width: 0; + padding: 0; +} +input[type="checkbox"], +input[type="radio"] { + display: inline; +} +.label-inline { + display: inline-block; + font-weight: normal; + margin-left: 0.5rem; +} +.container { + margin: 0 auto; + max-width: 112rem; + padding: 0 2rem; + position: relative; + width: 100%; +} +.row { + display: flex; + flex-direction: column; + padding: 0; + width: 100%; +} +.row.row-no-padding { + padding: 0; +} +.row.row-no-padding > .column { + padding: 0; +} +.row.row-wrap { + flex-wrap: wrap; +} +.row.row-top { + align-items: flex-start; +} +.row.row-bottom { + align-items: flex-end; +} +.row.row-center { + align-items: center; +} +.row.row-stretch { + align-items: stretch; +} +.row.row-baseline { + align-items: baseline; +} +.row .column { + display: block; + flex: 1 1 auto; + margin-left: 0; + max-width: 100%; + width: 100%; +} +.row .column.column-offset-10 { + margin-left: 10%; +} +.row .column.column-offset-20 { + margin-left: 20%; +} +.row .column.column-offset-25 { + margin-left: 25%; +} +.row .column.column-offset-33, +.row .column.column-offset-34 { + margin-left: 33.3333%; +} +.row .column.column-offset-40 { + margin-left: 40%; +} +.row .column.column-offset-50 { + margin-left: 50%; +} +.row .column.column-offset-60 { + margin-left: 60%; +} +.row .column.column-offset-66, +.row .column.column-offset-67 { + margin-left: 66.6666%; +} +.row .column.column-offset-75 { + margin-left: 75%; +} +.row .column.column-offset-80 { + margin-left: 80%; +} +.row .column.column-offset-90 { + margin-left: 90%; +} +.row .column.column-10 { + flex: 0 0 10%; + max-width: 10%; +} +.row .column.column-20 { + flex: 0 0 20%; + max-width: 20%; +} +.row .column.column-25 { + flex: 0 0 25%; + max-width: 25%; +} +.row .column.column-33, +.row .column.column-34 { + flex: 0 0 33.3333%; + max-width: 33.3333%; +} +.row .column.column-40 { + flex: 0 0 40%; + max-width: 40%; +} +.row .column.column-50 { + flex: 0 0 50%; + max-width: 50%; +} +.row .column.column-60 { + flex: 0 0 60%; + max-width: 60%; +} +.row .column.column-66, +.row .column.column-67 { + flex: 0 0 66.6666%; + max-width: 66.6666%; +} +.row .column.column-75 { + flex: 0 0 75%; + max-width: 75%; +} +.row .column.column-80 { + flex: 0 0 80%; + max-width: 80%; +} +.row .column.column-90 { + flex: 0 0 90%; + max-width: 90%; +} +.row .column .column-top { + align-self: flex-start; +} +.row .column .column-bottom { + align-self: flex-end; +} +.row .column .column-center { + align-self: center; +} +@media (min-width: 40rem) { + .row { + flex-direction: row; + margin-left: -1rem; + width: calc(100% + 2rem); + } + .row .column { + margin-bottom: inherit; + padding: 0 1rem; + } +} +a { + color: #9b4dca; + text-decoration: none; +} +a:focus, +a:hover { + color: #606c76; +} +dl, +ol, +ul { + list-style: none; + margin-top: 0; + padding-left: 0; +} +dl dl, +dl ol, +dl ul, +ol dl, +ol ol, +ol ul, +ul dl, +ul ol, +ul ul { + font-size: 90%; + margin: 1.5rem 0 1.5rem 3rem; +} +ol { + list-style: decimal inside; +} +ul { + list-style: circle inside; +} +.button, +button, +dd, +dt, +li { + margin-bottom: 1rem; +} +fieldset, +input, +select, +textarea { + margin-bottom: 1.5rem; +} +blockquote, +dl, +figure, +form, +ol, +p, +pre, +table, +ul { + margin-bottom: 2.5rem; +} +table { + border-spacing: 0; + display: block; + overflow-x: auto; + text-align: left; + width: 100%; +} +td, +th { + border-bottom: 0.1rem solid #e1e1e1; + padding: 1.2rem 1.5rem; +} +td:first-child, +th:first-child { + padding-left: 0; +} +td:last-child, +th:last-child { + padding-right: 0; +} +@media (min-width: 40rem) { + table { + display: table; + overflow-x: initial; + } +} +b, +strong { + font-weight: bold; +} +p { + margin-top: 0; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 300; + letter-spacing: -0.1rem; + margin-bottom: 2rem; + margin-top: 0; +} +h1 { + font-size: 4.6rem; + line-height: 1.2; +} +h2 { + font-size: 3.6rem; + line-height: 1.25; +} +h3 { + font-size: 2.8rem; + line-height: 1.3; +} +h4 { + font-size: 2.2rem; + letter-spacing: -0.08rem; + line-height: 1.35; +} +h5 { + font-size: 1.8rem; + letter-spacing: -0.05rem; + line-height: 1.5; +} +h6 { + font-size: 1.6rem; + letter-spacing: 0; + line-height: 1.4; +} +img { + max-width: 100%; +} +.clearfix:after { + clear: both; + content: " "; + display: table; +} +.float-left { + float: left; +} +.float-right { + float: right; +} + +/*# sourceMappingURL=milligram.min.css.map */ diff --git a/public/css/new_exercise.css b/public/css/new_exercise.css deleted file mode 100644 index 9b52678..0000000 --- a/public/css/new_exercise.css +++ /dev/null @@ -1,63 +0,0 @@ -body { - font-family: Arial, sans-serif; - margin: 0; - padding: 0; -} - -.container { - display: flex; - flex-direction: column; - align-items: center; -} - -header { - background-color: #d4a05a; - width: 100%; - padding: 10px; - text-align: center; -} - -.fields-section, .new-field-section { - margin: 20px; - padding: 20px; - border: 1px solid #ccc; - border-radius: 5px; - width: 80%; -} - -h2 { - margin-top: 0; -} - -.complete-button { - background-color: #8e44ad; - color: white; - border: none; - padding: 10px 20px; - cursor: pointer; - border-radius: 5px; -} - -form { - display: flex; - flex-direction: column; -} - -label { - margin-top: 10px; -} - -input, select, button[type="submit"] { - margin-top: 5px; - padding: 10px; - border: 1px solid #ccc; - border-radius: 5px; -} - -button[type="submit"] { - background-color: #8e44ad; - color: white; - border: none; - cursor: pointer; - margin-top: 20px; -} diff --git a/public/index.php b/public/index.php index 1484601..30509e9 100644 --- a/public/index.php +++ b/public/index.php @@ -1,46 +1,47 @@ -load(); - -use App\Route; -use App\Router; -use App\Controllers\Controller; -use App\Controllers\ExercisesController; - -$route = $_SERVER['REQUEST_URI']; -$method = $_SERVER["REQUEST_METHOD"]; - -if (!empty($_SERVER["QUERY_STRING"])) { - $route = substr($route, 0, strlen($_SERVER["REQUEST_URI"]) - strlen($_SERVER["QUERY_STRING"]) - 1); -} - -include_once SOURCE_DIR . '/Router.php'; -$router = new Router([$route, $method]); - -// Ajout des routes -$router->addRoute(new Route('GET', '/', [Controller::class, '/home.php'])); -$router->addRoute(new Route('GET', '/exercises', [ExercisesController::class, 'index'])); // Ajout de cette ligne -$router->addRoute(new Route('GET', '/exercises/new', [Controller::class, '/create.php'])); -$router->addRoute(new Route('GET', '/exercises/answering', [ExercisesController::class, 'showAnswering'])); -$router->addRoute(new Route('GET', '/exercises/{exerciseId}/fields', [Controller::class, '/fields.php'])); -$router->addRoute(new Route('POST', '/exercises', [ExercisesController::class, 'create'])); -$router->addRoute(new Route('POST', '/exercises/{exerciseId}/status', [ExercisesController::class, 'updateStatus'])); - -$router->matchRoute(); - -ini_set('display_errors', 1); -ini_set('display_startup_errors', 1); -error_reporting(E_ALL); +get('home_index', new Route('/', HomeController::class, 'index')); + +$router->get('exercises_index', new Route('/exercises', ExerciseController::class, 'index')); +$router->get('exercises_new', new Route('/exercises/new', ExerciseController::class, 'new')); +$router->get('exercises_answering', new Route('/exercises/answering', ExerciseController::class, 'answering')); +$router->post('exercises_create', new Route('/exercises/new', ExerciseController::class, 'new')); +$router->post('exercises_state', new Route('/exercises/:exercise/state', ExerciseController::class, 'state')); +$router->post('exercises_delete', new Route('/exercises/:exercise', ExerciseController::class, 'delete')); +$router->get('exercises_results', new Route('/exercises/:exercise/results', ExerciseController::class, 'results')); + +$router->get('fields_index', new Route('/exercises/:exercise/fields', FieldsController::class, 'index')); +$router->post('fields_create', new Route('/exercises/:exercise/fields', FieldsController::class, 'index')); +$router->get('fields_edit', new Route('/exercises/:exercise/fields/:field/edit', FieldsController::class, 'edit')); +$router->post('fields_update', new Route('/exercises/:exercise/fields/:field/edit', FieldsController::class, 'edit')); +$router->post('fields_delete', new Route('/exercises/:exercise/fields/:field', FieldsController::class, 'delete')); +$router->get('fields_results', new Route('/exercises/:exercise/results/:field', FieldsController::class, 'results')); + +$router->get('fulfillments_new', new Route('/exercises/:exercise/fulfillments/new', FulfillmentController::class, 'new')); +$router->post('fulfillments_create', new Route('/exercises/:exercise/fulfillments/create', FulfillmentController::class, 'create')); +$router->get('fulfillments_edit', new Route('/exercises/:exercise/fulfillments/:fulfillment/edit', FulfillmentController::class, 'edit')); +$router->post('fulfillments_update', new Route('/exercises/:exercise/fulfillments/:fulfillment/update', FulfillmentController::class, 'update')); +$router->get('fulfillments_results', new Route('/exercises/:exercise/fulfillments/:fulfillment', FulfillmentController::class, 'results')); + +$router->run(); diff --git a/src/Controllers/Controller.php b/src/Controllers/Controller.php index bacb2b2..60e3e4a 100644 --- a/src/Controllers/Controller.php +++ b/src/Controllers/Controller.php @@ -1,42 +1,32 @@ -variables = $variables; - } - - public function getVariables(): array - { - return $this->variables; - } - - // Fusion des deux méthodes view en une seule méthode polymorphe - public static function view($viewPath, $params = []) - { - if (is_array($viewPath)) { - $idsArray = $viewPath[1]; - $data = []; - - if ($idsArray != []) { - foreach ($idsArray as $key => $id) { - if (str_contains($key, "exercise")) { - $data["exercise"] = Exercises::findBy("id", $id)[0]; - } - } - } - - require_once VIEW_DIR . $viewPath[0]; - } else { - extract($params); - include(VIEW_DIR . '/' . $viewPath . '.php'); - } - } -} +dbConnection = DBConnection::getInstance(); + $this->router = Router::getInstance(); + if (session_status() === PHP_SESSION_NONE) { + session_start(); + } + } + + protected function view(string $path, array $params = null): void + { + ob_start(); + require TEMPLATES_DIR . $path . '.php'; + $content = ob_get_clean(); + require TEMPLATES_DIR . 'layout.php'; + } + +} + + diff --git a/src/Controllers/ExerciseController.php b/src/Controllers/ExerciseController.php new file mode 100644 index 0000000..64c9bb9 --- /dev/null +++ b/src/Controllers/ExerciseController.php @@ -0,0 +1,103 @@ +exerciseHelper = new ExerciseHelper(); // Initializes the ExerciseHelper instance + } + + public function index() { + // Logic to show exercise + + $this->view('exercises/index', [ + 'exercises' => $this->exerciseHelper->get(), // Retrieves exercises using ExerciseHelper + 'router' => $this->router, // Passes the router to the view + ]); + } + + public function answering() { + // Logic to show exercise + + $this->view('exercises/answering', [ + 'exercises' => $this->exerciseHelper->get(), // Retrieves exercises using ExerciseHelper + 'router' => $this->router, // Passes the router to the view + ]); + } + + public function new() { + // Logic to show exercise + + $params['router'] = $this->router; // Sets the router parameter + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $exercise = new Exercise(['title' => $_POST['title']]); // Creates a new Exercise instance + if ($exerciseId = $this->exerciseHelper->save($exercise)) { + $this->router->redirect('fields_index', ['exercise' => $exerciseId]); // Redirects to fields index if exercise is saved + } + $params["error"] = "Le titre est déjà utilisé. Veuillez en choisir un autre."; // Sets error message if title is used + } + $this->view('exercises/new', $params); // Renders the new exercise view with parameters + + } + + public function getId(): int { + // Getter logic here + return $this->id; // Returns the ID + } + + public function getTitle(): string { + // Getter logic here + return $this->title; // Returns the title + } + + public function getFields(int $fieldId): array { + // Logic to get fields by exercise ID and field ID + $params = [':exerciseId' => $this->id, ':fieldId' => $fieldId]; // Sets parameters for query + return $this->query->select('fields', Field::class, 'exercise_id = :exerciseId', $params); // Executes the select query to get fields + } + + public function createField(Field $field): void { + // Logic to create field + + $data = ['exercise_id' => $this->id, 'name' => $field->getName(), 'description' => $field->getDescription()]; // Sets data for insertion + $field->setId($this->query->insert('fields', Field::class, $data)); // Inserts new field and sets its ID + + } + + public function deleteField(Field $field): void { + // Logic to delete field + $params = [':fieldId' => $field->getId()]; // Sets parameters for deletion + $this->query->delete('fields', Field::class, 'id = :fieldId', $params); // Executes the delete query + + } + + public function getFulfillments(int $fulfillmentId): array { + // Logic to get fulfillments + } + + public function state(int $exerciseId, string $query) { + parse_str($query, $params); // Parses the query string into parameters + + $exercise = $this->exerciseHelper->get($exerciseId); // Retrieves the exercise using ExerciseHelper + $exercise->setState($params['state']); // Sets the state of the exercise + + $this->exerciseHelper->save($exercise); // Saves the exercise + + $this->view('exercises/index', [ + 'exercises' => $this->exerciseHelper->get(), // Retrieves exercises using ExerciseHelper + 'router' => $this->router, // Passes the router to the view + ]); + } +} diff --git a/src/Controllers/ExercisesController.php b/src/Controllers/ExercisesController.php deleted file mode 100644 index e7eed67..0000000 --- a/src/Controllers/ExercisesController.php +++ /dev/null @@ -1,80 +0,0 @@ -router = new Router(); - $this->exerciseHelper = new ExerciseHelper(); - } - public function index(): void - { - $exercises = $this->exerciseHelper->get(); - var_dump($exercises); // Affiche les exercices récupérés pour vérifier - $this->view('exercises/index', [ - 'exercises' => $exercises, - 'router' => $this->router, - ]); - } - - public function create(): void - { - $params['router'] = $this->router; - if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $exercise = new Exercise(['title' => $_POST['title']]); - if ($exerciseId = $this->exerciseHelper->save($exercise)) { - $this->router->redirect('fields_index', ['exercise' => $exerciseId]); - } - $params["error"] = "Le titre est déjà utilisé. Veuillez en choisir un autre."; - } - $this->view('exercises/new', $params); - } - - public function state(int $exerciseId, string $query): void - { - parse_str($query, $params); - - $exercise = $this->exerciseHelper->get($exerciseId); - $exercise->setState($params['state']); - - $this->exerciseHelper->save($exercise); - - $this->router->redirect('exercises_index'); - } - - public function answering(): void - { - $this->view('exercises/answering', [ - 'exercises' => $this->exerciseHelper->get(), - 'router' => $this->router, - ]); - } - - public function results(int $exerciseId): void - { - $exercise = $this->exerciseHelper->get($exerciseId); - - $this->view('exercises/results', [ - 'exercise' => $exercise, - 'fields' => $exercise->getFields(), - 'fulfillments' => $exercise->getFulfillments(), - 'router' => $this->router, - ]); - } - - public function delete(int $exerciseId): void - { - $this->exerciseHelper->delete($exerciseId); - $this->router->redirect('exercises_index'); - } -} diff --git a/src/Controllers/FieldsController.php b/src/Controllers/FieldsController.php new file mode 100644 index 0000000..16369c4 --- /dev/null +++ b/src/Controllers/FieldsController.php @@ -0,0 +1,41 @@ +exerciseHelper = new ExerciseHelper(); // Initializes the ExerciseHelper instance + } + + public function index(int $exerciseId): void + { + $exercise = $this->exerciseHelper->get($exerciseId); // Retrieves the exercise using ExerciseHelper + $params = [ + 'exercise' => $exercise, // Sets the exercise parameter + 'router' => $this->router, // Sets the router parameter + ]; + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $field = new Field([ + 'label' => $_POST['field']['label'], // Sets the label for the field + 'value_kind' => $_POST['field']['value_kind'], // Sets the value kind for the field + ]); + + if ($exercise->createField($field)) { + $this->router->redirect('fields_index', ['exercise' => $exercise->getId()]); // Redirects to fields index if the field is created + } else { + $params["error"] = "Le label déjà utilisé. Veuillez en choisir un autre."; // Sets error message if label is already used + } + } + + $this->view('fields/index', $params); // Renders the fields index view with parameters + } +} diff --git a/src/Controllers/FulfillmentController.php b/src/Controllers/FulfillmentController.php new file mode 100644 index 0000000..3b7c511 --- /dev/null +++ b/src/Controllers/FulfillmentController.php @@ -0,0 +1,46 @@ +router; + $this->view('site/index', compact('router')); + } + +} \ No newline at end of file diff --git a/src/Database/DBConnection.php b/src/Database/DBConnection.php new file mode 100644 index 0000000..b28cce6 --- /dev/null +++ b/src/Database/DBConnection.php @@ -0,0 +1,66 @@ +pdo)) { + $this->open(); + } + return $this->pdo; + } + + private function open(): void { + $this->pdo = new PDO(self::$dns, self::$user, self::$password); + } + + public function close(): void { + // Close connection logic here + self::$dbConnection = null; + } + + public function execute(string $sql, string $class, array $param = null, bool $single = false): object|bool|array + { + $request = $this->getPDO()->prepare($sql); + + $request->execute($param); + + if (str_starts_with($sql, 'SELECT')) { + if ($single) { + return $request->fetchObject($class); + } else { + return $request->fetchAll(PDO::FETCH_CLASS, $class); + } + } else { + return $request->rowCount() > 0; + } + } + + public function getLastItemId(): int { + // Return last inserted ID logic here + return $this->getPDO()->lastInsertId(); + } +} \ No newline at end of file diff --git a/src/Database/Query.php b/src/Database/Query.php new file mode 100644 index 0000000..5fcd193 --- /dev/null +++ b/src/Database/Query.php @@ -0,0 +1,57 @@ +dbConnection = DBConnection::getInstance(); + } + + public function select( + String $table, + String $class, + string $conditions = null, + array $params = null, + bool $single = false + ): array|object { + $sql = "SELECT * FROM {$table}"; + $sql .= $conditions ? " WHERE {$conditions}" : ""; + return $this->dbConnection->execute($sql, $class, $params, $single); + } + + public function insert(string $table, string $class, array $data): int + { + $fieldList = implode(', ', array_keys($data)); + $placeholderList = implode(', ', array_map(fn($key) => ":{$key}", array_keys($data))); + + $this->dbConnection->execute( + "INSERT INTO {$table} ({$fieldList}) VALUES ({$placeholderList})", + $class, + $data + ); + return $this->dbConnection->getLastItemId(); + } + + public function update(string $table, string $class, string $conditions, array $params, array $data): bool + { + $updateList = implode( + ', ', + array_map(fn($key) => "{$key} = :{$key}", array_keys($data)) + ); + $data = array_merge($params, $data); + + return $this->dbConnection->execute( + "UPDATE {$table} SET {$updateList} WHERE {$conditions}", + $class, + $data + ); + } + + public function delete(String $table, String $conditions, String $class, Array $params): bool { + return $this->dbConnection->execute("DELETE FROM {$table} WHERE {$conditions}", $class, $params); + } +} \ No newline at end of file diff --git a/src/Models/Database.php b/src/Models/Database.php deleted file mode 100644 index 01a8803..0000000 --- a/src/Models/Database.php +++ /dev/null @@ -1,84 +0,0 @@ -hostname = $hostname; - $this->database = $database; - $this->username = $username; - $this->password = $password; - - $dsn = 'mysql:dbname=' . $database . ';host=' . $hostname; - try { - $this->pdo = new PDO($dsn, $username, $password, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")); - - // Set Error Mode to Exception - $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - } catch (PDOException $e) { - throw new Exception("Could not connect to the database: " . $e->getMessage(), 500); - } - } - - public static function getInstance($hostname, $database, $username, $password) - { - if (self::$instance === null) { - self::$instance = new self($hostname, $database, $username, $password); - } - return self::$instance; - } - - public function closeConnection() - { - $this->pdo = null; - } - - public function getPDO() - { - return $this->pdo; - } - - /** - * Prepares and executes sql queries - * - * @param string $sql The SQL query to execute - * @param array $params The parameters of the SQL query - * @return array The results of the query - * @throws Exception If the query fails. - */ - public function query($sql, $params = []) - { - try { - $statement = $this->pdo->prepare($sql); - $statement->execute($params); - - return $statement->fetchAll(PDO::FETCH_ASSOC); - } catch (PDOException $e) { - throw new Exception("Query failed: " . $e->getMessage(), 500); - } - } - - public function getLastInsertedRow($tableName) - { - $sql = "SELECT * FROM $tableName WHERE id = :id"; - $lastInsertedId = $this->pdo->lastInsertId(); - - return $this->query($sql, ["id" => $lastInsertedId]); - } -} diff --git a/src/Models/Exercises.php b/src/Models/Exercise.php similarity index 94% rename from src/Models/Exercises.php rename to src/Models/Exercise.php index a82bac9..154eb80 100644 --- a/src/Models/Exercises.php +++ b/src/Models/Exercise.php @@ -1,100 +1,111 @@ -query = new Query(); - if (array_key_exists('title', $params)) { - $this->title = $params['title']; - } - parent::__construct($params); // Appel au constructeur parent après initialisation - } - - public function getId(): ?int - { - return $this->id ?? null; - } - - public function getTitle(): string - { - return $this->title; - } - - public function setTitle(string $title): void - { - $this->title = $title; - } - - public function getState(): string - { - return $this->state; - } - - public function setState(string $state): void - { - $this->state = $state; - } - - public function getFields(int $fieldId = null): array|Field - { - if (is_null($fieldId)) { - return $this->query->select('fields', Field::class, 'exercises_id = :id', [':id' => $this->id]); - } else { - return $this->query->select( - 'fields', - Field::class, - 'id = :field_id AND exercises_id = :exercises_id', - ['field_id' => $fieldId, 'exercises_id' => $this->id], - true - ); - } - } - - public function createField(Field $field): int - { - try { - return $this->query->insert('fields', Field::class, [ - 'label' => $field->getLabel(), - 'value_kind' => $field->getValueKind(), - 'exercises_id' => $this->id, - ]); - } catch (PDOException $e) { - error_log($e); - return false; - } - } - - public function deleteField(int $fieldId): void - { - foreach ($this->getFulfillments() as $fulfillment) { - $fulfillment->delete(); - } - $this->query->delete('fields', Field::class, 'id = :id', ['id' => $fieldId]); - } - - public function getFulfillments(int $fulfillment = null): array|Fulfillment - { - if (is_null($fulfillment)) { - return $this->query->select('fulfillments', Fulfillment::class, 'exercises_id = :id', [':id' => $this->id]); - } else { - return $this->query->select( - 'fulfillments', - Fulfillment::class, - 'id = :field_id AND exercises_id = :exercises_id', - ['field_id' => $fulfillment, 'exercises_id' => $this->id], - true - ); - } - } -} +query = new Query(); + if (array_key_exists('title', $params)) { + $this->title = $params['title']; + } + } + + + public function getId(): ?int + { + return $this->id ?? null; + } + + + public function getTitle(): string + { + return $this->title; + } + + + public function setTitle(string $title): void + { + $this->title = $title; + } + + + public function getState(): string + { + return $this->state; + } + + + public function setState(string $state): void + { + $this->state = $state; + } + + + public function getFields(int $fieldId = null): array|Field + { + if (is_null($fieldId)) { + return $this->query->select('fields', Field::class, 'exercises_id = :id', [':id' => $this->id]); + } else { + return $this->query->select( + 'fields', + Field::class, + 'id = :field_id AND exercises_id = :exercises_id', + ['field_id' => $fieldId, 'exercises_id' => $this->id], + true + ); + } + } + + + + public function createField(Field $field): int + { + try { + return $this->query->insert('fields', Field::class, [ + 'label' => $field->getLabel(), + 'value_kind' => $field->getValueKind(), + 'exercises_id' => $this->id, + ]); + } catch (PDOException $e) { + error_log($e); + return false; + } + } + + + + public function deleteField(int $fieldId): void + { + foreach ($this->getFulfillments() as $fulfillment) { + $fulfillment->delete(); + } + $this->query->delete('fields', Field::class, 'id = :id', ['id' => $fieldId]); + } + + + public function getFulfillments(int $fulfillment = null): array|Fulfillment + { + if (is_null($fulfillment)) { + return $this->query->select('fulfillments', Fulfillment::class, 'exercises_id = :id', [':id' => $this->id]); + } else { + return $this->query->select( + 'fulfillments', + Fulfillment::class, + 'id = :field_id AND exercises_id = :exercises_id', + ['field_id' => $fulfillment, 'exercises_id' => $this->id], + true + ); + } + } +} \ No newline at end of file diff --git a/src/Models/ExerciseHelper.php b/src/Models/ExerciseHelper.php index 0b74e38..36f6f14 100644 --- a/src/Models/ExerciseHelper.php +++ b/src/Models/ExerciseHelper.php @@ -1,47 +1,90 @@ -id)) { - $columnNames = ['title', 'state']; // Ajoute d'autres colonnes si nécessaire - $parameters = [ - 'title' => $exercise->title, - 'state' => $exercise->state, - 'id' => $exercise->id - ]; - return self::update($columnNames, 'id', $parameters); - } else { - $columnNames = ['title', 'state']; // Ajoute d'autres colonnes si nécessaire - $parameters = [ - 'title' => $exercise->title, - 'state' => $exercise->state - ]; - return self::insert($columnNames, $parameters); - } - } - - public function delete($id) - { - $tableName = self::tableName(); - $sql = "DELETE FROM $tableName WHERE id = :id"; - return self::getDatabaseInstance()->query($sql, [':id' => $id]); - } -} +query = new Query(); + } + + + public function get(int $exerciseId = null): array|Exercise + { + if (is_null($exerciseId)) { + return $this->query->select('exercises', Exercise::class); + } else { + return $this->query->select('exercises', Exercise::class, 'id = :id', ['id' => $exerciseId], true); + } + } + + public function save(Exercise $exercise): int + { + if (is_null($exercise->getId())) { + return $this->create($exercise); + } else { + return $this->update($exercise); + } + } + + + private function create(Exercise $exercise): int + { + try { + return $this->query->insert( + 'exercises', + Exercise::class, + ['title' => $exercise->getTitle(), 'state' => $exercise->getState()] + ); + } catch (PDOException $e) { + error_log($e); + return false; + } + } + + + private function update(Exercise $exercise): int + { + try { + return $this->query->update( + 'exercises', + Exercise::class, + 'id = :id', + ['id' => $exercise->getId()], + ['title' => $exercise->getTitle(), 'state' => $exercise->getState()] + ); + } catch (PDOException $e) { + error_log($e); + return false; + } + } + + public function delete(int $exerciseId): bool + { + $exercise = $this->get($exerciseId); + $pdo = DBConnection::getInstance()->getPDO(); + try { + $pdo->beginTransaction(); + foreach ($exercise->getFields() as $field) { + $exercise->deleteField($field->getId()); + } + $this->query->delete('exercises', Exercise::class, 'id = :id', ['id' => $exerciseId]); + $pdo->commit(); + return true; + } finally { + // If the transaction has not been committed, roll it back + if ($pdo->inTransaction()) { + $pdo->rollBack(); + return false; + } + } + } +} \ No newline at end of file diff --git a/src/Models/Field.php b/src/Models/Field.php new file mode 100644 index 0000000..5f5d9bd --- /dev/null +++ b/src/Models/Field.php @@ -0,0 +1,84 @@ +query = new Query(); + if (array_key_exists('label', $params)) { + $this->label = $params['label']; + } + if (array_key_exists('value_kind', $params)) { + $this->value_kind = $params['value_kind']; + } + } + public function getId(): int + { + return $this->id; + } + public function setId(int $id): void + { + $this->id = $id; + } + public function getName(): string + { + return $this->name; + } + public function getDescription(): string + { + return $this->description; + } + public function getLabel(): string + { + return $this->label; + } + + public function setLabel(string $label): void + { + $this->label = $label; + } + + public function getValueKind(): string + { + return $this->value_kind; + } + + public function setValueKind(string $value_kind): void + { + $this->value_kind = $value_kind; + } + + public function update(): bool + { + try { + return $this->query->update( + 'fields', + Field::class, + 'id = :id', + ['id' => $this->id], + [ + 'label' => $this->getLabel(), + 'value_kind' => $this->getValueKind(), + ] + ); + } catch (PDOException $e) { + error_log($e); + return false; + } + } +} \ No newline at end of file diff --git a/src/Models/Fields.php b/src/Models/Fields.php deleted file mode 100644 index d222599..0000000 --- a/src/Models/Fields.php +++ /dev/null @@ -1,11 +0,0 @@ - $title, "field_types" => $field_types, "exercises_id" => $exercises_id]); - } -} diff --git a/src/Models/FieldsHasFulfillments.php b/src/Models/FieldsHasFulfillments.php new file mode 100644 index 0000000..41299d8 --- /dev/null +++ b/src/Models/FieldsHasFulfillments.php @@ -0,0 +1,15 @@ +value; + } +} diff --git a/src/Models/Fulfillment.php b/src/Models/Fulfillment.php new file mode 100644 index 0000000..9be9f9a --- /dev/null +++ b/src/Models/Fulfillment.php @@ -0,0 +1,116 @@ +query = new Query(); + if (array_key_exists('date', $params)) { + $this->date = $params['date']; + } + if (array_key_exists('exercise', $params)) { + $this->exercise = $params['exercise']; + } + } + + public function getId(): int + { + return $this->id; + } + + public function getDate(): string + { + return $this->date; + } + + public function save(array $answers = []): int + { + if (!isset($this->id)) { + return $this->create($answers); + } else { + return $this->update($answers); + } + } + + protected function create(array $answers = [[]]): int + { + try { + $fulfillmentsId = $this->query->insert( + 'fulfillments', + Fulfillment::class, + ['date' => $this->date, 'exercises_id' => $this->exercise->getId()] + ); + foreach ($answers as $key => $answer) { + $this->query->insert( + 'fields_has_fulfillments', + Fulfillment::class, + ['fulfillments_id' => $fulfillmentsId, 'fields_id' => $key, 'value' => $answer] + ); + } + return $fulfillmentsId; + } catch (PDOException $e) { + error_log($e); + return false; + } + } + + protected function update(array $answers = [[]]): int + { + try { + foreach ($answers as $fields_id => $answer) { + $this->query->update( + 'fields_has_fulfillments', + Fulfillment::class, + 'fields_id = :fields_id AND fulfillments_id = :fulfillments_id', + [ + 'fields_id' => $fields_id, + 'fulfillments_id' => $this->id, + ], + [ + 'value' => $answer, + ] + ); + } + return true; + } catch (PDOException $e) { + error_log($e); + return false; + } + } + + public function getValue(Field $field): string + { + $fieldsHasFulfillments = $this->query->select( + 'fields_has_fulfillments', + FieldsHasFulfillments::class, + 'fields_id = :fields_id AND fulfillments_id = :fulfillments_id', + [ + ':fields_id' => $field->getId(), + ':fulfillments_id' => $this->id, + ], + true + ); + return $fieldsHasFulfillments->getValue(); + } + + public function delete(): void + { + $this->query->delete( + 'fields_has_fulfillments', + FieldsHasFulfillments::class, + 'fulfillments_id = :fulfillments_id', + ['fulfillments_id' => $this->id] + ); + $this->query->delete('fulfillments', Fulfillment::class, 'id = :id', ['id' => $this->id]); + } +} diff --git a/src/Models/Model.php b/src/Models/Model.php deleted file mode 100644 index fd98ee9..0000000 --- a/src/Models/Model.php +++ /dev/null @@ -1,75 +0,0 @@ - $value) { - $this->$name = $value; - } - } - - public static function findAll() - { - $tableName = static::tableName(); - $sql = "SELECT * FROM $tableName"; - return static::getDatabaseInstance()->query($sql); - } - - public static function findBy($column, $value) - { - $tableName = static::tableName(); - $sql = "SELECT * FROM $tableName WHERE $column = :value"; - return static::getDatabaseInstance()->query($sql, [':value' => $value]); - } - - protected static function insert($columnNames, $SQLParameters) - { - $tableName = static::tableName(); - - $sql = "INSERT INTO $tableName (" . join(',', $columnNames) . ") VALUES (" . join(',', array_map(function ($item) { - return ':' . $item; - }, $columnNames)) . ")"; - - static::getDatabaseInstance()->query($sql, $SQLParameters); - - $results = static::getDatabaseInstance()->getLastInsertedRow($tableName); - - error_log(print_r($results, true)); - - return $results; - } - - protected static function update($columnNames, $columnCondition, $SQLParameters) - { - $tableName = static::tableName(); - - // $columnName = :$columnName, - - $sql = "UPDATE $tableName SET " . join(',', array_map(function ($value) { - return $value . " = :" . $value; - }, $columnNames)) . " WHERE " . $columnCondition . " = :" . $columnCondition; - - return static::getDatabaseInstance()->query($sql, $SQLParameters); - } - - protected static function tableName() - { - $class_name = strtolower(get_called_class()); - preg_match('/(\\\\)?(\\w+?)$/', $class_name, $matches); - return $matches[2]; - } - - protected static function getDatabaseInstance() - { - static::$db = Database::getInstance($_ENV["DATABASE_HOST"], $_ENV["DATABASE_NAME"], $_ENV["DATABASE_USERNAME"], $_ENV["DATABASE_PASSWORD"]); - - return static::$db; - } -} diff --git a/src/Router/Handler.php b/src/Router/Handler.php deleted file mode 100644 index 5e0fa74..0000000 --- a/src/Router/Handler.php +++ /dev/null @@ -1,36 +0,0 @@ -controller = $routeData[0]; - $this->function = $routeData[1]; - } - - public function handle($args = []) - { - if (class_exists($this->controller)) { - $controllerInstance = new $this->controller; - - if ($this->controller == Controller::class) { - if (file_exists(VIEW_DIR . $this->function)) { - call_user_func([$this->controller, "view"], [$this->function, $args]); - return; - } - } - - if (method_exists($controllerInstance, $this->function)) { - call_user_func([$controllerInstance, $this->function], $args); - } - } - } -} diff --git a/src/Router/Route.php b/src/Router/Route.php index d8476bf..007f376 100644 --- a/src/Router/Route.php +++ b/src/Router/Route.php @@ -1,42 +1,78 @@ -method = strtoupper($method); - $this->path = rtrim($path, '/'); - $this->handler = $handler; - } - - public function getMethod() - { - return $this->method; - } - - public function getPath() - { - return $this->path; - } - - public function getHandler() - { - return $this->handler; - } - - public function matchesMethod($method) - { - return strtoupper($method) === $this->method; - } - - public function matchesPath($path) - { - return rtrim($path, '/') === $this->path; - } -} +path = trim($path, '/'); // Sets the path, trimming any leading/trailing slashes + $this->controller = $controller; // Sets the controller name + $this->method = $method; // Sets the method name + } + + /** + * Gets the path of the route + * + * @return string + */ + public function getPath(): string + { + return $this->path; // Returns the path + } + + /** + * Checks if the given URL matches the route + * + * @param string $url + * + * @return bool + */ + public function matches(string $url): bool + { + $url_components = parse_url($url); // Parses the URL into components + $path = preg_replace('#:([\w]+)#', '([0-9]+)', $this->path); // Replaces placeholders in the path with regex to match numbers + $pathToMatch = '/^\/' . str_replace('/', '\/', $path) . '$/'; // Prepares the regex pattern to match the path + + if (preg_match($pathToMatch, $url_components['path'], $matches)) { // Checks if the URL path matches the pattern + $this->matches = $matches; // Stores the matches + if (isset($url_components['query'])) { + $this->matches[] = $url_components['query']; // Adds the query component to matches if present + } + return true; // Returns true if matches + } else { + return false; // Returns false if no matches + } + } + + /** + * Executes the controller method based on the route + * + * @return void + */ + public function execute(): void + { + $controller = new $this->controller(); // Creates a new instance of the controller + $method = $this->method; // Sets the method to be called + + if (isset($this->matches[2])) { + $controller->$method($this->matches[1], $this->matches[2]); // Calls the method with two parameters if present + } elseif (isset($this->matches[1])) { + $controller->$method($this->matches[1]); // Calls the method with one parameter if present + } else { + $controller->$method(); // Calls the method with no parameters + } + } +} diff --git a/src/Router/Router.php b/src/Router/Router.php index aa389a9..f03b0e5 100644 --- a/src/Router/Router.php +++ b/src/Router/Router.php @@ -1,87 +1,106 @@ -routes = []; - $this->routeRequest = $request; - } - - public function addRoute($route) - { - $this->routes[] = $route; - } - - public function matchRoute() - { - $routeRequest = $this->routeRequest; - - // Debugging: Affiche la route demandée et la méthode - echo 'Requested route: ' . $routeRequest[0] . ' with method: ' . $routeRequest[1] . PHP_EOL; - - foreach ($this->routes as $route) { - - // Debugging: Affiche chaque route vérifiée - echo 'Checking route: ' . $route->getPath() . PHP_EOL; - - if ($route->matchesMethod($routeRequest[1])) { - - if ($route->matchesPath($routeRequest[0])) { - - $handler = new Handler($route->getHandler()); - - $handler->handle(); - - return; - } - - $routeArray = explode("/", $route->getPath()); - $routeArray = array_filter($routeArray); - - $requestRouteArray = explode("/", $routeRequest[0]); - $requestRouteArray = array_filter($requestRouteArray); - - if (count($requestRouteArray) == count($routeArray)) { - - $parameters = []; - $pathRequestIsValid = false; - - foreach ($requestRouteArray as $key => $pathSegment) { - - if ($pathSegment != $routeArray[$key]) { - - if (preg_match('/{(.*?)}/', $routeArray[$key], $matches)) { - - $parameters[$matches[1]] = $pathSegment; - $pathRequestIsValid = true; - - } else { - - $pathRequestIsValid = false; - break; - - } - } - } - - if ($pathRequestIsValid) { - - $handler = new Handler($route->getHandler()); - - $handler->handle($parameters); - - return; - } - } - } - } - throw new Exception('Route not found'); - } -} +routes['GET'][$name] = $route; // Adds the route to the GET routes array + } + + /** + * Registers a POST route + * + * @param string $name + * @param Route $route + * + * @return void + */ + public function post(string $name, Route $route): void + { + $this->routes['POST'][$name] = $route; // Adds the route to the POST routes array + } + + /** + * Runs the router to match and execute the appropriate route + * + * @return void + */ + public function run(): void + { + foreach ($this->routes[$_SERVER['REQUEST_METHOD']] as $route) { // Iterates through the routes for the current request method + if ($route->matches($_SERVER['REQUEST_URI'])) { // Checks if the route matches the current URI + $route->execute(); // Executes the matched route + } + } + } + + /** + * Generates a URL for the given route name and parameters + * + * @param string $name + * @param array $params + * @param string $query + * + * @return string + */ + public function generateUrl(string $name, array $params = [], string $query = ''): string + { + foreach ($this->routes as $routes) { + if (isset($routes[$name])) { + $path = '/' . $routes[$name]->getPath(); // Gets the path for the route + + foreach ($params as $key => $param) { + $path = str_replace(':' . $key, $param, $path); // Replaces path parameters with actual values + } + + return $path . ($query ? '?' . $query : ''); // Returns the complete URL with query string if provided + } + } + throw new InvalidUrlException(); // Throws an exception if the route is not found + } + + /** + * Redirects to the given route name with parameters + * + * @param string $name + * @param array $params + * + * @return void + */ + public function redirect(string $name, array $params = []): void + { + $location = $this->generateUrl($name, $params); // Generates the URL for redirection + header("Location: {$location}"); // Sends the HTTP header to redirect + } +} diff --git a/src/views/404.php b/src/views/404.php deleted file mode 100644 index 563931f..0000000 --- a/src/views/404.php +++ /dev/null @@ -1,13 +0,0 @@ - -

404 Error

-'; -ob_start(); - -?> - -
- -
-
- - - - -
-
- -'; - -$exerciseData = $data["exercise"]; - -ob_start(); -?> - - -
-
- - Exercise: /fields"> -
-
- -
-
-
-

Fields

- - - - - - - - - - - -
LabelValue kind
- - /status=answering"> Complete and be ready for answers - -
-
-

New Field

-
/fields" accept-charset="UTF-8" method="post"> -
- - -
- -
- - -
- -
- -
-
-
-
-
- -'; - -ob_start(); -?> -
-
-

-

Exercise
Looper

-
-
- - - - - - - - - <?= $title ?> - - - - - - -
- -
- - - \ No newline at end of file diff --git a/src/views/TakeExercise.php b/src/views/TakeExercise.php deleted file mode 100644 index c204b82..0000000 --- a/src/views/TakeExercise.php +++ /dev/null @@ -1,35 +0,0 @@ -'; - -ob_start(); -?> - -
-
- - -
-
- -
- -
- - - - - - - - New Field - - - -
-
-

Exercise: de

-
-
-

Fields

- -
-
-

New Field

-
- - - - - - - -
- "; - echo "Label: " . htmlspecialchars($label) . "
"; - echo "Value Kind: " . htmlspecialchars($valueKind); -} -?> - -
-
- - diff --git a/templates/exercises/answering.php b/templates/exercises/answering.php new file mode 100644 index 0000000..ce41202 --- /dev/null +++ b/templates/exercises/answering.php @@ -0,0 +1,23 @@ + + +
    + + getState() == 'answering'): ?> +
  • +
    +
    getTitle() ?>
    + Take it +
    +
  • + + +
\ No newline at end of file diff --git a/templates/exercises/index.php b/templates/exercises/index.php new file mode 100644 index 0000000..0715c08 --- /dev/null +++ b/templates/exercises/index.php @@ -0,0 +1,125 @@ + + +
+
+

Building

+ + + + + + + + + + getState() == 'building'): ?> + + + + + + + +
Title
getTitle() ?> + getFields()): ?> + + + + + + + + + + +
+
+ +
+

Answering

+ + + + + + + + + + getState() == 'answering'): ?> + + + + + + + +
Title
getTitle() ?> + + + + + + +
+
+ +
+

Closed

+ + + + + + + + + + getState() == 'closed'): ?> + + + + + + + +
Title
getTitle() ?> + + + + + + +
+
+
+ \ No newline at end of file diff --git a/templates/exercises/new.php b/templates/exercises/new.php new file mode 100644 index 0000000..a23300e --- /dev/null +++ b/templates/exercises/new.php @@ -0,0 +1,17 @@ + +

+ +
+
+ + +
+
+
+ +
+
\ No newline at end of file diff --git a/templates/exercises/results.php b/templates/exercises/results.php new file mode 100644 index 0000000..6702a6e --- /dev/null +++ b/templates/exercises/results.php @@ -0,0 +1,60 @@ +generateUrl( + 'exercises_results', + ['exercise' => $params['exercise']->getId(),] + ) . '">' . $params['exercise']->getTitle() . ''; +$headerColor = 'results'; +?> + + + + + + + + + + + + + + + + getValue($field); ?> + + + + + +
Take $params['exercise']->getId(), "field" => $field->getId()] + ) ?>">getLabel() + ?>
+ $fulfillment->getId()] + ) ?>"> + getDate() ?> UTC + + + + + + + + + +
\ No newline at end of file diff --git a/templates/fields/edit.php b/templates/fields/edit.php new file mode 100644 index 0000000..0db3175 --- /dev/null +++ b/templates/fields/edit.php @@ -0,0 +1,57 @@ +getTitle()}"; +?> + +

Editing Field

+ +
+
+ + +
+
+ + +
+
+
+ +
+
\ No newline at end of file diff --git a/templates/fields/index.php b/templates/fields/index.php new file mode 100644 index 0000000..1b10ad1 --- /dev/null +++ b/templates/fields/index.php @@ -0,0 +1,81 @@ +getTitle()}"; +?> + +
+
+

Fields

+ + + + + + + + + + getFields() as $field) : ?> + + + + + + + + +
LabelValue kind
getLabel() ?>getValueKind() ?> + + + + + + +
+ getFields()): ?> + + Complete and be ready for answers + + +
+
+

New Field

+
+
+ + +
+
+ + +
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/templates/fields/results.php b/templates/fields/results.php new file mode 100644 index 0000000..ce38861 --- /dev/null +++ b/templates/fields/results.php @@ -0,0 +1,36 @@ +generateUrl( + 'exercises_results', + ['exercise' => $params['exercise']->getId()] + ) . '">' . $params['exercise']->getTitle() . ''; +$headerColor = 'results'; +?> +

getLabel() ?>

+ + + + + + + + + + + + + + + +
TakeContent
+ $params['exercise']->getId(), "fulfillment" => $fulfillment->getId(),] + ) ?>"> + getDate() ?> UTC + + + getValue($params['field']); ?> +
\ No newline at end of file diff --git a/templates/fulfillments/edit.php b/templates/fulfillments/edit.php new file mode 100644 index 0000000..321cf6e --- /dev/null +++ b/templates/fulfillments/edit.php @@ -0,0 +1,41 @@ + + +

Your take

+

Bookmark this page, it's yours. You'll be able to come back later to finish.

+
+ + + getValueKind() == "single_line"): ?> + + + getValueKind() == "single_line_list"): ?> + + + getValueKind() == "multi_line"): ?> + + + + +
+ +
+
\ No newline at end of file diff --git a/templates/fulfillments/new.php b/templates/fulfillments/new.php new file mode 100644 index 0000000..0aa582a --- /dev/null +++ b/templates/fulfillments/new.php @@ -0,0 +1,35 @@ + + +

Your take

+

If you'd like to come back later to finish, simply submit it with blanks

+
+ getFields() as $field) : ?> + + getValueKind() == "single_line") : ?> + + + getValueKind() == "single_line_list") : ?> + + + getValueKind() == "multi_line") : ?> + + + + +
+ +
+
\ No newline at end of file diff --git a/templates/fulfillments/results.php b/templates/fulfillments/results.php new file mode 100644 index 0000000..f403225 --- /dev/null +++ b/templates/fulfillments/results.php @@ -0,0 +1,18 @@ +generateUrl( + 'exercises_results', + ['exercise' => $params['exercise']->getId()] + ) . '">' . $params['exercise']->getTitle() . ''; +$headerColor = 'results'; +?> + +

getDate() ?>

+
+ +
getLabel() ?>
+
getValue($field) ?>
+ +
\ No newline at end of file diff --git a/templates/layout.php b/templates/layout.php new file mode 100644 index 0000000..9bc09a1 --- /dev/null +++ b/templates/layout.php @@ -0,0 +1,42 @@ + + + + + ExerciseLooper + + + + + + + + + + + +
+
+

+

Exercise
Looper

+
+
+ +
+
+ + + +
+
+ + +
+ ExerciseLooper + +
+
+ + \ No newline at end of file diff --git a/templates/layoutold.php b/templates/layoutold.php new file mode 100644 index 0000000..9bc09a1 --- /dev/null +++ b/templates/layoutold.php @@ -0,0 +1,42 @@ + + + + + ExerciseLooper + + + + + + + + + + + +
+
+

+

Exercise
Looper

+
+
+ +
+
+ + + +
+
+ + +
+ ExerciseLooper + +
+
+ + \ No newline at end of file diff --git a/templates/site/index.php b/templates/site/index.php new file mode 100644 index 0000000..9031a16 --- /dev/null +++ b/templates/site/index.php @@ -0,0 +1,17 @@ + +
\ No newline at end of file diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php deleted file mode 100644 index 729c0c5..0000000 --- a/tests/DatabaseTest.php +++ /dev/null @@ -1,22 +0,0 @@ -load(); - -final class DatabaseTest extends TestCase -{ - public function testDatabaseNotNull(): void - { - $database = Database::getInstance($_ENV["DATABASE_HOST"], $_ENV["DATABASE_NAME"], $_ENV["DATABASE_USERNAME"], $_ENV["DATABASE_PASSWORD"]); - - $this->assertNotNull($database); - } -} From 3d788297cb08296cdc83c6337b8b308bf2f937ab Mon Sep 17 00:00:00 2001 From: Nazmi Uksmajli Date: Fri, 20 Dec 2024 23:56:17 +0100 Subject: [PATCH 55/56] change port to data base --- public/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.php b/public/index.php index 30509e9..f5a4d5f 100644 --- a/public/index.php +++ b/public/index.php @@ -14,7 +14,7 @@ define('TEMPLATES_DIR', dirname(__DIR__) . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR); DBConnection::setUp( - 'mysql:host=127.0.0.1;port=3308;dbname=looper;charset=utf8mb4', + 'mysql:host=127.0.0.1;port=3306;dbname=looper;charset=utf8mb4', 'root', 'root_password' ); From 5e70ee2b5b40ee7bd6057aa3c76d139f5371bb05 Mon Sep 17 00:00:00 2001 From: Nazmi Uksmajli Date: Sat, 21 Dec 2024 00:01:53 +0100 Subject: [PATCH 56/56] Update README.md --- README.md | 100 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index c466851..777e147 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,80 @@ # MAW11 - Exercice Looper -## Subject +## Subject -Create a copy of this web application : [Exercice Looper] -(https://maw-looper.mycpnv.ch) +Create a copy of this web application: [Exercice Looper](https://maw-looper.mycpnv.ch/) -## Deadline +## Deadlines -intérmédiaire `31.10.2024` -Final : `21.12.2024` +- **Intermediate:** 31.10.2024 +- **Final:** 21.12.2024 -## Requirement +## Requirements -I must use HTML5, CSS, PHP and POO - -## Installation - -### local installation (for development) - -### Prerequisites: - -- Have a `php` and a database connection service installed and be able to reach them in a shell (test ´php -v´ to verify) Successful verification if the commande is recogized. -- Have - -### Procedure: - -1. **Get the repository** from github (clone or `.zip`download) (exemple clonee in a shell in the ´C:/Users//documents/Github/´folder) +You must use: +- HTML5 +- CSS +- PHP +- Object-Oriented Programming (OOP) +## Installation - - - - -create database dans modelisation -composer installe dsns la racine du proijet -ce qui me reste a faire ce qui foncrionne -php -S localhost:4444 -t public - +### Local Installation (for Development) + +#### Prerequisites: + +1. Have PHP and a database connection service installed. +2. Verify the installation in a shell: + ```bash + php -v + ``` + Successful verification occurs if the command is recognized. + +#### Procedure: + +1. Clone the repository from GitHub or download it as a ZIP file: + ```bash + git clone + ``` + Example: Clone it into the folder `C:/Users//Documents/Github/`. + +2. Set up the database: + - Run the provided script `create_db.sql` located in the `modelisation` folder to create the necessary database. + - Database connection details are: + ```php + DBConnection::setUp( + 'mysql:host=127.0.0.1;port=3308;dbname=looper;charset=utf8mb4', + 'root', + 'root_password' + ); + ``` + +3. Install dependencies: + ```bash + composer install + ``` + Run this command in the root of the project directory. + +4. Start the development server: + ```bash + php -S localhost:4444 -t public + ``` + +## Progress + +### Completed Features: + +- Exercise creation works. +- Field creation works. +- Fields can be viewed within an exercise. + +### Known Issues: + +- Exercises cannot be viewed. +- Fields cannot be viewed individually. +- Exercises cannot be filled. +- Responses cannot be viewed. +- CSS is broken. +- No best practices like dotenv or similar are followed. +- Code documentation is minimal. \ No newline at end of file
- -

New Exercise

-