Reputation: 1190
I'm looking to improve my OOP code design as far as I can, but I'm stuck on this issue.
For example, I want to get the profiles of all friends of a user, in a social networking site. So I have tables Friendships
and Profiles
. And I have classes Friends
and Profile
.
Let Friends
be the class that handle a user's friendships and Friends::getFriendsProfiles()
be the function that fetches and returns all the user's friends' profiles.
So inside my function Friends::getFriendsProfiles()
I can either do a
A table join
(e.g. SELECT * FROM Friends LEFT JOIN Profiles ON Friends.user2 = Profile.userId WHERE Friends.user1 = :userid
), or
I can just fetch the user ids of the friends, create a Profile
object for each friend id, and call $profile->getProfile($friendid)
which runs a query (SELECT * FROM Profiles WHERE userId = $friendid
) to fetch a friend's profile. Then return the set of all the friends' Profile
object.
Option 1 Disadvantage: My Friendship class knows about Profiles. And when I need to make a change on the way a profile is returned (e.g. I want to add another property to each profile object), I need to change it in 2 different places.
Option 2 Disadvantages: Instead of making 1 query (which I think should run in O(1) ?), it is now O(n) where n is the number of the user's friends.
But Option 2 is so much cleaner and loosely coupled. Which option should I take?
Upvotes: 1
Views: 358
Reputation: 12675
I would definitely go with Option 1 and only use 1 query.
The Friends
class must not know much about the Profiles
if the constructor could work with an array.
You could do something like:
SELECT Profiles.*
FROM Friends
LEFT JOIN Profiles ON Friends.user2 = Profile.userId
WHERE Friends.user1 = :userid
Then in the loop:
$profiles = array()
while ($data = mysqli_fetch_assoc($result)){
$profiles[] = new Profile($data);
}
One perhaps cleaner solution would be to make it a method of the Profile
class.
Profile::getFriendsProfiles()
The loop:
$profiles = array()
while ($data = mysqli_fetch_assoc($result)){
$profiles[] = new self($data);
}
The constructor from Profile
could be:
function __constructor(array $data = null)
{
if (null !== $data) {
// fill properties
$this->id_profile = $data['id_profile']; // example
...
}
}
It would be better if the SQL code would be in another object the Table Data Gateway
.
If you really wan't to improve your OOP then read about software design patterns.
You can start with Martin Fowlers's site here.
Upvotes: 2
Reputation:
Option 1 Disadvantage: My Friendship class knows about Profiles. And when I need to make a change on the way a profile is returned (e.g. I want to add another property to each profile object), I need to change it in 2 different places.
Domain objects are naturally going to have some coupling. This is just the reality of the system you are modelling. This is not so much a coupling problem between friendship and profile its a problem of tight coupling between your business layer and your data layer. If you had a datamapper, finder class etc and made your business object persitance ignorant then changes like this should not matter too much.
If you use the second option you run into the n+1 select problem. In this instance I would not sacrifice performance when there are more important areas that you could consider decoupling.
Upvotes: 1