Aller au contenu

Exercices Todolist

Exercice - Création d’une TodoList#

Première étape, on crée une route (URL) /todos qui appellera la méthode index de la classe Todos (controller)

./index.php

<?php
// charge le controller
require './controllers/Todos.php';
// creation de la route /todos
Flight::route('/todos', [new Todos(), 'index']);
?>

Cette méthode index crée un tableau de todos vide (pour l’instant), et envoie ce tableau à la vue todos

./controllers/Todos.php

<?php
class Todos {
  public function index () {
    $todos = [];
    Flight::render('todos', ['todos' => $todos]);
  }
}
?>

Notre vue todos se contente d’afficher le tableau des todos (s’il y en a), et créé également un formulaire pour en ajouter de nouveaux.

./views/todos.php

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <h1>Todos</h1>
  <?php if (sizeof($todos) > 0) { ?>
    <ul>
      <?php foreach ($todos as $todo) { ?>
        <li><?= $todo ?></li>
      <?php } ?>
    </ul>
  <?php } else { ?>
    <p>No todos yet</p>
  <?php } ?>
  <form action="/todos/add" method="post">
    <label>
      New todo
      <input type="text" name="todo">
    </label>
    <input type="submit" value="Add todo">
  </form>
</body>
</html>

Ajouter un todo#

On note que lors de la validation du formulaire, les données sont envoyées à l’URL /todos/add en mode POST. Créons alors cette route, qui appelle la méthode add de notre controller:

./index.php

<?php
// charge le controller
require './controllers/Todos.php';
// creation des routes
Flight::route('/todos', [new Todos(), 'index']);
Flight::route('POST /todos/add', [new Todos(), 'add']);
?>

Dans notre controller, cette méthode doit alors créer un nouveau todo, et sauvegarder cette information. C’est à ce moment là que les models de l’architecture MVC nous sont utiles. Un model est une abstraction de notre modèle de données, par exemple une base de données. C’est ce que l’on va utiliser ici.

Tout d’abord, créez une base de données todos, qui contient une table todos avec 2 champs : id (int) et text (text ou varchar). Puis, configurer l’extension PDO pour Flight dans index.php (il faut aussi activer l’extension PDO dans votre php.ini)

./index.php

<?php
Flight::register('db', 'PDO', array('mysql:host=localhost;dbname=todos', 'root', 'root'));
?>

Notre modèle va alors utiliser cette configuration, puis exécuter une requête SQL

./models/Todo.php

<?php
class Todo {
  public function __construct () {
    $this->db = Flight::db();
  }
  public function add ($todo) {
    $sql = 'INSERT INTO todos (id, text) VALUES (NULL, :todo)';
    $stmt = $this->db->prepare($sql);
    $stmt->bindParam(':todo', $todo);
    $stmt->execute();
  }
}
?>

Dans notre controller, on peut alors appeler cette méthode add() en lui passant la valeur reçue en POST si elle existe. Et on redirige vers l’URL /todos, car on ne souhaite pas rester sur /todos/add.

./controllers/Todos.php

<?php
require './models/Todo.php';

class Todos {
  public function __construct () {
    $this->Todo = new Todo();
  }
  public function index () {
    $todos = [];
    Flight::render('todos', ['todos' => $todos]);
  }
  public function add () {
    if (isset($_POST['todo'])) {
      // appel du modèle
      $this->Todo->add($_POST['todo']);
    }
    Flight::redirect('/todos');
  }
}
?>

Maintenant que l’on a des todos, il faut également penser à mettre à jour notre méthode index qui doit les afficher. Pour cela, on crée d’abord une nouvelle méthode getAll() dans notre modèle.

./models/Todo.php

<?php
class Todo {
  ...
  public function getAll () {
    $sql = 'SELECT id, text FROM todos';
    $result = $this->db->query($sql, PDO::FETCH_ASSOC);
    $data = [];
    if ($result) {
      foreach($result as $r) {
        $data[] = $r;
      }
    } else {
      echo 'SQL error';
    }
    return $data;
  }

}
?>

Que l’on utilise depuis notre controller:

./controllers/Todos.php

<?php
class Todos {
  public function index () {
    $todos = $this->Todo->getAll();
    Flight::render('todos', ['todos' => $todos]);
  }
}
?>

Et on pense à mettre à jour la vue, car un todo est composé de deux champs: id et text

...
  <h1>Todos</h1>
  <?php if (sizeof($todos) > 0) { ?>
    <ul>
      <?php foreach ($todos as $todo) { ?>
        <li><?= $todo['text'] ?></li>
      <?php } ?>
    </ul>
  <?php } else { ?>
    <p>No todos yet</p>
  <?php } ?>
...

Supprimer un todo#

Pour supprimer un todo:

  • dans la vue: création d’un lien vers /todos/remove/{id}

./views/todos.php

...
  <h1>Todos</h1>
  <?php if (sizeof($todos) > 0) { ?>
    <ul>
      <?php foreach ($todos as $todo) { ?>
        <li><?= $todo['text'] ?> <a href="/todos/remove/<?= $todo['id'] ?>">X</a></li>
      <?php } ?>
    </ul>
  <?php } else { ?>
    <p>No todos yet</p>
  <?php } ?>
...
  • création d’une nouvelle route /todos/remove/{id}

./index.php

<?php
// charge le controller
require './controllers/Todos.php';
// creation des routes
Flight::route('GET /todos', [new Todos(), 'index']);
Flight::route('POST /todos/add', [new Todos(), 'add']);
Flight::route('GET /todos/remove/@id', [new Todos(), 'remove']);
?>
  • dans le controller, création d’une méthode remove
    • appelle le modèle
    • redirige vers /todos

./controllers/Todos.php

<?php
require './models/Todo.php';

class Todos {
  ...
  public function remove ($id) {
    $this->Todo->remove($id);
    Flight::redirect('/todos');
  }
}
?>
  • dans le modèle, création d’une méthode remove
    • supprimer le todo de la base de données

./models/Todo.php

<?php
class Todo {
  ...
  public function remove ($id) {
    $sql = 'DELETE FROM todos WHERE todos.id = :id';
    $stmt = $this->db->prepare($sql);
    $stmt->bindParam(':id', $id);
    $stmt->execute();
  }
}
?>