# Need a little help perfecting my friendship associations

**URL:** <https://www.ruby-forum.com/t/need-a-little-help-perfecting-my-friendship-associations/67601>\
**Category:** Rails\
**Created:** [September 5, 2006, 4:26pm UTC](https://www.ruby-forum.com/t/need-a-little-help-perfecting-my-friendship-associations/67601 "2006-09-05T16:26:51Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![elliterate](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/9de053/32.png) [@elliterate](https://www.ruby-forum.com/u/elliterate)\
**Post date:** [September 5, 2006, 4:26pm UTC](https://www.ruby-forum.com/t/need-a-little-help-perfecting-my-friendship-associations/67601/1 "2006-09-05T16:26:51Z")

</div>

Hey,

I’ve somehow managed to get friendships working first try, but it’s not  
perfect.

First the code:

create\_table :friendships do |t|  
t.column :initiator\_id, :integer, :null =\> false  
t.column :recipricator\_id, :integer, :null =\> false  
t.column :confirmed, :boolean, :null =\> false,  
:default  
=\> 0  
end

class Friendship \< ActiveRecord::Base  
belongs\_to :recipricator,  
:foreign\_key =\> ‘recipricator\_id’,  
:class\_name =\> ‘User’  
belongs\_to :initiator,  
:foreign\_key =\> ‘initiator\_id’,  
:class\_name =\> ‘User’  
end

class User \< ActiveRecord::Base  
has\_many :initiated\_friendships,  
:foreign\_key =\> ‘initiator\_id’,  
:class\_name =\> ‘Friendship’,  
:conditions =\> ‘confirmed = 1’  
has\_many :recipricated\_friendships,  
:foreign\_key =\> ‘recipricator\_id’,  
:class\_name =\> ‘Friendship’,  
:conditions =\> ‘confirmed = 1’  
has\_many :unconfirmed\_friendship\_requests,  
:foreign\_key =\> ‘recipricator\_id’,  
:class\_name =\> ‘Friendship’  
has\_many :unconfirmed\_friendship\_proposals,  
:foreign\_key =\> ‘initiator\_id’,  
:class\_name =\> ‘Friendship’  
end

My first problem is that to get a User object from my friendship I’ve to  
do  
something like:  
current\_user.unconfirmed\_friendship\_requests.first.initiator # Gets the  
User  
object of the user that made the friendship request

I’d like ‘unconfirmed\_friendship\_requests’ to contain all users that  
were  
associated with the initiator\_id, I don’t care about the recipricator\_id  
as  
that’s always the current user. The same goes for  
‘unconfirmed\_friendship\_proposals’ but the opposite.

The second problem, is that to get all the friends of a user I need to  
iterate over initiated\_friendships and recipricated\_friendships. I’d  
like to  
just do current\_user.friends.

btw, I don’t like those friendship systems where you can befriend a user  
despite them not wanting you to, hence why I’ve used a confirmed flag.

Cheers,  
Ian.

---

<div class="post-metadata">

**Author:** ![elliterate](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/9de053/32.png) [@elliterate](https://www.ruby-forum.com/u/elliterate)\
**Post date:** [September 5, 2006, 10:06pm UTC](https://www.ruby-forum.com/t/need-a-little-help-perfecting-my-friendship-associations/67601/2 "2006-09-05T22:06:27Z")

</div>

Ian,

Looks like a social networking site in the makings.

To my eyes you’re on the right track, but if you stop here you’re going  
to end up with a lot of extra code. If you go ahead and make your  
model a bit more verbose I think you can really shorten up the indexes  
into the different types of relationships.

This piece you’re model is missing is the “has\_many :through” concept.  
Your model is an interesting unary many to many relationship. (Many  
users can have many unconfirmed friends, etc…). You’re model is spot  
on with friendship table facilitating the different joins.

class User \< ActiveRecord::Base  
has\_many :initiated\_friendships,  
:foreign\_key =\> ‘initiator\_id’,  
:class\_name =\> ‘Friendship’,  
:conditions =\> ‘confirmed = 1’  
has\_many :reciprocated\_friendships,  
:foreign\_key =\> ‘reciprocator\_id’,  
:class\_name =\> ‘Friendship’,  
:conditions =\> ‘confirmed = 1’  
has\_many :unconfirmed\_friendship\_requests,  
:foreign\_key =\> ‘recipricator\_id’,  
:class\_name =\> ‘Friendship’,  
:conditions =\> ‘confirmed = 0’  
has\_many :unconfirmed\_friendship\_proposals,  
:foreign\_key =\> ‘initiator\_id’,  
:class\_name =\> ‘Friendship’,  
:conditions =\> ‘confirmed = 0’

```
has_many :initiated,
         :through => 'initiated_friendships'
         :foreign_key => 'reciprocator_id'
has_many :reciprocated,
         :through => 'reciprocated_friendships'
         :foreign_key => 'initiator_id'

```

has\_many :unconfirmed\_requests,  
:through =\> unconfirmed\_friendship\_requests,  
:foreign\_key =\> ‘initiator\_id’  
has\_many :unconfirmed\_proposals,  
:through =\> unconfirmed\_friendship\_proposals,  
:foreign\_key =\> ‘recipricator\_id’  
end

The real evangelist of this style would be Josh S. of  
[http://www.hasmanythrough.com](http://www.hasmanythrough.com) . His site has a lot of great examples  
if you don’t mind digging through his blog for them.

In the above code I’ve never had to use a foreign key in a has\_man  
:through association so I’m not sure it works the way I’ve demonstrated  
(I don’t have the time right now to put together a test so it’s just a  
theory.

If this does indeed function this would take the command to index form:  
current\_user.unconfirmed\_friendship\_requests.first.initiator  
to:  
current\_user. unconfirmed\_requests.first

Another thought I had while making the modifications was that it might  
be simpler in the long run to us a directional association that  
indicates whether a person is considered a friend or not. When the  
relationship is only made in a single direction it is unconfirmed with  
the connector being the initiator and the connected to being the  
reciprocator. Once the reciprocator confirms by making a connection  
themselves we don’t really care which of them initiated, or at-least I  
would imagine that the initiator/recipricator roles would be less  
important after the relationship is fully formed.

The big advantage would eliminating an sql request every time a friends  
list is drawn up. In order to compile a list of all friends right now  
you will need to grab both initiated and reciprocated confirmed  
friendships (2 sql requests). If I understand your application  
correctly this would be far superior to be able to request all  
confirmed friends in a single request. If this is indeed a social  
networking site the listing of friends will probably be one of the most  
frequent database actions so if we can cut the execution time for this  
in half in the long run we’ll generate a lot of efficiency.

Anyway. Thats my $.02 for what it’s worth.

-Aaron

---

<div class="post-metadata">

**Author:** ![elliterate](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/9de053/32.png) [@elliterate](https://www.ruby-forum.com/u/elliterate)\
**Post date:** [September 6, 2006, 12:12am UTC](https://www.ruby-forum.com/t/need-a-little-help-perfecting-my-friendship-associations/67601/3 "2006-09-06T00:12:43Z")

</div>

Thanks for the excellent reply Aaron.

The reason I had avoided using a record for each direction of friendship  
was  
to avoid what in my eyes is duplicate record.  
I was aware of the two query overhead, though I was hoping rails would  
be  
able to do some OR magic on the JOIN for me. I may just go with the the  
two  
friendships record for a confirmation approach, it seems a lot simpler.  
This  
is as far as my current knowledge of Rails associations will stretch.  
Rails  
is going to attempt a join on either the initiator or the recipricator,  
not  
both of them ANDed, correct? Am I going to have to use :finder\_sql?

On second thoughts, could I just not user :finder\_sql with the single  
record  
approach and do the JOIN magic myself? That’d reduce it to one query and  
one  
record.

I think i’ll give this a try in the morning…

---

<div class="post-metadata">

**Author:** ![elliterate](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/9de053/32.png) [@elliterate](https://www.ruby-forum.com/u/elliterate)\
**Post date:** [September 6, 2006, 1:28am UTC](https://www.ruby-forum.com/t/need-a-little-help-perfecting-my-friendship-associations/67601/4 "2006-09-06T01:28:49Z")

</div>

Ian,

After all that I knew I’d seen something like this before. Josh S.  
(mentioned previously) wrote a post about self referencial maps several  
months back. I went back and found it. Low and behold one of the  
commentors is building a very similar system of references. Anyway  
here’s the link:  
[http://blog.hasmanythrough.com/articles/2006/04/21/self-referential-through](http://blog.hasmanythrough.com/articles/2006/04/21/self-referential-through)

I think you’ll have to actually write the sql yourself to get OR syntax  
merging the two sets. I’m not aware of any rails sugar available to do  
so, which doesn’t mean it doesn’t exist, it just means I haven’t heard  
of it yet if it does.

Glad to be of assistance,

Aaron - removed\_email\_address@domain.invalid
