Steps to reproduce:

1) Add the widget to any node
2) Add the related nodes and save
3) Edit the node and rearrange the related nodes
4) Save the node and you will find the the order has not been changed

Any help would be much appreciated!

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

nagy.balint’s picture

Version: 7.x-1.0-beta1 » 7.x-1.x-dev

This seems to be a valid use case. However it is not so easy to implement.

A possible solution could be to store the weight for each item in the field instance. And then that means that reordering the items in a specific field would only change the order in that specific field, and would not effect other field instances where the relation appears. Also there is a question about how this will play in views, or how we can use the order in views.

Patches are welcome.

Changing version here as well.

gvincke’s picture

Having the same problem with relation_add-7.x-1.2
The relation_add field offers a "weight" option that seems to be not recorded in database anywhere. So it's really confusing in terms of UI.
Personally I add a field "weight" to my relations to sort the endpoints with views but then the relation_add field sort them in the added order when I edit. Not user friendly...
Having this real sort option in edit and see mode would be very convenient.
Thanks !

mr.york’s picture

Category: Bug report » Feature request
Issue summary: View changes
alcroito’s picture

FileSize
3.29 KB

I'm not sure if this a related issue or not (in case I misunderstood the intents of the issue).

My use case is that I have added a relation_add field to an entity, and set it to unlimited items. When I add a few items, and try to reorder them, the order isn't saved.

The attached patch against 7.x-1.2 adds an rid field to the field table, which lets us run a sort on weights when loading the field data.

In views I then add the delta field to sort the relations, but it causes some duplicates which I then fix by doing a views query alter hook, to add additional constraints on the join clause.

This is a dirty approach, but I haven't thought of a good relationship handler that would be able to fix this, because it depends on multiple tables at once (in my case the field_data_{relation_type} table, user table (where the field is attached) and the relation table).

Hopefully this is a starting point for this issue.

potassiumchloride’s picture

Category: Feature request » Support request

I have the same issue, and I think this is a support request, not a feature request.

If the relations are draggable in the node/edit then it is safe to assume that most users will expect any changes to the order be saved.

alcroito’s picture

Category: Support request » Feature request

It is true that people will expect that, but the actual code does not account for this, so you can consider it a bug, or a feature depending how you look at it.

The patch I attached should let you save the order, although the order will not be used anywhere at the moment.

potassiumchloride’s picture

Thanks for that patch. In my use case, this issue may make Relation Add unusable because I would have to put up warnings to the client that say: "Reordering these items by dragging them does not affect the display order." And, the display order is very important to them. Yes, I could add a field for order #, and they would have to fill that in, but it doesn't change the fact that the intuitive method - dragging the items into an order - would have to be ignored by users with different skills and tolerances.

potassiumchloride’s picture

I have tried the method described by @gvincke. I added a "Display Order" field to my relation types (to serve as the weight), and I try to use that in views to order the related nodes on the display side. This method works fine when A <--> B (order=1) and A <-->C (order=2). On A, I see the related nodes listed as:

B (order=1)
C (order=2)

But if B <--> D (with order 3) or if B or C are related to any other nodes with the same relation type, the display on the A page breaks. Then it shows:

B (order=1)
C (order=2)
B (order=3)

Testing reveals that setting the node <--> relation Relationship to "avoid node duplication" gets rid of the duplicate but then it does not display or utilize the Order field anymore. So, I can have ordered nodes with duplicates or unordered nodes with no duplicates.

I would try the patch and go that route as a next step, but I don't know enough to be able to understand @Placinta:

In views I then add the delta field to sort the relations, but it causes some duplicates which I then fix by doing a views query alter hook, to add additional constraints on the join clause.

Please help.

alcroito’s picture

Honestly I can't help you much but post the code I used, and as I warned, it is a very dirty approach to solving the problem, and the code is specific to my use case.

I had a simple relation type that connects a user object (profile) and a node object (course), so the relation contains data for each profile and course. With the attached patch I was able to add delta values to the field tables, and with the below code I modified the views queries to filter on the relation id's to eliminate the duplicates.

/**
 * Implements hook_views_query_alter().
 *
 * For user course profile view, we have to add an additional clause to the join condition on the field_ table.
 * To eliminate duplicates.
 *
 * @param $view
 * @param $query
 */
function pw_fr_user_views_views_query_alter(&$view, &$query) {
  if (in_array($view->name, array('user_course_profile'))) {
    if (
    isset($query->table_queue['users_relation__field_data_field_course_profiles']['join'])
    && isset($query->table_queue['relation']['alias'])
    ) {
      // Get the relation alias name.
      $relation_alias = $query->table_queue['relation']['alias'];

      // Add the extra condition to eliminate duplicates.
      $query->table_queue['users_relation__field_data_field_course_profiles']['join']->extra[] = array(
        'field' => 'field_course_profiles_rid',
        'right_expression' => "$relation_alias.rid",
      );

      // Changes the views_join object to our own one, that supports custom expressions in the extra array.
      $query->table_queue['users_relation__field_data_field_course_profiles']['join'] =
        pw_fr_user_views_recreate_views_join($query->table_queue['users_relation__field_data_field_course_profiles']['join']);
    }
  }

  if (in_array($view->name, array('enrolled_in_courses'))) {
    if (
      isset($query->table_queue['users_relation__field_data_field_course_profiles']['join'])
    ) {
      // Get the relation alias name.
      $relation_alias = $query->table_queue['relation_node']['alias'];

      // Add the extra condition to eliminate duplicates.
      $query->table_queue['users_relation__field_data_field_course_profiles']['join']->extra[] = array(
        'field' => 'field_course_profiles_rid',
        'right_expression' => "$relation_alias.rid",
      );

      // Changes the views_join object to our own one, that supports custom expressions in the extra array.
      $query->table_queue['users_relation__field_data_field_course_profiles']['join'] =
        pw_fr_user_views_recreate_views_join($query->table_queue['users_relation__field_data_field_course_profiles']['join']);
    }
  }
}

/**
 * Given a views_join object, recreates one using pw_views_join.
 *
 * @param $join
 * @return pw_views_join
 */
function pw_fr_user_views_recreate_views_join($join) {
  $new_join = new pw_views_join();

  $keys = array(
    'table',
    'left_table',
    'left_field',
    'field',
    'extra',
    'type',
    'definition',
    'extra_type',
    'adjusted',
  );

  foreach ($keys as $key) {
    if (isset($join->$key)) {
      $new_join->$key = $join->$key;
    }
  }

  //$new_join->construct();
  return $new_join;
}

/**
 * Class pw_views_join
 *
 * Supports custom expressions in the extra conditions array.
 * To use, set the left field and right_expression keys.
 */
class pw_views_join extends views_join {

  /**
   * Overridden method to support adding custom expression to extras array, not only place holder values.
   *
   * @param $select_query
   * @param $table
   * @param $view_query
   */
  function build_join($select_query, $table, $view_query) {
    if (empty($this->definition['table formula'])) {
      $right_table = $this->table;
    }
    else {
      $right_table = $this->definition['table formula'];
    }

    if ($this->left_table) {
      $left = $view_query->get_table_info($this->left_table);
      $left_field = "$left[alias].$this->left_field";
    }
    else {
      // This can be used if left_field is a formula or something. It should be used only *very* rarely.
      $left_field = $this->left_field;
    }

    $condition = "$left_field = $table[alias].$this->field";
    $arguments = array();

    // Tack on the extra.
    if (isset($this->extra)) {
      if (is_array($this->extra)) {
        $extras = array();
        foreach ($this->extra as $info) {
          $extra = '';
          // Figure out the table name. Remember, only use aliases provided
          // if at all possible.
          $join_table = '';
          if (!array_key_exists('table', $info)) {
            $join_table = $table['alias'] . '.';
          }
          elseif (isset($info['table'])) {
            // If we're aware of a table alias for this table, use the table
            // alias instead of the table name.
            if (isset($left) && $left['table'] == $info['table']) {
              $join_table = $left['alias'] . '.';
            }
            else {
              $join_table = $info['table'] . '.';
            }
          }

          // Convert a single-valued array of values to the single-value case,
          // and transform from IN() notation to = notation
          if (isset($info['value']) && is_array($info['value']) && count($info['value']) == 1) {
            if (empty($info['operator'])) {
              $operator = '=';
            }
            else {
              $operator = $info['operator'] == 'NOT IN' ? '!=' : '=';
            }
            $info['value'] = array_shift($info['value']);
          }

          if (isset($info['value']) && is_array($info['value'])) {
            // With an array of values, we need multiple placeholders and the
            // 'IN' operator is implicit.
            foreach ($info['value'] as $value) {
              $placeholder_i = ':views_join_condition_' . $select_query->nextPlaceholder();
              $arguments[$placeholder_i] = $value;
            }

            $operator = !empty($info['operator']) ? $info['operator'] : 'IN';
            $placeholder = '( ' . implode(', ', array_keys($arguments)) . ' )';
          }
          // Make sure that if right expression is set, don't add any placeholders or arguments.
          elseif (!isset($info['right_expression'])) {
            // With a single value, the '=' operator is implicit.
            $operator = !empty($info['operator']) ? $info['operator'] : '=';
            $placeholder = ':views_join_condition_' . $select_query->nextPlaceholder();
            $arguments[$placeholder] = $info['value'];
          }

          // If right expression is set use it as an extra, otherwise revert to the default operator placeholder behavior.
          if (isset($info['right_expression'])) {
            $right_expression = $info['right_expression'];
            $extras[] = "$join_table$info[field] = $right_expression";
          } else {
            $extras[] = "$join_table$info[field] $operator $placeholder";
          }
        }

        if ($extras) {
          if (count($extras) == 1) {
            $condition .= ' AND ' . array_shift($extras);
          }
          else {
            $condition .= ' AND (' . implode(' ' . $this->extra_type . ' ', $extras) . ')';
          }
        }
      }
      elseif ($this->extra && is_string($this->extra)) {
        $condition .= " AND ($this->extra)";
      }
    }

    $select_query->addJoin($this->type, $right_table, $table['alias'], $condition, $arguments);
  }
}
adam-delaney’s picture

I would agree that if the weight and draggable handles are available in the widget the order should be preserved and available to use in other modules like views.

Cale Bierman’s picture

Version: 7.x-1.x-dev » 7.x-1.2

I changed the version as the patch from #4 no longer applies cleanly to the dev release

doitDave’s picture

+1 for #10, please consider this a severe issue (also because the module is bitterly needed for serious work with relation at all). :)

osopolar’s picture

Maybe the delta from the field relation_add could be stored in relation endpoint.

See #1304196: How should we do weighted Relations? #21

We have delta and r_index. The delta is not used for anything in relation but this is going to change in d8 (probably). The r_index isn't a thing there but instead delta is used to describe what r_index does now in addition to usual things it does in field api.

Or the weight patch from #20 could be used

Chewie’s picture

Version: 7.x-1.2 » 7.x-1.5
Status: Active » Needs review
FileSize
4.89 KB

Don't agree that weight of relation should be stored in relation entity base table. Weight is the attribute of entity where relation have connection
Updated patch for 1.5 version

Status: Needs review » Needs work

The last submitted patch, 14: relation_add_weights-1993738-14.patch, failed testing.

Status: Needs work » Needs review

The last submitted patch, 4: relation_add_weights.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 14: relation_add_weights-1993738-14.patch, failed testing.

Chewie’s picture

Chewie’s picture

FileSize
2.55 KB
azinck’s picture

#20 does not apply against HEAD, nor does it apply against 1.5.

Also: I'm rather unclear about what #20 is trying to do. Where is the delta info going to be stored, Chewie?

sdstyles’s picture

Version: 7.x-1.5 » 7.x-1.x-dev
Status: Needs work » Needs review
FileSize
2.73 KB
2.81 KB

This patch is fixing weight order issue on latest 7.x-1.x-dev branch, you won't be able to apply it against 7.x-1.6 or other previous versions.

To patch latest stable version 7.x-1.6 use 7.x-1.6-relation-add-weight-1993738-22.patch

arturs.v’s picture

I'm testing the 1.6 version of the patch and while it does work with new nodes I'm facing an issue with existing data. The query does not seem to account for items that does not have the weight value set. Here is a modified patch that attempts to get around that problem. In ideal world the hook_query_TAG_alter could maybe be adjusted to do the same thing.

arturs.v’s picture

Status: Needs review » Needs work
arturs.v’s picture

I realised that I have made the patch to work with a very specific field in mind. This updated patch works for all fields.