I was wondering how fetchAll of PDO is actually implemented to get an Idea how to map the result from the database including a GROUP_CONCAT() comma separated list string to an array property.
Having a sql like
$query = "Select a.id, GROUP_CONCAT(b.name) AS referencingNames FROM a JOIN b on (a.id = b.id_a)"
Will return something like
id (int) | referencingNames (srting) |
---|---|
1 | Mark, Mona, Sam |
2 | Jim, Tom, Sara, Mike |
3 | ... |
My Object to map to looks like this
class someObject {
public int $id;
public array $referencingNames;
}
When I call my php code then:
$pdo = new PDO(....)
$statement = $pdo->prepare($query);
$statement->execute();
$objects = $statement->fetchAll(PDO::FETCH_CLASS, someObject::class);
I am running into a type error, as referencingNames
is obviously a string
.
What I then tried was to set $referencingNames
private and use the magic function __set()
as it says in the php docs the following
is run when writing data to inaccessible (protected or private) or non-existing properties
class someObject {
public int $id;
private string $referencingNames;
public ?array $refNamesList;
public function __set($name, $value)
{
if($name == "referencingNames") {
$this->referencingNames = $value;
$this->refNamesList = explode(",", $value);
} else {
$this->$name = $value;
}
}
}
The bad news: this didn't work out. I get back an object where refNamesList stays null. Logging the call of __set()
did not give me any output, so I assume, it does not get called.
Has anybody an Idea how I can map GROUP_CONCAT
to an array with PDOs fetchAll()
without building my own solution?
I mean, fetching all, and iterating the whole thing is still an option, but I was wondering if I can do this more elegantly anyhow.
As the name of the column you are loading is part of the class, it's setting that value anyway without having to call the __set
method. So one way (seems to work) is to add a column alias which doesn't exist in the class - nameList in this example...
$query = "Select a.id, GROUP_CONCAT(b.name) AS nameList
FROM a
JOIN b on (a.id = b.id_a)"
this should then call the __set
method and you can process when you get nameList...
class someObject {
public int $id;
public array $referencingNames;
public function __set($name, $value)
{
if($name == "nameList") {
$this->referencingNames = explode(",", $value);
} else {
$this->$name = $value;
}
}
}