phppdomagic-function

How to map GROUP_CONCAT with pdo->fetchAll and mode PDO::FETCH_CLASS from string to an array


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.


Solution

  • 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;
        }
      }
    }