Lesson Weekend

Now that we have our methods working, we can adapt our Silex app to handle this many to many relationship. Let's start by modifying our index.html.twig for our home page.

views/index.html.twig
<html>
<head>
    <title>To Do List</title>
</head>
<body>
    <h1>Welcome to To Do List</h1>

    <h3><a href="/categories">Add or view a category</a></h3>
    <h3><a href="/tasks">Add or view a task</a></h3>

</body>
</html>

In this view, we are linking to pages where users can add or view categories and tasks.

Here are the routes in our app.php file.

app/app.php
$app->get("/", function() use ($app) {
    return $app['twig']->render('index.html.twig', array('categories' => Category::getAll(), 'tasks' => Task::getAll()));
});

$app->get("/tasks", function() use ($app) {
    return $app['twig']->render('tasks.html.twig', array('tasks' => Task::getAll()));
});

$app->get("/categories", function() use ($app) {
    return $app['twig']->render('categories.html.twig', array('categories' => Category::getAll()));
});

Now let's update the pages that we linked to in our index.html.twig file.

views/categories.html.twig
<html>
<head>
    <title>To Do List</title>
</head>
<body>
    <h1>Categories</h1>

    {% if categories is not empty %}
        <p>Here are all your categories:</p>
        <ul>
            {% for category in categories %}
                <li><a href="/categories/{{ category.getId }}">{{ category.getName }}</a></li>
            {% endfor %}
        </ul>
    {% endif %}

    <form action='/categories' method='post'>
        <label for='name'>Category name</label>
        <input id='name' name='name' type='text'>

        <button type='submit'>Add category</button>
    </form>
    <form action='/delete_categories' method='post'>
        <button type='submit'>Clear</button>
    </form>

    <p><a href='/'>Home</a></p>
</body>
</html>
views/tasks.html.twig
<html>
<head>
    <title>To Do List</title>
</head>
<body>
    <h1>Tasks</h1>

    {% if tasks is not empty %}
        <p>Here are all your tasks:</p>
        <ul>
            {% for task in tasks %}
                <li><a href="/tasks/{{ task.getId }}">{{ task.getDescription }}</a></li>
            {% endfor %}
        </ul>
    {% endif %}

    <form action='/tasks' method='post'>
        <label for='description'>Task description:</label>
        <input id='description' name='description' type='text'>

        <button type='submit'>Add task</button>
    </form>

    <form action='/delete_tasks' method='post'>
        <button type='submit'>Clear</button>
    </form>

    <p><a href='/'>Home</a></p>
</body>
</html>

In both we have a form which submits to a post("/tasks"... or post("/categories"... route in our app.php file. Then if there have been any tasks or categories added to the database, we will list them with a link to their individual page with the route get("/tasks/{id}"... or get("/categories/{id}"....

Here are these routes in our app.php file:

app/app.php
$app->post("/tasks", function() use ($app) {
    $description = $_POST['description'];
    $task = new Task($description);
    $task->save();
    return $app['twig']->render('tasks.html.twig', array('tasks' => Task::getAll()));
});

$app->get("/tasks/{id}", function($id) use ($app) {
    $task = Task::find($id);
    return $app['twig']->render('task.html.twig', array('task' => $task, 'categories' => $task->getCategories(), 'all_categories' => Category::getAll()));
});

$app->post("/categories", function() use ($app) {
    $category = new Category($_POST['name']);
    $category->save();
    return $app['twig']->render('categories.html.twig', array('categories' => Category::getAll()));
});

$app->get("/categories/{id}", function($id) use ($app) {
    $category = Category::find($id);
    return $app['twig']->render('category.html.twig', array('category' => $category, 'tasks' => $category->getTasks(), 'all_tasks' => Task::getAll()));
});

So now we can create our individual category and task pages. Let's call them category.html.twig and task.html.twig.

views/category.html.twig
<html>
<head>
    <title>To Do List</title>
</head>
<body>
    <h1>{{ category.getName }}</h1>

    {% if tasks is not empty %}
        <p>Here are the tasks for this category:</p>
        <ul>
            {% for task in tasks %}
                <li>{{ task.getDescription }}</li>
            {% endfor %}
        </ul>
    {% endif %}

    <h4>Add a task to this category:</h4>

    <form action='/add_tasks' method='post'>
        <input id="category_id" name="category_id" type="hidden" value="{{ category.getId }}">
        <label for="task_id">Select a task</label>
        <select id='task_id' name='task_id' type='text'>
          {% for task in all_tasks %}
            <option value="{{ task.getId }}"> {{ task.getDescription }} </option>
          {% endfor %}
        </select>
        <button type='submit'>Add task</button>
    </form>

    <p><a href="/categories/{{ category.getId }}/edit">Edit this category</a></p>

    <p><a href='/'>Home</a></p>
</body>
</html>
views/task.html.twig
<html>
<head>
    <title>To Do List</title>
</head>
<body>
    <h1>{{ task.getDescription }}</h1>

    {% if categories is not empty %}
        <p>Here are the categories for this task:</p>
        <ul>
            {% for category in categories %}
                <li>{{ category.getName }}</li>
            {% endfor %}
        </ul>
    {% endif %}

    <h4>Add a category to this task:</h4>

    <form action='/add_categories' method='post'>
        <input id="task_id" name="task_id" type="hidden" value="{{ task.getId }}">
        <label for="category_id">Select a category</label>
        <select id='category_id' name='category_id' type='text'>
          {% for category in all_categories %}
            <option value="{{ category.getId }}"> {{ category.getName }} </option>
          {% endfor %}
        </select>
        <button type='submit'>Add category</button>
    </form>

    <p><a href='/'>Home</a></p>
</body>
</html>

These are the pages where we will actually have our users make the connections between the two tables. Since these two pages are very similar, I will just talk about the category.html.twig page and you can make the same connections to the task.html.twig page.

So at the top of the page we have included a form, whose action attribute is set to "/add_tasks" with the post method. This form will be used to connect tasks to the particular category we are looking at. We start this form with a hidden field to provide us with the particular category's ID, so we can use it in our add_tasks route.We are also using a select tag in this form, which we haven't used before, so here is how this works:

  1. We loop through all of the tasks in Task::getAll and for each one, we create a dropdown with <option value="{{ task.getId }}"> {{ task.getDescription }} </option>.
  2. The name attribute "task_id" will be the same for each dropdown and it will allow us to submit that particular task's ID as task_id.

Let's build out the routes for these two forms.

app/app.php
$app->post("/add_tasks", function() use ($app) {
    $category = Category::find($_POST['category_id']);
    $task = Task::find($_POST['task_id']);
    $category->addTask($task);
    return $app['twig']->render('category.html.twig', array('category' => $category, 'categories' => Category::getAll(), 'tasks' => $category->getTasks(), 'all_tasks' => Task::getAll()));
});

$app->post("/add_categories", function() use ($app) {
    $category = Category::find($_POST['category_id']);
    $task = Task::find($_POST['task_id']);
    $task->addCategory($category);
    return $app['twig']->render('task.html.twig', array('task' => $task, 'tasks' => Task::getAll(), 'categories' => $task->getCategories(), 'all_categories' => Category::getAll()));
});

Again, I will just focus on the get("/add_categories"... route, but the basic information applies to the get("/add_tasks"... route too.

So we find the correct Task object and the correct Category object from the form inputs. Then we run our addCategory() method on our $task object, and then we render our task.html.twig file again with the variables task, tasks, categories and all_categories.

Alright, let's fire up the server and see if this thing works!

In order to add a task to a category, we need to submit a form. Our form should route to /add_tasks if we are going to use the addTask method in that route in our app.php file.

Because we will need to find the correct category on which to add the tasks, we need to pass the ID of the category into the form as a hidden field. For example:

<input id="task_id" name="task_id" type="hidden" value="{{ task.getId }}">

To use the <select> tag in in a form:

<select id='category_id' name='category_id' type='text'>
    {% for category in all_categories %}
        <option value="{{ category.getId }}"> {{ category.getName }} </option>
    {% endfor %}
</select>

Each drop down option should be enclosed in its own <option> tag and the value attribute should be set to the ID of that particular option, but the the name attribute of the <select> tag should be set to whatever you want the value of the <option> tag to be once the form is submitted. In this case, category_id.