简体   繁体   中英

Looping through values in a twig template and finding existing ones (Symfony2)

I've been working on a Symfony2 project for some time now, and though I think I'm executing this particular part correctly, it's not quite working how I need it to.

I am displaying data on a twig template relating to tasks and the categories they reside in. This uses a join table, as it is a ManyToMany relationship as a task can be assigned to multiple categories. Luckily, there are only 3 categories so it's not too complex.

Currently, I am looping through the categories that are assigned to each task like so... in this example, 'task' is the data retrieved from the task entity where 'category' is an array collection mapped to the join table:

{% for cat in task.category %}
    <input type="checkbox" checked name="cat[]" /><label>{{ cat.name }}</label>
{% endfor %}

As you can see there are a list of checkboxes, and the categories assigned to the task are to be checked. The above code only show the categories that are assigned.

To get all categories, I did:

{% for category in categories %}
    ...
{% endfor %}

Where "categories" is the findAll() result from the Category entity.

Basically, what I need to do is loop through all the available categories but only check those that are assigned. I tried creating an array which could be cross referenced:

{% for cat in stage.category %}
{% set arr = {} %}
{% for category in categories %}
   {% set arr = arr|merge({ 'id': cat.id}) %}
{% endfor %}
{% if category.id in arr %}
   ...
{% endif %}
{% endfor %}

But the array only ever has one value in it, as I think it's just overwriting it. So, if the task is only assigned to a single category this is great, but unfortunately it could be assigned to several.

What I need is a way to loop through the categories and run a check to see if it's assigned to the current task.

I also tried eliminating the array option and used this:

{% for cat in stage.category %}
    {% for category in categories %}
        {% if cat.id == category.id %}
            <input id="category_{{ category.id }}" checked type="checkbox" name="category[]" />
            <label style="font-weight:100" for="category_{{ category.id }}">{{ category.name }}</label><br />
        {% else %}
            <input id="category_{{ category.id }}" type="checkbox" name="category[]" />
            <label style="font-weight:100" for="category_{{ category.id }}">{{ category.name }}</label><br />
        {% endif %}
    {% endfor %}
{% endfor %}

But this just displays all categories twice, one checked and one unchecked.

Am I approaching this the right way? Or is there a better, easier way to do this?

Hope someone can point me in the right direction.

EDIT:

Thanks for your answers - the problem is, some of the data isn't directly accessible in order to perform the solutions offered, but I was thinking quite a lot about this and came up with this:

// get all the active categories for each task
foreach($tasks as $task) {
    foreach($task->getCategory() as $task_category) {
        $task_array[$task->getId()]['active'][$task_category->getId()] = $task_category->getName();
    }
}

// get all available categories
foreach($categories as $category) {
    $category_array[$category->getId()] = $category->getName();
}

// find out which categories are not in the active array and add them as inactive
foreach($task_array as $key => $task_cat) {
    foreach($task_cat as $new_key => $new_task) {
        foreach ($category_array as $cat_key => $cat_name) {
            if (!array_key_exists($cat_key, $new_task)) {
                $task_array[$key]['inactive'][$cat_key] = $cat_name;
            }
        }
    }
}

And then, I have a $task_array which looks like this:

Array
(
    [1] => Array
        (
            [active] => Array
                (
                    [1] => Standard Wire Guide
                    [2] => Standard Wire Guide with Additional Features
                )

            [inactive] => Array
                (
                    [3] => Stator
                )

        )

    [2] => Array
        (
            [active] => Array
                (
                    [1] => Standard Wire Guide
                    [2] => Standard Wire Guide with Additional Features
                )

            [inactive] => Array
                (
                    [3] => Stator
                )

        )

....
);

And now I have a manageable array I can work with in the twig file.

Thanks for all your suggestions too :)

Give this a try. I think it's what you're looking for. It works for me.

The trick is to put the conditional around just the "checked" attribute.

//
// In our controller. 
//
$categories =
[
  ['id' => 1, 'name' => 'catOne'],
  ['id' => 2, 'name' => 'catTwo'],
  ['id' => 3, 'name' => 'catThree']
];
$tasks =
[
  ['id' => 1, 'name' => 'Some task', 'cats' => [ 1, 3 ]],
  ['id' => 2, 'name' => 'Another task', 'cats' => [2]]
];
$vars = ['categories' => $categories, 'tasks' => $tasks];
Return $this->render( 'SiteBundle:Debug:debug.html.twig', $vars );

{# 
 # In the template.
 #}
{% for task in tasks %}
  {{ task.name }}:
  {% for category in categories %}
      <input id="category_{{ category.id }}"
             type="checkbox"
             {% if category.id in task.cats %}checked{% endif %}
             name="category[]"/>
      <label style="font-weight:100"
             for="category_{{ category.id }}">{{ category.name }}</label>
  {% endfor %}<br>
{% endfor %}

I suggest the following:

Add the following method to Catorgy :

namespace MyBundle\Entity;

class Catorgy {
    ...
    public function isAssigned()
    {
        return (count($this->getTasks()) > 0);
    }
    ...
}

If your many-to-many relationship is correctly defined, the getTasks method either already exist or shouldn't be difficult to add.

Then, in Twig, methods named MyClass::isSomething() can be accessed with just MyClass.something :

{% for cat in catorgies %}
    <input id="category_{{ cat.id }}" type="checkbox" name="category[]"{% if cat.assigned %} checked{% endif %}/>
    <label style="font-weight:100" for="category_{{ cat.id }}">{{ cat.name }}</label><br />
{% endfor %}

It's the simplest way I can think of.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM