John Davidson

php - Associative array (mixed data) to object properties

0 comments
Message:


The problem is not exactly passing arrays to objects per se, but distributing the initial values acquired from the database correctly in their related objects. Here's the situation. I have a forum member class and I need to pass only the values relevant to that class and also make it so it would assume certain array keys are one and the same, specifically the user ID key.


Example of a member class:


class Member {
private $id;
private $username;

public function __construct($row) {
foreach($row as $key => $value) {
$this->$key = $value;
}

public function getUserURL() {
return '<a href="profile.php?id=' . $this->id . '">' . $this->username . '</a>';
}
}

Now imagine there is a posts table too and I need to get both post and user/author data from that:


$res = $db->query("SELECT posts.id, posts.author_id, users.username FROM posts JOIN users ON author_id = users.id");
$row = $res->fetch_assoc();

And now I need to inject the user data into the member class for instantiation:


$author = new Member($row);

And there goes the first problem. I'm not really injecting only the author data but also post data into it. This could be solved in a number of dubious ways. One of them is injecting the relevant values separately instead of entire array like this:


 $author = new Member($row["author_id"], $row["username"], ...);

But this isn't very convenient as there could be more to author values than just these two, plus they can vary in number and position, which means I'd have to remember their positions and sometimes fill in the gaps with empty values or NULLs.


Of course, I could just ignore the fact I'm passing foreign data along, memory is cheap, but you may have noticed that there is still one more problem on top of it, which is the user ID key (author_id!= id). Depending on a page/table it can be "id", "uid", "author_id", "from_id", etc. Using an alias for user/author ID in the queries isn't an option either, because sometimes I need those foreign keys to be exactly what they are as they provide more leverage to the code logic.


A potential solution to both issues could be the following:


$author = new Member($row["author_id"]);
$author->getUserURL($row["username"]);

This way I don't have to worry about either foreign data or varying user/author ID keys as all are strictly defined and just once. However, the problem with this scenario is that it makes the member class hardly useful now that most of it has to be done manually. Passing values to each method for one thing. And if previously it would use existing object properties to concoct the needed result, now they mostly just return what's already in the array. What I really want is just inject an array and do something as simple as this:


$author->getUserURL();
$author->getUserAvatar();
$author->getUserHomepage();

...etc. But it seems unachievable unless two conditions are met: 1) Fixed user ID key, 2) Strictly user data. It obviously works on profile and members pages (no foreign data), but not anywhere else. Of course, I could ignore all that foreign data in object and just use array_key_exists() to check for all possible variants of author/user ID like this:


public function __construct($row) {

if(array_key_exists("author_id", $row)) {
$this->id = $row["author_id"];
}

if(array_key_exists("from_id", $row)) {
$this->id = $row["from_id"];
}

...
}

...but this is just not elegant enough. I need a more refined approach and I can't seem to come up with one. It appears that to get it right I have to at least partially break the class or somehow split the array into posts and user data, but I don't see how this could be done without too much work. It's at times like this that I just want to run two DB queries to get two separate arrays of data out of the box, which could solve everything, but everyone knows that that, too, is a bad practice.


So, has anyone got better ideas? I do realize that this isn't the best of questions as it's mostly about efficiency and not "how", but it's small things like this that could lead to bad coding in the long run and I'm still fresh to OOP.



My Answer:
One possible solution to this problem is to create a separate class for handling the user data and then inject that class into the Member class. This way, you can keep the Member class clean and focused on its own responsibilities. Here's an example of how you could implement this:

php
class UserData {
private $id;
private $username;

public function __construct($row) {
$this->id = $row['id'];
$this->username = $row['username'];
}

public function getId() {
return $this->id;
}

public function getUsername() {
return $this->username;
}
}

class Member {
private $userData;

public function __construct(UserData $userData) {
$this->userData = $userData;
}

public function getUserURL() {
return '' . $this->userData->getUsername() . '';
}
}

$res = $db->query("SELECT posts.id, posts.author_id, users.username FROM posts JOIN users ON author_id = users.id");
$row = $res->fetch_assoc();

$userData = new UserData($row);
$author = new Member($userData);

echo $author->getUserURL();


This way, you can easily handle the user data separately and inject it into the Member class without cluttering the Member class with unnecessary data. This approach also allows for better separation of concerns and makes the code more maintainable in the long run.

Rate this post

3 of 5 based on 1053 votes

Comments




© 2024 Hayatsk.info - Personal Blogs Platform. All Rights Reserved.
Create blog  |  Privacy Policy  |  Terms & Conditions  |  Contact Us