Creating a list of tasks in symfony

After we are set, we should create a table with a list of our unfinished tasks. In the previous chapter, when we created a database model we didn’t add a column to flag the finished task, so we will do this now to demonstrate how the database model should be amended while the project is growing.

Modify the src/AppBundle/Entity/Task.php file and add the following:

    // ... 
    /**
     * @ORMColumn(type="datetime")
     */
    private $created_at;

    // add the code below, to recognize finished tasks:
    /**
     * @ORMColumn(type="boolean")
     */
    private $finished = false;

Update the database and generate the following new methods:

$ php app/console doctrine:schema:update --force
$ php app/console doctrine:generate:entities --no-backup AppBundle

Now we need to modify our data fixtures. In the previous chapter, we created an example tag fixture, now we need to add an example task. Add the following code to the file:

<?php

// file: src/AppBundle/DataFixtures/ORM/LoadTaskData.php
namespace AppBundleDataFixturesORM;

use AppBundleEntityTask;
use DoctrineCommonDataFixturesFixtureInterface;
use DoctrineCommonPersistenceObjectManager;

class LoadTaskData implements FixtureInterface
{
    public function load(ObjectManager $manager)
    {
        $obj = new Task();
        $obj->setName('Example task for today');
        $obj->setNotes('This task is created as a fixture and has no real meaning. It's only demonstration, sorry to disappoint you');
        $obj->setCreatedAt(new DateTime());
        $obj->setFinished(false);

        $manager->persist($obj);
        $manager->flush();
    }
}

Load the new data as follows:

$ php app/console doctrine:fixtures:load

The preceding code will simply load a new task to the database.

Now we need to modify the controller to read the tasks list from the database. Modify src/AppBundle/Controller/TaskController.php as follows:

    public function listAction(Request $request)
    {
        $em = $this->getDoctrine()->getManager();

        $tasks = $em->getRepository('AppBundle:Task')
            ->createQueryBuilder('t')
            ->where('t.finished = :finished')
            ->orderBy('t.due_date', 'ASC')
            ->setParameter('finished', false)
            ->getQuery()
            ->getResult();

        return $this->render('task/list.html.twig', ['tasks' => $tasks]);
    }

What we have changed here is that we used entity manager and created an SQL query using query builder. In this query, we have requested for all entities that have the finished status set to false. We also requested our data to be sorted out by the due_date column. The result of the database query is then passed to the template.

Finally, we need to implement the view. Edit app/Resources/views/task/list.html.twig so it looks like the following:

{% extends '::base.html.twig' %}

{% block body %}
    <h2 class="ui aligned header">Your tasks list</h2>
    <p class="ui aligned">Below you will find your unfinished tasks</p>

    <table class="ui blue table">
        <thead>
            <tr>
                <th>Name</th>
                <th>Notes</th>
                <th>Tags</th>
                <th>Due</th>
            </tr>
        </thead>
        <tbody>
            {% for task in tasks %}
            <tr>
                <td>{{ task.name }}</td>
                <td>{{ task.notes }}</td>
                <td>
                    {% for tag in task.tags %}
                    <span class="ui tag label">{{ tag.name }}</span>
                    {% endfor %}
                </td>
                <td>
                    {{ task.dueDate is empty ? "" : task.dueDate|date('Y-m-d') }}
                </td>
            </tr>
            {% else %}
                <tr>
                    <td colspan="4" class="center aligned">
                            <h2>There are no unfinished tasks at the moment. Good Job!</h2>
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
{% endblock %}

The most interesting part of the code is highlighted. As you may have observed, the for…end loop can also have a default value in case the if loop is empty and provided with a simple else keyword. This is very convenient when you need to provide an elegant default value for situations where the provided array is empty.

The next interesting thing is the way Twig handles the objects and arrays. When reaching for task.name in a PHP object, Twig, in fact, is looking for the getName or isName method, and when such a method exists, it will be used. If you provide an array and not an object, Twig will look for the name key and will use its value.

Another interesting thing happens around the dueDate method. Doctrine ORM for every date and time column transforms data using the standard PHP DateTime object. As we cannot just output the object, we need to use the Twig filter. With Twig we are using filter very often, it’s one of the basic ways to modify a provided variable. In our case, we are checking whether the date provided to our object is null, and if not, we will format it using the PHP DateTime method format syntax to display date in the YYYY-MM-DD format.

When you finish everything, you will probably have results similar to this one:

Creating a list of tasks

You may have noticed that both the tags and due fields are empty. It’s because we didn’t connect our tags and task in fixtures, and we left the due date field empty.

In order to add tags to the task object, we need to slightly modify our fixtures. First, modify the src/AppBundle/DataFixtures/ORM/LoadTagData.php file:

<?php

namespace AppBundleDataFixturesORM;

use DoctrineCommonDataFixturesAbstractFixture;
use DoctrineCommonDataFixturesOrderedFixtureInterface;
use DoctrineCommonPersistenceObjectManager;
use AppBundleEntityTag;

class LoadTagData extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        // ... code written in previous chapter
        $manager->persist($obj);
        
        // adding object as reference, to use it later
        $this->addReference('tag:'.$tag, $obj); 
        // ...
    }

    public function getOrder()
    {
        return 1;
    }
}

The highlighted updates to the code add the Tag objects to the reference table and allows to fetch the object when it’s needed. In our case, our references will be called tag:company, tag:home, or tag:important. When we are adding references, the order of objects loaded is important, so we are also implementing OrderedFixtureInterface and a simple method to inform Doctrine in what order it should load the files.

Similar updates need to be added to src/AppBundle/DataFixtures/ORM/LoadTaskData.php:

use DoctrineCommonDataFixturesAbstractFixture;
use DoctrineCommonDataFixturesOrderedFixtureInterface;
use AppBundleEntityTask;
use DoctrineCommonPersistenceObjectManager;

class LoadTaskData extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        // ...
        $obj->setDueDate(new DateTime());
        $obj->addTag($this->getReference('tag:home'));
        $obj->addTag($this->getReference('tag:important'));

        $manager->persist($obj);
        $manager->flush();
    }

    public function getOrder()
    {
        return 10;
    }
}

In this class, we are adding a method to mark the load order, but we are also getting tags as referenced objects. Now we need to reload the data as follows:

$ php app/console doctrine:fixtures:load

Now we are done.

Note

As we have used only one filter in this chapter, we will use more of them in the following chapters. If you are interested and want to learn more about them, go to the Twig documentation at http://twig.sensiolabs.org/doc/filters/index.html.

About the author

Deven Rathore

I'm Deven Rathore, a multidisciplinary & self-taught designer with 3 years of experience. I'm passionate about technology, music, coffee, traveling and everything visually stimulating. Constantly learning and experiencing new things.

Pin It on Pinterest

Shares