An universal Relation API

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
mercmobily's picture

Hi,

OK, now that I got your attention with a "catchy" title... and for those who didn't have a heart attack:

(This thread is specifically for Alex.K. However, everybody's welcome to jump in.)

http://drupal.org/project/user_relations_api

This is not a new API. This is a "glue API". This is the short version:

  • You install it
  • You tell it which "relation" module it should talk to
  • That's it

From then on, any Drupal module has access to a generic set of functions to see if two people have an established relation, who the "related" users are, and so on.

The issues right now:

  • The name is... err... well... err... hummm... yeah.

  • There is only the FriendList layer, and it's untested

  • The function names... are logical, but ugly. Ideas are welcome

  • Right now user_relations_api_relation_types_all() returns something that is straight from FriendList. Something generic is likely to be preferred. I just did it this way because it was the most natural way to go for me

  • The code is not immaculate yet. I picked the easiest way of doing everything

  • I am not 100000000% convinced that setting the default relations is a good idea. This should be a setting per client-module. But, good luck coding it... Maybe I am just being a perfectionist, and it is fine

  • Did I cover all of the most common options?

  • If a module doesn't cover relation types, it will just have to pretend that only 1 is set ("friends" I assume)

  • Alex: We can -- and should -- enrich the users objects returned with some extra information, like "when the relation was created" etc. However, I think that can come later.

This module is much needed with the upcoming shindig integration module (which is allegedly about to be released, fingers crossed). If we agree with this API, then I will start sending patches out to every contrib module that has a dependency on [friend,friendlist,user_relationships,buddylist,buddylist2] and tell them to use this module instead.

Alex, it should be immensely trivial for you to implement the user_relationships layer to this.

This could seriously put an end to the mess in the "user relations" issue. It would also be trivial to write a layer for the "flag" module (although it would only manage one way relationships, because of the nature of it).

The goal is to make this the default module for any other developer who wants to know who on earth is a friend of user 4. So, let's do it, and let's do it right.

Bye,

Merc.

Comments

...?

mercmobily's picture

Hi,

I know I shouldn't hold my breath. But... Alex? Anybody?

I can write the User Relationship side of it on my own. However, I suspect it would take you 30 minutes against a good 4 hours. Plus, this module would work precisely so that other modules can finally work with User Relations without having to pick -- therefore, more integration, more easily.

Maybe I should put this issue forwards in the broader forums?

Merc.

Reciprocity for Flag?

aaron's picture

How about you just add a patch for a layer of reciprocity on the Flag module? For instance, you could allow a flag to be marked 'reciprocal' in the admin screen, which would require two users/nodes/comments/whatever to require a reciprocal flag set by the other before it's considered active. Then add the requisite view filters, and you're set.

Aaron Winborn
Drupal Multimedia (book, in October!)
AaronWinborn.com (blog)
Advomatic (work)

Aaron Winborn
Drupal Multimedia (my book, available now!)
AaronWinborn.com
Advomatic

+1 for flag

Michelle's picture

I haven't really dug into flag yet, but I've heard the buzz and have a lot of ideas on how I want to use it. For my personal sites, I plan on reproducing the nice and simple and sadly dead buddylist 1 using flag. But I know less Drupal savvy folks need a drop in module that can do it all for them. So a more complex buddy/friend/ur/whatever type of module built on top of flag would be awesome.

Michelle


See my Drupal articles and tutorials or come check out the Coulee Region

+1

bonobo's picture

What Aaron and Michelle said --

Using Flag gets you actions, triggers, and views out of the box.

Cheers,

Bill


FunnyMonkey
Tools for Teachers

When you start adding

moshe weitzman's picture

When you start adding reciprosity for flag, you kill the simplicity and genericness of that module. I am not seeing a way to do it which keeps the spirit of flag. maybe someone else does see it.

.

Michelle's picture

Well, I can't speak for anyone else, but I was envisioning using flag as an API so the code wouldn't be added to flag but to the module sitting on top of it. So when you click on a user to "flag" them as a friend, it fires off the triggers to let that person know, maybe some sort of moderation if they need to approve it, etc.

Maybe I'm just talking out of my hat, though... I haven't looked at flag's code.

Michelle


See my Drupal articles and tutorials or come check out the Coulee Region

Great, but...

mercmobily's picture

Hi,

I think it's lgreat that you are trying to get away from a specialised "friends" module and use Flag instead.
However, wouldn't that just be yet another "relation" module, just with a different backend to mark a relationship?

Maybe I am missing the legendary clue stick here. Just wondering.

Merc.

Besides...

mercmobily's picture

Hi,

Besides, what I am trying to do here is create a common API, so that it won't matter if somebody does come up with a great layer around flag to implement something amazingly good. (funnily enough, FriendList could be changed really quite easily to use Flag, but that's another story).

With this API idea, what I am trying to achieve is a situation where it won't actually matter if Drupal has multiple API modules. That's where I am trying to go with this API -- plus, I'll need it when I start working on the Shindig implementation (which right now depends on buddylist).

I personally think we should concentrate on that.

Merc.

Folks, could we stay on

alex.k's picture

Folks, could we stay on topic :) That is, would anyone be interested in a common API for UR/FL/BL. Our options are (after talking to Merc about a custom vs. buddylist-compatible API)...

  • Maintain a compatible hook system, without a central module to depend on, see my comment below.
  • Have just one common function equivalent to buddylist_get_buddies($uid), which is as Tony correctly points out is likely what most use cases revolve around.
  • Implement a buddylist-compatible API for more complete read-only access to user's relationships

These are listed in my order of preference... another pitch for a hook system.

The code snippet below isn't necessary to invoke a hook. Here's an example...

<?php
//in user_relationships, an implementation of hook_socnet_is_related()
function user_relationships_api_socnet_is_related($uid1, $uid2) {
  if (.....) {
    return
TRUE;
  }
}
?>

<?php
//in FriendsList, an implementation of hook_socnet_is_related()
function friendslist_socnet_is_related($uid1, $uid2) {
  if (.....) {
    return
TRUE;
  }
}
?>

Then, a contrib module, or a theme, that needs to display something for just related users, would do:

<?php
$result
= module_invoke_all('socnet_is_related', $uid1, $uid2);
if (
count($result)) {
  ....
}
?>

The contract for implementing functions is to return TRUE if the users are related, and nothing if they aren't. So the calling module checks if $result contains any values to use for its logic.

Humm

mercmobily's picture

Hi,

Two problems with the hook approach:

1) Default relation type.

I think this is something that can easy get overlooked. With this approach, each "relation" module (FriendList, User Relationships) would have to give the option to have a default relation type (or two, one for one-way and one for two-ways). And, BTW, I am +1 +1 +1 for having separate functions for 1 way and 2 way relation types, to give modules more choice.

2) Complexity for the module developers

Take this code:

<?php
$result
= module_invoke_all('socnet_is_related', $uid1, $uid2);
if (
count($result)) {
  ....
}
?>

Now... we can tell them "if you get just one 'yes', then you're sorted". But it just feels a bit wrong.
What about a function that returns a list of users? Our functions would return an array each. Then, the user would have to always remember to merge these two arrays (which are a bunch of $user objects).

Yes, there will only be 1 "relation" module in 99.5% of the cases. But we can't tell people "oh just get the first returned result", anyway.

If I were a module developer wanting to find out if two people are friends, I would feel a lot happier by writing:

<?php
$result
= socnet_is_two_way_related($uid1,$uid2);
?>

And:

<?php
$user_list
= socne_two_way_relations($user);
?>

What concerns me is that we are talking about the code that everybody will have to write to see who is friends and who is fan or who is associate etc. I don't think making it cryptic will do them a favour.

So, this is what I don't like about the generic API. What don't you like about the "single API" approach?

Merc.

From

alex.k's picture

From http://api.drupal.org/api/function/module_invoke_all/6:

Return value
An array of return values of the hook implementations. If modules return arrays from their implementations, those are merged into one array.

There is nothing to merge, drupal does it already. If there are more than one implementing modules, they will all fall into one array. That, I think, is a feature, that you wouldn't get using a common API unless it also implements essentially the same system. A site running FriendList plus, say, Ubercart, or a custom site-specific module, will be able to poll both for relationships, without them knowing of each other's existence.

So, a trivial API would be

<?php
function socnet_is_related($uid1, $uid2) {};
function
socnet_get_related_users($uid) {};
function
socnet_is_blocked($blocked_by_uid, $blocked_uid) {};
?>

and maybe one or two more.

I am more and more sold on the idea

mercmobily's picture

Hi,

I am more and more sold on this idea. I didn't know invoke_all merged things.
This does indeed simplify things a lot.

So, three things are left which you don't seem to agree on:

1) The need for functions both for 1-way relations and 2-way relations

2a) The need for pretty much every function I have in my API, after some MUCH needed renaming. If not, which functions would you not have precisely? (and why :-D )

2) The need for our modules to define a default rtid for 1way and 2way, but giving the caller the option to specify a rtid (which could well be ignored by the API if for example it doesn't have relation types)

3) The ability to return all of the relation types. This is where the hooks() idea might fall... but maybe not. I am thinking that a third party module might want to just know what the relation types are and what they are, and rtids amongst modules might clash (although it's gonna be rare-to-absurd). I have one contrib module developer right now who is doing integration for FriendList and User Relationships and wants to know the relation types. So, this is a real use case.

4) A project (no code, just the project) that will host clear documentation of what these calls should be.

Bye,

Merc.

Could you list your proposed

alex.k's picture

Could you list your proposed function names, for the extras.

As to #3, I don't see the use cases... pls share if you know any. I can see returning text labels, which would be reasonably unique. I'm pretty sure every relationship module out there has a relationship with the ID of 1. Returning a descriptive associative array for each relationship will work, but it's too complicated.

OK...

mercmobily's picture

Hi,

I am not fussed about the function names, but I am about the exported functonalities. Please see my near absolete API (link above) to see what fnctionalities I think we need to export.

THe use case for #3 is very simple and in fact it's a real case scenario sorry, I am not a home, Idon't have the issue link handy). That is: a module mantaner wants to allow the user to decide to which relation type(s) it will give access to specific nodes. So, it needs to find out a list of relation types. S/he will have a form in her own module which alows users to select the wanted relations, ad will store them. She will ten query the AAPI about relations of that type.

Without this information, I strongly believe tis API would be too limited.
Al we need to do, really, is return an array with the relation types in a pre-decided format

Bye,

Merc.

If it's standard node

alex.k's picture

If it's standard node read/write/delete access, I would suggest s/he just implement it for FL, as user_relationships already has this feature.

Looking at http://drupal.org/project/user_relations_api I would propose these functions, hopefully self explanatory:

<?php
//true iff uid1 has a confirmed relationship with uis2, optionally filter by relationship name
function hook_socnet_is_related($uid1, $uid2, $relation_name = NULL) {};

//true iff user1 has requested a relationship to uid2, optionally set which relationship to query
function hook_socnet_is_pending($user1, $user2, $relation_name = NULL){};

//true iff user1 does not want to see uid2
function hook_socnet_is_blocked($blocked_by_uid, $blocked_uid) {};

//return a list of uids of established relationships of $uid, optinally filter by relationship name
function hook_socnet_get_related_users($uid, $relation_name = NULL) {};

//return a list of uid of all, to whom uid has sent requests, optionally filter by relationship name
function hook_socnet_all_pending_from($uid, $relation_name = NULL){};

//return a list of uid of users who want to relate to $uid, optionally filter by relationship name
function hook_socnet_all_pending_to($uid, $relation_name = NULL){};

//return a list of all relationship names to use in other functions
function hook_socnet_get_relation_names(){};
?>

You can see what I've folded one-way and two-way relationship functions. If this is important for the use cases you know, let's add function hook_socnet_is_mutual_relationship($relation_name) rather than duplicate the function count. Tomorrow three-way relationships might be all the rage, and it won't fly :)

Last issues

mercmobily's picture

Hi,

If it's standard node read/write/delete access, I would suggest s/he just implement it for FL, as user_relationships already has this feature.

FriendList has had Node Access since the very beginning.

This module that is integrating is actually giving access (or not access) to the user's facebook status. So, not specific nodes. He's doing it for FriendList. I would love him to stop and do it for any module using this API.
I can see that you did it pretty much. Actually getting this api to just deal with the names solves a lot of problems - nice one, kudos. What concerns me, is that it should also return the type -- one way, two ways.

But, seeing the rest of the API, you don't seem to want to distinguish one way relations and two way ones. I don't quite get it -- what does "is related" mean? Are they "friends"? Or is user 1 fan of user 2? Are you going to assume that the person is enquiring about a 2 way relationship?

Finally, about the last parameter being optional, you mention an "optional filter" whereas I suggested a "configurable default" of each module. We need to decide on this one, so that all of the submodules work the same.

Bye,

Merc.

Two way

alex.k's picture

This module that is integrating is actually giving access (or not access) to the user's facebook status.

That's a nice feature... And it should be doable using the api above, i think.

There is a function that lets us know whether each relationship is one way or not. Just there isn't one that does it for all relationships. So, if a module really needs to know, it will check each individual one (and I think, it's not an important distinction, because site admin will know which relationship names should see facebook status, and enable the right ones). Otherwise we have to return an associative array, or an object...

Two way relationships are a superset of one way relationships... I don't think a special function is needed for two-way relationships. If A is a fan of B, that's enough information to act upon - you don't necessarily need to know beforehand that fan is a two-way relationship, and have to call a different function. One function to answer all questions will work here. And the same reason for the relationship name being optional everywhere. To illustrate, Buddylist has just one type, 'buddy'. In this case specifying a name makes no sense, it's a no-op. UR has multiple types, but not a default one. In this case I will return true if there is any established relationship at all, I think. FL has a default, and you'd return that one. So I don't think making the name mandatory is needed here, would you agree?

OK

mercmobily's picture

That's a nice feature... And it should be doable using the api above, i think.

I agree!

There is a function that lets us know whether each relationship is one way or not. Just there isn't one that does it for all relationships. So, if a module really needs to know, it will check each individual one (and I think, it's not an important distinction, because site admin will know which relationship names should see facebook status, and enable the right ones). Otherwise we have to return an associative array, or an object...

I had misunderstood that function. OK. So there is a way of knowing about a relationship type (silly me). I guess that'd be enough. Still, let's see about the api function names

Two way relationships are a superset of one way relationships... I don't think a special function is needed for two-way relationships. If A is a fan of B, that's enough information to act upon - you don't necessarily need to know beforehand that fan is a two-way relationship, and have to call a different function. One function to answer all questions will work here.

I am not 1000% convinced. But maybe I am just resistant to a different approach.
Let's see.

  • Case 1: User1 and user2 have a two way relationship ("friend" for simplicity) hook_socnet_is_related($uid1, $uid2) will return TRUE (or rather, its count() >0)

  • Case 2: User1 is a fan of user2. Do we return TRUE?

  • Case 3: User2 is a fan of user1. Do we return TRUE?

I guess the answer is "yes". So, this means that function is generic and will just ask "Are the two users friends, or is one of them a fan of the other one".
To me, I must say, that seems limiting.
This ambiguity gets worst for hook_socnet_get_related_users($uid, $relation_name = NULL). What are we returning this time? It's easy for two way relations. But for fans? You return the users the person is a fan of? Or the users who are fan of the user?

I think there is a risk that this API will just be too ambiguous about this..

And the same reason for the relationship name being optional everywhere. To illustrate, Buddylist has just one type, 'buddy'. In this case specifying a name makes no sense, it's a no-op. UR has multiple types, but not a default one. In this case I will return true if there is any established relationship at all, I think. FL has a default, and you'd return that one. So I don't think making the name mandatory is needed here, would you agree?

I disagree on this one. I think it should be dead clear what each function does. If one module filters and the others uses a default, that's unexpected behaviour.

I think it needs to be one or the other -- and yes, "filter" seems to be the one that makes the most sense.

Merc.

Practical counter-proposal to the API

mercmobily's picture

Hi,

OK, here is a counter-proposal to the API you proposed Alex.
The main issue I have is that APIs need to be very, very precise. One way and two way relations are logically different. Not having two set of functions will lead an API that is too generic to be actually used.

The reasons aree the ones I outlined in my previous post, starting from "I am not 1000% convinced.". The more I think about it, the less convinced I am because there is no "really sane" answer to my questions in that post. Those functions would be too genetic and therefore too limited -- so, people might end up supporting just Friendlist or just User Relationships to do the real work.

So:

<?php
//true iff uid1 has a confirmed relationship with uis2, optionally filter by relationship name
function hook_socnet_is_related($uid1, $uid2, $relation_name = NULL, $relation_style='A') {};

//true iff user1 has requested a relationship to uid2, optionally set which relationship to query
function hook_socnet_is_pending($user1, $user2, $relation_name = NULL, $relation_style='A'){};

//true iff user1 does not want to see uid2
function hook_socnet_is_blocked($blocked_by_uid, $blocked_uid, $relation_name = NULL, $relation_style='A') {};

//return a list of uids of established relationships of $uid, optinally filter by relationship name
function hook_socnet_get_related_users($uid, $relation_name = NULL, $relation_style='A') {};

//return a list of uid of all, to whom uid has sent requests, optionally filter by relationship name
function hook_socnet_all_pending_from($uid, $relation_name = NULL, $relation_style='A'){};

//return a list of uid of users who want to relate to $uid, optionally filter by relationship name
function hook_socnet_all_pending_to($uid, $relation_name = NULL, $relation_style='A'){};

//return a list of all relationship names to use in other functions
function hook_socnet_get_relation_names($relation_style='A'){};

// hook_socnet_is_mutual_relationship($relation_name)
function hook_socnet_relation_is_two_way($relation_style='A'){};
?>

Where relation style is:

'A' -- All (one way, two ways)
'O' -- one way only
'T' -- two way only

And if in the future somebody thinks of other ways, we'll add to this. default is "A"

Even with this new API, there are still two problems we need to figure out -- and they come from this being a set of hooks, rather than a "proper" API.

1) What if two different relation modules return a set of user's friends, and the same user is repeated? So, if FriendList returns 3,4,5,6 and Flag returns 1,3,10,15 ... as you can see, 3 is repeated. The uid array needs to be flattened so that ids are not repeated. We might have to tell other developers that they need to do to this, and give a simple example code.

2) hook_socnet_relation_is_two_way($mutual_only=FALSE) might not work in case there are two different relation modules offering two relation types with the same name but one is mutual and the other one is not. Now... IN GENERAL, having two different modules with the same relation type is gonna kill this whole system, because we are working on names rather than ids. I think it's fine... but we need to make sure it's clear, in the API page, that this really shouldn't happen.

So, I think with a warning in the project's page, a standard snipset to take out repeated entries in an array, and with this extra parameter, this might be an API.

No?

Merc.

This will work, I agree. I

alex.k's picture

This will work, I agree. I only want to use complete words for $relation_style: 'all', 'one-way', 'two-way'.

In the last function

<?php
// hook_socnet_is_mutual_relationship($relation_name)
function hook_socnet_relation_is_two_way($relation_style='A'){};
?>

the change is unintended? The argument should be a relation name. I also just want to follow 'verb-object' in the naming scheme like the others.
<?php
// hook_socnet_is_mutual_relationship($relation_name)
function hook_socnet_is_two_way_relation($relation_name){};
?>

1) What if two different relation modules return a set of user's friends, and the same user is repeated?
Right-o, it's just a matter of running it through array_unique(). Let's add a note to the effect of "This hook may return duplicates, if more than one module implements it. If it is important to have unique results, run
<?php
$result
= module_invoke_all('socnet_get_related_users', $uid);
//if any users were returned, remove any duplicates from the list
$result = is_array($result) ? $array_unique($result) : $result;
?>

2) hook_socnet_relation_is_two_way($mutual_only=FALSE) might not work in case there are two different relation modules offering two relation types with the same name
Let's note that in the documentation, as a caveat. One thing that implementing modules can do is, when creating their own relationship types, to call hook_socnet_get_relation_names(), and warn the user if they are picking a name that's already used elsewhere.

In Drupal fashion, do

cridenour's picture

In Drupal fashion, do something along the lines of,

<?php

define
('ALL_RELATIONSHIPS', 0);
define('ONE_WAY_RELATIONSHIPS', 1);
define('TWO_WAY_RELATIONSHIPS', 2);
?>

and using those for the API.

Hummm

mercmobily's picture

Hi,

I thought about this. However...
The point of using hooks is that any module will be able to use them without including anything -- just call a hook.

So, we can't really have constants because calling modules have nothing to include. If we decide that modules have to, then we might as well use the original idea of an API module to be included.

Merc.

I am not saying...

mercmobily's picture

Hi,

I am not saying that I am "that" person.
However... I noticed that FriendList could very easily be changed so that it uses flag as its backend.
The reason is that FriendList has two tables, one for the relations and one for the statuses. The statuses are what really make a friendship a "friendship", if you know what I mean.

So, yeah, I am fantasizing about giving the option of giving FL the ability to use Friends as a backend and see what happens. It would definitely be interesting, IF possible.

Merc.

Options

mercmobily's picture

Hi,

I looked into it. While it's possible to use FriendList's API functions to manage the "status" of the relationship, it is way messy.

Now I am thinking if it would be beneficial to actually rip some of the code out of FriendList (especially the "status" section and the Rules integration) and just use it as a basis for a "status" layer for Flag. I think It would work.
So, the options are:

  • Changing the FriendList API so that the status recalculation functions can take any "relation" table rather than friendlist_relations. Drawbacks: it would be quite hard; it would make the API a little less "readable";

  • Ripping out the "status" part of FriendList_api and the rules bit about "status", and just use that as a means of having a relation status. Drawbacks: code duplication (although it would probably change in time);

In both cases, this would be creating yet another "friends" module somehow (although it would be a way of enriching Flag). BUT, if my efforts here in creating a standard API are successful, then this won't matter all that much.

If somebody wants to go one way or the other, I am here to offer full (and fast responding) support.

Bye,

Merc.

Hummm

mercmobily's picture

Hi Aaron,

How about you just add a patch for a layer of reciprocity on the Flag module?

And forget about FriendList and User Relationships?
I don't know. The problem I have with it is that the market of the "relation" modules has been very... err... "fluid". I don't think the policy of "deciding on one" is really going to be a winner here. Maybe I am wrong.

Once again, what I am trying to do is face the problem in such a realistic and neat way. Saying "let's create another solution which might be better" is great because it means "improvements", but I don't really think it really solves the problem we face right now.

Merc.

Some alternatives

alex.k's picture

Merc, sorry for the wait...

Here's my take. The API is good in itself, but we don't know what the use cases are... we don't know what other modules would want in a simplified API like this, what functions would be perfectly suited, and what would go unused. So what we end up risking is producing "jack of all trades, master of none". Something that covers two-thirds of use cases but leaves developers in the wild for the rest. Additionally, you need to get all interested developers to co-maintain this module as they evolve theirs.
Not to say there aren't successful examples of this. Voting API is one. The module, however, has no UI itself, which is the right separation. We can't boast this feature. This would be the way to go, actually, to have a module with just a DB abstraction layer and Views integration, and a dozen other, independently maintained pluggable modules that would implement the UI, invites, notifications and the like. We are past that stage with both our modules, however :)

If I were to support a common API, it wouldn't actually be this. I would clone the buddylist API, copied with all function signatures. There are many modules that support it, and many devs who have worked with it. So they'd just need to rename their buddylist_* calls to make their module work. That would be a serious value-add.

What do you think, however, about disposing of this module, and agreeing on a common set of hook functions? A use case would be, UR and FL implement something like {module_name}_socnet_is_related($uid1, $uid2, &$return). An unrelated contrib module runs

<?php
 
foreach (module_list() as $module) {
   
$function = "{$module}_socnet_is_related";
    if (
function_exists($function)) {
     
$function($account1->uid, $account2->uid, $result);
    }
  }
?>

or some such construct. What we get is an interoperable API, which anyone can selectively implement, and which doesn't need a glue module, or any dependency whatsoever. And, someone could write their own per-site custom modules to override behavior of UR, or FL, or whatever, if they so desire - this without having to edit the source of UR or FL. To 'spread the word' we just need to maintain a codeless project with documentation, or a page on d.o, which would list all possible hooks.

If this is silly for any technical or style reason, let me know :)

mutual dependent module

catch's picture

Rather than dynamic function names which adds complexity for the caller, why not a common module which both UR and FL can depend upon?

My comment...

mercmobily's picture

Hi,

I agree with Alex that the function names could and should be named after buddylist, just out of simplicity. Right now, they are just "logical".

I agree with catch that using hooks would make calling awkward. It would make sense to have it like that if a user could have multiple relation modules at the same time. However, that's highly unlikely and counterproductive.

catch: I see where you're going. And I think that's where I am trying to go as well with this. The module I propose here, user_relation_api, would be pretty much that: you tell it which API to use (because it needs to know somehow), be it FriendList, User Relationship, PumpedUp Flags, etc.) and it will ask "questions" to the right module. I would love you to have a quick look at the code I wrote, both to see if you think I went the right direction design-wise and to see if that's the result you were thinking about.

Requiring FriendList and User Relationship on it would be nice because it would guarantee that that layer is indeed provided if one of those modules is installed. But I didn't dare going that far in my proposal.

Also, note that this would be a simple subset of what FL/UR/SuperFlag would offer. Most (very possibly all) modules in Drupal really just need to know "are user1 and user2 friends?", and "who are the friends of User1", etc. The APIs are more complex (they allow fancy querying, adding relations, etc.) but no other module wants to know about that.

I am slowly convincing myself that:

  • Renaming the functions so that they are not the product of an autistic mind, but based on Buddylist's
  • Making sure the code is actually sane (Hint catch hint); the way I did things might make you laugh -- I've never dealt with something like this before in PHP
  • Making it a dependency to any Relation-type module

Would possibly fix the "mess" of the relations API now, and for the future.

Bye,

Merc.

Api, final?

mercmobily's picture

Hi,

I am adding this at the bottom because things are getting a little too nested. So note: this message is the latest, which adds to the highly nested discussion above :-D
Comments:

1) Yes, I did get the parameter name wrong. However, now that we have a naming connection for relation style, and since we ought to keep it expandable, I changed the function so that it returns the "style".

This should be the final version of the API.

<?php
// true if uid1 has a confirmed relationship with uid2, optionally filter by relationship name and style
function hook_socnet_is_related($uid1, $uid2, $relation_name = NULL, $relation_style='all') {};

// true if user1 has requested a relationship to uid2, optionally set which relation and style to query
function hook_socnet_is_pending($user1, $user2, $relation_name = NULL, $relation_style='all'){};

// true if user1 does not want to see uid2
function hook_socnet_is_blocked($blocked_by_uid, $blocked_uid, $relation_name = NULL, $relation_style='all') {};

// return a list of uids of established relationships of $uid, optionally filter by relationship name
function hook_socnet_get_related_users($uid, $relation_name = NULL, $relation_style='all') {};

// return a list of uid of all, to whom uid has sent requests, optionally filter by relationship name
function hook_socnet_all_pending_from($uid, $relation_name = NULL, $relation_style='all'){};

// return a list of uid of users who want to relate to $uid, optionally filter by relationship name
function hook_socnet_all_pending_to($uid, $relation_name = NULL, $relation_style='all'){};

// return a list of all relationship names to use in other functions
function hook_socnet_get_relation_names($relation_style='all'){};

//
// return 'two-way' or 'one-way' depending on the relation type
function hook_socnet_relation_type($relation_name){};

// NOTES TO DEVELOPERS:
//
// * For functions that return a list of users, you should check that there aren't any duplicate IDs.
$result = (array) module_invoke_all('socnet_get_related_users', $uid);
$result = array_unique($result)
//
// * This API works on user IDs, not user objects, in order to save node_loads
?>

Do we have an API?
Alex.k, if you agree with this, it means that we both do. At that point... I would love to get a more experienced Drupal person (Catch? Webchick?) to look at it, and let us know if this is indeed a sane approach.

I am excited: is Drupal really about to get its unified relation API, totally independent of the underlying module?

Merc.

Looks good, let's commit to

alex.k's picture

Looks good, let's commit to this version. Hope it gets adoption!

I did not elaborate much on your point of is_related in the context of one-way relationships. This function

<?php
// true if uid1 has a confirmed relationship with uid2, optionally filter by relationship name and style
function hook_socnet_is_related($uid1, $uid2, $relation_name = NULL, $relation_style='all') {};
?>

to me, should do this:
- If there is a two-way relationship, return TRUE
- If there is a one-way relationship from uid1 to uid2, return TRUE (e.g. uid1 is a fan of uid2)
- If there is only a one-way relationship from uid2 to uid1, do not return TRUE. if uid2 knows uid1, that doesn't mean that uid1 knows uid2.

Hmmmm

mercmobily's picture

Hi,

I totally agree.
Now, I would love to see a Drupal guru to check that we didn't do something horrific.

I think that's a good idea... Catch? Webchick?

Merc.

Ok done...

mercmobily's picture

Hi,

I created this project:

http://drupal.org/project/drupal_universal_relation_api

I have also implemented all of the required calls for FriendList. I have committed the code, but I haven't tested it yet. However, I believe this completes Version 1 of the API.

Alex.k, I made you co-maintainer of the project. I think a good goal would be to keep all of the important pieces of information in the project's page, so that developers don't have to dig any information from anywhere.

I will work towards documenting the functions more thoroughly. However, this is definitely a start.

Bye,

Merc.