Hi,

I have two tables, and only one of them is meant to be a base one.
I was hoping you could have a look at what I did, and maybe tell me if it looks OK...

So, here we go.

friendlist_relations;
+--------------------------+---------------------+------+-----+---------+----------------+
| Field                    | Type                | Null | Key | Default | Extra          |
+--------------------------+---------------------+------+-----+---------+----------------+
| requester_id             | int(10) unsigned    | NO   | PRI | NULL    |                | 
| requestee_id             | int(10) unsigned    | NO   | PRI | NULL    |                | 
| rtid                     | int(10) unsigned    | NO   | PRI | NULL    |                | 
| message                  | varchar(255)        | NO   |     |         |                | 
| create_time              | int(10) unsigned    | NO   |     | NULL    |                | 
| disregarded_time         | int(10) unsigned    | NO   |     | NULL    |                | 
| twoways_established_flag | tinyint(3) unsigned | NO   |     | NULL    |                | 
| rid_views                | int(10) unsigned    | NO   | PRI | NULL    | auto_increment | 
+--------------------------+---------------------+------+-----+---------+----------------+


friendlist_relation_types;
+--------+---------------------+------+-----+---------+----------------+
| Field  | Type                | Null | Key | Default | Extra          |
+--------+---------------------+------+-----+---------+----------------+
| rtid   | int(10) unsigned    | NO   | PRI | NULL    | auto_increment | 
| name   | varchar(255)        | NO   | UNI |         |                | 
| name_p | varchar(255)        | NO   |     |         |                | 
| oneway | tinyint(3) unsigned | NO   |     | 0       |                | 
| active | int(10) unsigned    | NO   |     | NULL    |                | 
+--------+---------------------+------+-----+---------+----------------+

Here is the code of the hook:


function friendlist_api_views_data(){

  $data = array(

    'friendlist_relations' => array(

      'table' => array(
        'group' => t('Relations'),
        'base'  => array(
          'field' => 'rid_views',
          'title' => t('Relations between users'),
          'help'  => t("Relations between users in the system. They can be one-way or two ways"),
        ),
      ),

      'requester_id' => array(
        'title' => t('Requesting user'),
        'help' => t('The user who made the request'),
        'relationship' => array(
          'base' => 'users',
          'field' => 'uid',
          'handler' => 'views_handler_relationship',
          'label' => t('Requester'),
        ),
      ),

      'requestee_id' => array(
        'title' => t('Requestee user'),
        'help' => t('The user who received the request'),
        'relationship' => array(
          'base' => 'users',
          'field' => 'uid',
          'handler' => 'views_handler_relationship',
          'label' => t('Requestee'),
        ),

      ),

      'rtid' => array(
        'title' => t('The relation type ID'),
        'help' => t('The type of relationship'),
      ),

      'message' => array(
        'title' => t('Message sent'),
        'help' => t('Message sent by the requesting user'),
      ),

      'create_time' => array(
        'title' => t('Date of the request'),
        'help' => t('When the friendship request was sent'),
        'field' => array(
          'handler' => 'views_handler_field_date',
          'click sortable' => TRUE,
        ),
        'sort' => array(
          'handler' => 'views_handler_sort_date',
        ),
        'filter' => array(
          'handler' => 'views_handler_filter_date',
        ),
      ),

      'disregarded_time' => array(
        'title' => t('When the issue was disregarded'),
        'help' => t('When the friendship request was denied'),
        'field' => array(
          'handler' => 'views_handler_field_date',
          'click sortable' => TRUE,
        ),
        'sort' => array(
          'handler' => 'views_handler_sort_date',
        ),
        'filter' => array(
          'handler' => 'views_handler_filter_boolean_operator',
        ),
      ),

      'twoways_established_flag' => array(
        'title' => t('Two way relationship established'),
        'help' => t('This relationship is mutual'),
        'field' => array(
         'handler' => 'views_handler_field_boolean',
         'click sortable' => TRUE,
        ),
        'filter' => array(
          'handler' => 'views_handler_filter_boolean_operator',
          'label' => t('Established'),
        ),
        'sort' => array(
          'handler' => 'views_handler_sort',
        ),
      ),
      'rid_views' => array(
        'title' => t('The relation ID'),
        'help' => t('This is only here to make sure that Views works'),
      ),

      /*'action_link' = array(
        'field' => array(
          'title' => t('Action'),
          'help' => t('Provide the basic action to do for that user'),
          'handler' => 'views_handler_field_friendlist_action',
        )
      )*/
      
    ),

    'friendlist_relation_types' => array(
      'table' => array(
        'group' => t('Relationship types'),

        /* No need to have this as a base, it will just confuse me and users
          'base' => array(
          'field' => 'rtid',
          'title' => t('Relation type'),
          'help' => t('A type of relation in FriendList'),
          'weight' => -10,
        ),*/

        'join' => array(
          'friendlist_relations' => array(
            'left_field' => 'rtid',
            'field' => 'rtid',
          ),
        ),
      ),


      'rtid' => array(
        'title' => t('Relation ID'),
        'help' => t('The Relation ID'),
      ),

      'name' => array(
        'title' => t('Name of the relation type'),
        'help' => t('Name of the relation type'),
      ),
      'name_p' => array(
        'title' => t('Plural form of the relation type'),
        'help' => t('Plural form of the relation type'),
      ),
      'oneway' => array(
        'title' => t('One way relation type'),
        'help' => t('Relation types that do not require confirmation'),
        'field' => array(
         'handler' => 'views_handler_field_boolean',
         'click sortable' => TRUE,
        ),
        'filter' => array(
          'handler' => 'views_handler_filter_boolean_operator',
          'label' => t('One way'),
        ),
        'sort' => array(
          'handler' => 'views_handler_sort',
        ),
      ),

      'active' => array(
        'title' => t('Active'),
        'help' => t('Active relation types'),
        'field' => array(
         'handler' => 'views_handler_field_boolean',
         'click sortable' => TRUE,
        ),
        'filter' => array(
          'handler' => 'views_handler_filter_boolean_operator',
          'label' => t('One way'),
        ),
        'sort' => array(
          'handler' => 'views_handler_sort',
        ),
      ),
    ),
  );

  return $data;

}

Does this look right? I know it _works_. However... I am not entirely sure it is 100% OK.

Thanks a million!

Merc.

Comments

mercmobily’s picture

Hi,

I think I've actually completed the Views integration...
A final comment from you Views Gurus would make my day as a Drupal developer :-D

Thanks a lot!

(views_handler_field_friendlist_link was the result of _tons_ of trial&error... I am not even sure how I managed!)


function friendlist_api_views_data(){

  $data = array(

    'friendlist_relations' => array(

      'table' => array(
        'group' => t('Relations'),
        'base'  => array(
          'field' => 'rid_views',
          'title' => t('Relations between users'),
          'help'  => t("Relations between users in the system. They can be one-way or two ways"),
        ),
      ),

      'requester_id' => array(
        'title' => t('Requesting user'),
        'help' => t('The user who made the request'),
        'relationship' => array(
          'base' => 'users',
          'field' => 'uid',
          'handler' => 'views_handler_relationship',
          'label' => t('Requester'),
        ),
      ),

      'requestee_id' => array(
        'title' => t('Requestee user'),
        'help' => t('The user who received the request'),
        'relationship' => array(
          'base' => 'users',
          'field' => 'uid',
          'handler' => 'views_handler_relationship',
          'label' => t('Requestee'),
        ),

      ),

      'rtid' => array(
        'title' => t('The relation type ID'),
        'help' => t('The type of relationship'),
      ),

      'message' => array(
        'title' => t('Message sent'),
        'help' => t('Message sent by the requesting user'),
      ),

      'create_time' => array(
        'title' => t('Date of the request'),
        'help' => t('When the friendship request was sent'),
        'field' => array(
          'handler' => 'views_handler_field_date',
          'click sortable' => TRUE,
        ),
        'sort' => array(
          'handler' => 'views_handler_sort_date',
        ),
        'filter' => array(
          'handler' => 'views_handler_filter_date',
        ),
      ),

      'disregarded_time' => array(
        'title' => t('When the issue was disregarded'),
        'help' => t('When the friendship request was denied'),
        'field' => array(
          'handler' => 'views_handler_field_date',
          'click sortable' => TRUE,
        ),
        'sort' => array(
          'handler' => 'views_handler_sort_date',
        ),
        'filter' => array(
          'handler' => 'views_handler_filter_boolean_operator',
        ),
      ),
      'requestee_id_current' => array(
        'real field' => 'requestee_id',
        'title' => t('Requestee user is the currently logged in user'),
        'help' => t('Filter results so that only the current user is the requestee'),
        'filter' => array(
          'handler' => 'views_handler_filter_user_current',
        ),
      ),

      'requester_id_current' => array(
        'real field' => 'requester_id',
        'title' => t('Requester user is the currently logged in user'),
        'help' => t('Filter results so that only the current user is the requester'),
        'filter' => array(
          'handler' => 'views_handler_filter_user_current',
        ),
      ),

      'twoways_established_flag' => array(
        'title' => t('Two way relationship established'),
        'help' => t('This relationship is mutual'),
        'field' => array(
         'handler' => 'views_handler_field_boolean',
         'click sortable' => TRUE,
        ),
        'filter' => array(
          'handler' => 'views_handler_filter_boolean_operator',
          'label' => t('Established'),
        ),
        'sort' => array(
          'handler' => 'views_handler_sort',
        ),
      ),
      'rid_views' => array(
        'title' => t('The relation ID'),
        'help' => t('This is only here to make sure that Views works'),
      ),

      'action_link' => array(
        'field' => array(
          'title' => t('Action'),
          'help' => t('PICK ME'),
          'handler' => 'views_handler_field_friendlist_link',
        ),
      ),
     
    ),

    'friendlist_relation_types' => array(
      'table' => array(
        'group' => t('Relationship types'),

        /* No need to have this as a base, it will just confuse me and users
          'base' => array(
          'field' => 'rtid',
          'title' => t('Relation type'),
          'help' => t('A type of relation in FriendList'),
          'weight' => -10,
        ),*/

        'join' => array(
          'friendlist_relations' => array(
            'left_field' => 'rtid',
            'field' => 'rtid',
          ),
        ),
      ),


      'rtid' => array(
        'title' => t('Relation type ID'),
        'help' => t('The Relation type ID'),
        'field' => array(
          'handler' => 'views_handler_field',
          'click sortable' => TRUE,
        ),
      ),

      'name' => array(
        'title' => t('Name of the relation type'),
        'help' => t('Name of the relation type'),
        'field' => array(
          'handler' => 'views_handler_field',
          'click sortable' => TRUE,
        ),
      ),
      'name_p' => array(
        'title' => t('Plural form of the relation type'),
        'help' => t('Plural form of the relation type'),
        'field' => array(
          'handler' => 'views_handler_field',
          'click sortable' => TRUE,
        ),
      ),
      'oneway' => array(
        'title' => t('One way relation type'),
        'help' => t('Relation types that do not require confirmation'),
        'field' => array(
         'handler' => 'views_handler_field_boolean',
         'click sortable' => TRUE,
        ),
        'filter' => array(
          'handler' => 'views_handler_filter_boolean_operator',
          'label' => t('One way'),
        ),
        'sort' => array(
          'handler' => 'views_handler_sort',
        ),
      ),

      'active' => array(
        'title' => t('Active'),
        'help' => t('Active relation types'),
        'field' => array(
         'handler' => 'views_handler_field_boolean',
         'click sortable' => TRUE,
        ),
        'filter' => array(
          'handler' => 'views_handler_filter_boolean_operator',
          'label' => t('One way'),
        ),
        'sort' => array(
          'handler' => 'views_handler_sort',
        ),
      ),
    ),
  );

  return $data;

}

/**
 * Field handler to present a link to the user.
 */
class views_handler_field_friendlist_link extends views_handler_field {

  function construct() {
    parent::construct();
    $this->additional_fields['requestee_id'] = 'requestee_id';
    $this->additional_fields['requester_id'] = 'requester_id';
    $this->additional_fields['rtid'] = 'rtid';
  }

  function option_definition() {
    $options = parent::option_definition();
    $options['text'] = array('default' => '', 'translatable' => TRUE);
    return $options;
  }

  function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);
    $form['text'] = array(
      '#type' => 'textfield',
      '#title' => t('Text to display'),
      '#default_value' => $this->options['text'],
    );
  }

  // An example of field level access control.
  function access() {
    return user_access('access user profiles');
  }

  function query() {
    $this->ensure_my_table();
    $this->add_additional_fields();
  }

  function render($values) {

    global $user;

    $text = !empty($this->options['text']) ? $this->options['text'] : t('view');
    $requestee_id = $values->{$this->aliases['requestee_id']};
    $requester_id = $values->{$this->aliases['requester_id']};
    $rtid = $values->{$this->aliases['rtid']};

    if($requester_id == $user->uid){
      return friendlist_ui_get_link('add', $requestee_id ,$rtid). " " .
             friendlist_ui_get_link('delete', $requestee_id , $rtid);
    } else {
      return '';
    }

  }
}
mooffie’s picture

Looks good.

Start using it and see if it answers common needs.

For example, it could be useful to make the "relation type" a filter because users are likely to want to narrow down the list to family/work/club types.

In other words, we, "gurus" or non gurus, aren't likely to be much of a help here, because you know best what needs your module is supposed to answer. Prepare a list of things that should be possible --listen to the users of your module-- and see if they are indeed possible with your Views support. If a user complains "I'm starting a view of type 'user' but I don't see how to continue" then perhaps you should consider connecting your table to the "users" one somwhow.

(Random notes: you have some duplicate keys in your array, e.g. requestee_id_current. Also note that fields containing NULLs don't work properly with 'views_handler_filter_boolean_operator' (because Views compares the field to '0', and NULL always fails comparison)).

mercmobily’s picture

Hi,

Looks good.

That's suck a relief!

Start using it and see if it answers common needs.

OK!

For example, it could be useful to make the "relation type" a filter because users are likely to want to narrow
down the list to family/work/club types.

OK, I did it. I set the field as "numeric". This means that my users will have to type in the relation type ID in order to filter it. It would be _nicer_ to give them the option to pick the relation type... but it gets tricky. I guess I would have to write a filter handler that would give a set of options... right? Is there some code that you know of that does this already?

Or, are Views users going to be OK with keying in an ID, you reckon?

In other words, we, "gurus" or non gurus, aren't likely to be much of a help here, because you know best what needs your module is supposed to answer. Prepare a list of things that should be possible --listen to the users of your module-- and see if they are indeed possible with your Views support.

OK :-D

If a user complains "I'm starting a view of type 'user' but I don't see how to continue" then perhaps you should consider connecting your table to the "users" one somehow.

Actually, I think connecting this to "user" would be really really important. For example, from a list of random users, showing the "FriendList links" for each one would be a huge plus.
I don't suppose you could give me some general directions on how to do this? Some other module that does it...? Or...?

(Random notes: you have some duplicate keys in your array, e.g. requestee_id_current. Also note that fields containing NULLs don't work properly with 'views_handler_filter_boolean_operator' (because Views compares the field to '0', and NULL always fails comparison)).

About the duplicate keys: one is "requestee_id" and one is "requester_id"... they are different! :-D Also, I don't have any filter on booleans which could possibly contain a NULL value... I checked. Do I? :-?

THANK YOU!

Bye!

Merc.

mercmobily’s picture

Hi,

One note: about joining user_relations to node, I thought about adding:

http://local.quellagiusta.net/admin/build/modules
 'friendlist_relations' => array(

      'table' => array(

        // NEW BIT
        'join' => array(
          'user' => array(
            'left_field' => 'uid',
            'field' => 'requester_id',
          ),
        ),

        'group' => t('Relations'),
        'base'  => array(
          'field' => 'rid_views',
          'title' => t('Relations between users'),
          'help'  => t("Relations between users in the system. They can be one-way or two ways"),
        ),
      ),

However... IF THIS WORKS, this will inevitably link the table "users" with the requester ID, which is only one of the two user fields in in my table!
What can be done here?

The other option would be to add a "relation" handler to the "users" table definition in views, just like I did here with requestee_id and requester_id linking to the "users" table in my user_relationship table.

Should I alter the default "users" views definition and add the relationship? Is this a sane way to go about it/

Bye!

Merc.

mooffie’s picture

it could be useful to make the "relation type" a filter

[...] I set the field as "numeric". This means that my users will have to type in the relation type ID in order to filter it. It would be _nicer_ to give them the option to pick the relation type

A very similar filter is the node type filter. Look it up in the 'logrus' documentation (especially note the hierarchy chart), and do something similar.

I thought about adding: [...] join' => [...] The other option would be to add a "relation" handler

Right! there are two ways to join tables in hook_views_data: implicitly (using ['table']['join']), or explicitly (using a relationship handler).

You can add two relationships to the 'users' table: for the requester, and for the requestee.

Should I alter the default "users" views definition and add the relationship? Is this a sane way to go about it

Sure. Go to Views' 'modules' folder and search the files for '_views_data_alter', you'll find that this technique is used.

mooffie’s picture

Also, I don't have any filter on booleans which could possibly contain a NULL value... I checked. Do I?

When you're doing 'INSERT INTO {friendlist_relations} ...', and if you don't populate all the fields, MySQL will assign NULL to some of them (see the 'Default' column in the two DESCRIBEs you pasted above). Views' boolean filter considers NULL data as neither True nor False. Maybe it won't cause a problem in your case, but you should be aware of it.

mercmobily’s picture

Hi,

Ah, silly me. For the record, that was an old "describe", that's now fixed.

Merc.

mercmobily’s picture

A very similar filter is the node type filter. Look it up in the 'logrus' documentation (especially note the hierarchy chart), and do something similar.

Now, THIS was a hell of a piece of advice that really, really made my day. It worked like a charm.

About the relationships... I am afraid I need a little more guidance :-(

Merc.

mercmobily’s picture

I thought about adding: [...] join' => [...] The other option would be to add a "relation" handler

Right! there are two ways to join tables in hook_views_data: implicitly (using ['table']['join']), or explicitly (using a relationship handler).
You can add two relationships to the 'users' table: for the requester, and for the requestee.

Should I alter the default "users" views definition and add the relationship? Is this a sane way to go about it

Sure. Go to Views' 'modules' folder and search the files for '_views_data_alter', you'll find that this technique is used.

I think I am gonna pass on linking the user table to the friendlist_relation one for now. I can't even really work out *how* they are linked up logically.

So... that's it. I think I got Views support all done.

Amazing.

Merc.

mercmobily’s picture

Status: Active » Fixed

Hi,

thank you!

Merc.

Anonymous’s picture

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for two weeks with no activity.