diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..1c08f96
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,31 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the
+// README at: https://github.com/devcontainers/templates/tree/main/src/php
+{
+ "name": "PHP",
+ // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
+ "image": "mcr.microsoft.com/devcontainers/php:1-8.2-bullseye",
+
+ // Features to add to the dev container. More info: https://containers.dev/features.
+ // "features": {},
+
+ // Configure tool-specific properties.
+ // "customizations": {},
+
+ // Use 'forwardPorts' to make a list of ports inside the container available locally.
+ "forwardPorts": [
+ 8080
+ ],
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "rexshi.phpdoc-comment-vscode-plugin"
+ ]
+ }
+ }
+
+ // Use 'postCreateCommand' to run commands after the container is created.
+ // "postCreateCommand": "sudo chmod a+x \"$(pwd)\" && sudo rm -rf /var/www/html && sudo ln -s \"$(pwd)\" /var/www/html"
+
+ // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
+ // "remoteUser": "root"
+}
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..f33a02c
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,12 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for more information:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+# https://containers.dev/guide/dependabot
+
+version: 2
+updates:
+ - package-ecosystem: "devcontainers"
+ directory: "/"
+ schedule:
+ interval: weekly
diff --git a/docs/looper.png b/docs/looper_erd.png
similarity index 100%
rename from docs/looper.png
rename to docs/looper_erd.png
diff --git a/docs/looper_mcd.jpg b/docs/looper_mcd.jpg
new file mode 100644
index 0000000..cda7abc
Binary files /dev/null and b/docs/looper_mcd.jpg differ
diff --git a/docs/looper_mld.jpg b/docs/looper_mld.jpg
new file mode 100644
index 0000000..d0328c6
Binary files /dev/null and b/docs/looper_mld.jpg differ
diff --git a/public/index.php b/public/index.php
index 3150de9..bc2bbb3 100644
--- a/public/index.php
+++ b/public/index.php
@@ -1,5 +1,11 @@
getId() . '/fields');
}
+ /**
+ * This method deletes an exercise identified by `$id`. It only allows deletion if the exercise
+ * is in the `Building` or `Closed` state.
+ *
+ * @param int $id The ID of the exercise to be deleted.
+ * @return void
+ */
public function deleteExercise(int $id)
{
$exercise = new Exercise($id);
@@ -25,6 +48,12 @@ public function deleteExercise(int $id)
header('Location: /exercises');
}
+ /**
+ * This method changes the state of an exercise identified by `$id`.
+ *
+ * @param int $id The ID of the exercise whose state is to be changed.
+ * @return void
+ */
public function changeStateOfExercise(int $id)
{
if (!isset($_GET['exercise']['status'])) {
diff --git a/src/controllers/field_controller.php b/src/controllers/field_controller.php
index 460ae4a..c20dd2f 100644
--- a/src/controllers/field_controller.php
+++ b/src/controllers/field_controller.php
@@ -1,9 +1,27 @@
getId() . '/fulfillments/' . $fulfillment->getId() . '/edit');
}
+ /**
+ * Edit a fulfillment
+ * This method handles the editing of an existing fulfillment, identified by `$fulfillment_id`,
+ * for a specific exercise, identified by `$exercise_id`.
+ *
+ * @param int $exercise_id The ID of the exercise associated with the fulfillment.
+ * @param int $fulfillment_id The ID of the fulfillment that is being edited.
+ * @return void This function updates the fulfillment and redirects, without returning a value.
+ */
public function editFulfillment(int $exercise_id, int $fulfillment_id)
{
if (!isset($_POST['fulfillment']['answers_attributes'])) {
diff --git a/src/controllers/navigation.php b/src/controllers/navigation.php
index 4e0709a..3f77884 100644
--- a/src/controllers/navigation.php
+++ b/src/controllers/navigation.php
@@ -1,25 +1,59 @@
db = new PDO('pgsql:host=' . $host . ';port=' . $port . ';dbname=' . $dbname, $postgres_user, $postgres_password);
}
+ /**
+ * select (all select, returning at the end of an insert) basicly all that is returning somethings
+ *
+ * @param string $squery the Sql query (default: '')
+ * @param array $args (default: [])
+ * @return array[array] the result of the query
+ */
public function select(string $squery, array $args = [])
{
if ($args) {
@@ -22,6 +34,13 @@ public function select(string $squery, array $args = [])
return $statement->fetchAll();
}
+ /**
+ * modify (insert, update) basicly all that isn't returning anything
+ *
+ * @param string $squery The Sql query (default: '')
+ * @param array $args (default: [])
+ * @return void
+ */
public function modify(string $squery, array $args = [])
{
if ($args) {
diff --git a/src/models/databases_connectors/postgresql/postgresql_access.php b/src/models/databases_connectors/postgresql/postgresql_access.php
index b537f18..41b28dd 100644
--- a/src/models/databases_connectors/postgresql/postgresql_access.php
+++ b/src/models/databases_connectors/postgresql/postgresql_access.php
@@ -1,12 +1,32 @@
postgresql = new Postgresql($host, $port, $dbname, $postgres_user, $postgres_password);
diff --git a/src/models/exceptions/looper_exception.php b/src/models/exceptions/looper_exception.php
index 66040bf..960c24a 100644
--- a/src/models/exceptions/looper_exception.php
+++ b/src/models/exceptions/looper_exception.php
@@ -1,10 +1,32 @@
httpErrorMessage = $httpErrorMessage;
}
+ /**
+ * Get the HTTP return code.
+ *
+ * This method returns the HTTP status code that represents the type of error encountered.
+ *
+ * @return int The HTTP error return code.
+ */
public function getReturnCode(): int
{
return $this->httpReturnCode;
}
+ /**
+ * Get the HTTP error message.
+ *
+ * This method returns the HTTP error message, which can provide a more detailed explanation
+ * of the error that occurred, suitable for logging or debugging.
+ *
+ * @return string The HTTP error message.
+ */
public function getErrorMessage(): string
{
return $this->httpErrorMessage;
diff --git a/src/models/exercise.php b/src/models/exercise.php
index efb22c3..30ae0b9 100644
--- a/src/models/exercise.php
+++ b/src/models/exercise.php
@@ -1,9 +1,18 @@
database_access = (new DatabasesChoose())->getDatabase();
@@ -26,22 +45,43 @@ public function __construct(int $id)
$this->id = $id;
}
- public static function create($title): self
+ /**
+ * This is a static method to create an exercise
+ *
+ * @param string $title the title of the exercise
+ * @return self the created exercise
+ */
+ public static function create(string $title): self
{
$database_access = (new DatabasesChoose())->getDatabase();
return new self($database_access->createExercise($title));
}
+ /**
+ * Get the id of the exercise
+ *
+ * @return int the id of the exercise
+ */
public function getId()
{
return $this->id;
}
+ /**
+ * Get the title of the exercise
+ *
+ * @return string the title of the exercise
+ */
public function getTitle()
{
return $this->database_access->getExerciseTitle($this->id);
}
+ /**
+ * get exercise list of field in exercise
+ *
+ * @return array[Field] the list of field in exercise
+ */
public function getFields(): array
{
$array_field = [];
@@ -51,6 +91,14 @@ public function getFields(): array
return $array_field;
}
+ /**
+ * Create a field in the exercise
+ *
+ * @param string $label the label of the field
+ * @param Kind $kind the kind of the field
+ * @throws ExerciseNotInBuildingStatus if the exercise is not in building status
+ * @return Field the created field
+ */
public function createField(string $label, Kind $kind): Field
{
if ($this->getStatus() != Status::Building) {
@@ -59,21 +107,44 @@ public function createField(string $label, Kind $kind): Field
return new Field($this->database_access->createField($this->id, $label, $kind->value));
}
+ /**
+ * Is field in exercise ?
+ *
+ * @param Field $field the field to check
+ * @return bool true if the field is in the exercise, false otherwise
+ */
public function isFieldInExercise(Field $field): bool
{
return $this->database_access->isFieldInExercise($this->id, $field->getId());
}
+ /**
+ * Check if a fulfillment is part of the exercise.
+ *
+ * @param Fulfillment $fulfillment The fulfillment object to check.
+ * @return bool Returns true if the fulfillment is part of the exercise, false otherwise.
+ */
public function isFulfillmentInExercise(Fulfillment $fulfillment): bool
{
return $this->database_access->isFulfillmentInExercise($this->id, $fulfillment->getId());
}
+ /**
+ * This method deletes the exercise
+ *
+ * @return void the deleted exercise
+ */
public function delete()
{
$this->database_access->deleteExercise($this->id);
}
+ /**
+ * Get all static exercises by status (if status is null, get all exercises)
+ *
+ * @param Status $status the status of the exercise default null
+ * @return array[Exercise] the list of exercises by status
+ */
public static function getExercises(Status $status = null)
{
$database_access = (new DatabasesChoose())->getDatabase();
@@ -93,21 +164,43 @@ public static function getExercises(Status $status = null)
return $exercises;
}
+ /**
+ * get status of an exercise (Building, Answering, Closed)
+ *
+ * @return Status the status of the exercise (Building, Answering, Closed)
+ */
public function getStatus(): Status
{
return Status::from($this->database_access->getExerciseStatus($this->id));
}
+ /**
+ * Set the exercise as a status (Building, Answering, Closed)
+ *
+ * @param Status $status the status to set the exercise as (Building, Answering, Closed)
+ * @return void
+ */
public function setExerciseAs(Status $status)
{
$this->database_access->setExerciseStatus($this->id, $status->value);
}
+ /**
+ * get number of field in exercise
+ *
+ * @return int the number of field in exercise
+ */
public function getFieldsCount(): int
{
return $this->database_access->getFieldsCount($this->id);
}
+ /**
+ * create a fulfillment in exercise when the exercise is in answering status
+ *
+ * @throws ExerciseNotInAnsweringStatus if the exercise is not in answering status
+ * @return Fulfillment the created fulfillment
+ */
public function createFulfillment(): Fulfillment
{
if ($this->getStatus() != Status::Answering) {
@@ -116,7 +209,12 @@ public function createFulfillment(): Fulfillment
return new Fulfillment($this->database_access->createFulfillment($this->id));
}
- public function getFulfillments()
+ /**
+ * Get a list of fulfillments in exercise (All fulfillments in exercise)
+ *
+ * @return array[Fulfillment] the list of fulfillments in exercise
+ */
+ public function getFulfillments(): array
{
$fulfillments = [];
foreach ($this->database_access->getFulfillments($this->id) as $field) {
@@ -126,8 +224,19 @@ public function getFulfillments()
}
}
+/**
+ * This exception is thrown when an exercise is not found
+ */
class ExerciseNotFoundException extends LooperException
{
+ /**
+ * This is the constructor of the exception
+ *
+ * @param string $message the message of the exception
+ * @param int $code the code of the exception
+ * @param Exception|null $previous the previous exception
+ * @return void
+ */
public function __construct($message = 'The exercise does not exist', $code = 0, Exception $previous = null)
{
// Make sure everything is assigned properly
@@ -135,16 +244,38 @@ public function __construct($message = 'The exercise does not exist', $code = 0,
}
}
+/**
+ * This exception is thrown when an exercise is not in building status
+ */
class ExerciseNotInBuildingStatus extends LooperException
{
+ /**
+ * This is the constructor of the exception
+ *
+ * @param string $message the message of the exception
+ * @param int $code the code of the exception
+ * @param Exception|null $previous the previous exception
+ * @return void
+ */
public function __construct($message = 'The Exercise is not in building status', $code = 0, Exception|null $previous = null)
{
parent::__construct(400, 'The Exercise is not in building status', $message, $code, $previous);
}
}
+/**
+ * This exception is thrown when an exercise is not in answering status
+ */
class ExerciseNotInAnsweringStatus extends LooperException
{
+ /**
+ * This is the constructor of the exception
+ *
+ * @param string $message the message of the exception
+ * @param int $code the code of the exception
+ * @param Exception|null $previous the previous exception
+ * @return void
+ */
public function __construct($message = 'The Exercise is not in answering status', $code = 0, Exception|null $previous = null)
{
parent::__construct(400, 'The Exercise is not in answering status', $message, $code, $previous);
diff --git a/src/models/field.php b/src/models/field.php
index aef72f2..a7b5e83 100644
--- a/src/models/field.php
+++ b/src/models/field.php
@@ -1,8 +1,17 @@
id = $id;
@@ -26,16 +45,31 @@ public function __construct(int $id)
}
}
+ /**
+ * Returns the ID of a field.
+ *
+ * @return int The unique identifier of the object.
+ */
public function getId(): int
{
return $this->id;
}
+ /**
+ * Get the label of the field
+ *
+ * @return string the label of the field
+ */
public function getLabel(): string
{
return $this->database_access->getFieldLabel($this->id);
}
+ /**
+ * Get the kind of the field
+ *
+ * @return Kind the kind of the field
+ */
public function getKind(): Kind
{
switch ($this->database_access->getFieldKind($this->id)) {
@@ -48,6 +82,12 @@ public function getKind(): Kind
}
}
+ /**
+ * Set the label of the field
+ *
+ * @param string $label the new label of the field
+ * @return void
+ */
public function setLabel(string $label): void
{
if ($this->getExercise()->getStatus() != Status::Building) {
@@ -56,6 +96,12 @@ public function setLabel(string $label): void
$this->database_access->setFieldLabel($this->id, $label);
}
+ /**
+ * Set the kind of the field
+ *
+ * @param Kind $kind the new kind of the field
+ * @return void
+ */
public function setKind(Kind $kind): void
{
if ($this->getExercise()->getStatus() != Status::Building) {
@@ -64,7 +110,12 @@ public function setKind(Kind $kind): void
$this->database_access->setFieldKind($this->id, $kind->value);
}
- public function delete()
+ /**
+ * Delete the field
+ *
+ * @return void
+ */
+ public function delete(): void
{
if ($this->getExercise()->getStatus() != Status::Building) {
throw new ExerciseNotInBuildingStatus();
@@ -72,14 +123,30 @@ public function delete()
$this->database_access->deleteField($this->id);
}
- public function getExercise()
+ /**
+ * Get the exercise of the field
+ *
+ * @return Exercise
+ */
+ public function getExercise(): Exercise
{
return new Exercise($this->database_access->getExerciseByFieldId($this->id));
}
}
+/**
+ * This exception is thrown when a field is not found
+ */
class FieldNotFoundException extends LooperException
{
+ /**
+ * The constructor of the FieldNotFoundException class
+ *
+ * @param string $message the message of the exception default 'The field does not exist'
+ * @param int $code the code of the exception default 0
+ * @param Exception $previous the previous exception default null
+ * @return void
+ */
public function __construct($message = 'The field does not exist', $code = 0, Exception $previous = null)
{
parent::__construct(404, 'Field not found', $message, $code, $previous);
diff --git a/src/models/fulfillment.php b/src/models/fulfillment.php
index df69440..8905725 100644
--- a/src/models/fulfillment.php
+++ b/src/models/fulfillment.php
@@ -1,15 +1,32 @@
id = $id;
@@ -21,16 +38,33 @@ public function __construct(int $id)
}
}
+ /**
+ * Get the id of the fulfillment
+ *
+ * @return int the id of the fulfillment
+ */
public function getId()
{
return $this->id;
}
+ /**
+ * get fulfillment creation date
+ *
+ * @return int the timestamp of the creation date
+ */
public function getTimestamp()
{
return $this->database_access->getFulfillmentTimestamp($this->id);
}
+ /**
+ * create fulfillment fields data
+ *
+ * @param Field $field the field to create
+ * @param string $body the body of the field
+ * @return FulfillmentField the created field
+ */
public function createFields(Field $field, string $body)
{
if ($this->getExercise()->getStatus() != Status::Answering) {
@@ -42,6 +76,11 @@ public function createFields(Field $field, string $body)
return new FulfillmentField($field->getId(), $this->id);
}
+ /**
+ * get a fields of the fulfillment
+ *
+ * @return array[FulfillmentField $field] the fields of the fulfillment
+ */
public function getFields()
{
$fulfillment = [];
@@ -53,14 +92,30 @@ public function getFields()
return $fulfillment;
}
+ /**
+ * get exercise of the fulfillment
+ *
+ * @return Exercise the exercise of the fulfillment
+ */
public function getExercise()
{
return new Exercise($this->database_access->getExerciseByFulfillmentId($this->id));
}
}
+/**
+ * This exception is thrown when the fulfillment does not exist
+ */
class FulfillmentNotFoundException extends LooperException
{
+ /**
+ * constructor of the exception
+ *
+ * @param string $message the message of the exception Default: 'The fulfillment does not exist'
+ * @param int $code the code of the exception Default: 0
+ * @param Exception $previous the previous exception Default: null
+ * @return void
+ */
public function __construct($message = 'The fulfillment does not exist', $code = 0, Exception $previous = null)
{
parent::__construct(404, 'fulfillment not found', $message, $code, $previous);
diff --git a/src/models/fulfillment_field.php b/src/models/fulfillment_field.php
index c50e879..0f9c037 100644
--- a/src/models/fulfillment_field.php
+++ b/src/models/fulfillment_field.php
@@ -1,11 +1,29 @@
fulfillment_id;
}
+ /**
+ * Get the body of the fulfillment
+ *
+ * @return string the body of the fulfillment
+ */
public function getBody()
{
return $this->database_access->getFulfillmentBody(parent::getId(), $this->fulfillment_id);
}
+ /**
+ * Set the body of the fulfillment
+ *
+ * @param string $body the body of the fulfillment
+ * @return void
+ */
public function setBody(string $body)
{
if ($this->getExercise()->getStatus() != Status::Answering) {
diff --git a/src/models/router.php b/src/models/router.php
index a0a417e..efa2279 100644
--- a/src/models/router.php
+++ b/src/models/router.php
@@ -1,5 +1,14 @@
controller_entry as $class => $routes) {
@@ -64,6 +80,7 @@ public function run($request_method, $request_uri)
private function callRoute($request_method, $request_uri, $class, $routes)
{
+ // Iterate on all the route and check if it's the same route then launch the right controller on this route
foreach ($routes[$request_method] as $route => $method) {
$same_route = $this->isSameRoute($route, request_uri: $request_uri);
diff --git a/src/views/create_an_exercise.php b/src/views/create_an_exercise.php
index 2582529..060bb4b 100644
--- a/src/views/create_an_exercise.php
+++ b/src/views/create_an_exercise.php
@@ -1,4 +1,9 @@
getKind();
diff --git a/src/views/errors/error.php b/src/views/errors/error.php
index 22f8463..c94e0e8 100644
--- a/src/views/errors/error.php
+++ b/src/views/errors/error.php
@@ -1,4 +1,10 @@
=$exercise->getTitle()?>
for="=$field->getId()?>">=$field->getLabel()?>
getBody() : '' ?>
getKind()->value == 0): ?>
-
-
diff --git a/src/views/take_an_exercise.php b/src/views/take_an_exercise.php
index 32b9eeb..9b7276f 100644
--- a/src/views/take_an_exercise.php
+++ b/src/views/take_an_exercise.php
@@ -1,4 +1,10 @@
+