0

I'm currently working with one of my friends on making a portfolio for all of his projects and as strangely as it seems, I cannot manage to make setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Class Namespace') working on his code while it is working on my projects.

Here is the error returned by PHP : !(https://cdn.discordapp.com/attachments/525023910698156034/583938068311179265/unknown.png)

Here is the class Entity which contains the __construct() function and the hydration() function :

<?php

namespace App\Entity;

class Entity {

    public function __construct(array $array) {
        $this->hydrate($array);
    }

    public function hydrate($array) {
        foreach ($array as $key => $value) {
            $setter = 'set' . ucfirst($key);
            if (method_exists($this, $setter)) {
                $this->$setter($value);
            }
        }
    }
}

Then, here is the Project Class which implements the class Entity :

<?php

namespace App\Entity;

class Project extends Entity implements \JsonSerializable {

    private $_title;
    private $_description;
    private $_imagePath;
    private $_link;
    private $_repoLink;
    private $_creationDate;

    public function jsonSerialize() {
        return [
            'title' => $this->_title,
            'description' => $this->_description,
            'imagePath' => $this->_imagePath,
            'link' => $this->_link,
            'repoLink' => $this->_repoLink,
            'creationDate' => $this->_creationDate,
        ];
    }

    /
     * @return string
     */
    public function getTitle(): string {
        return $this->_title;
    }

    /
     * @param string $title
     */
    public function setTitle(string $title) {
        $this->_title = $title;
    }

    /
     * @return string
     */
    public function getDescription(): string {
        return $this->_description;
    }

    /
     * @param string $description
     */
    public function setDescription(string $description) {
        $this->_description = $description;
    }

    /
     * @return string
     */
    public function getImagePath(): string {
        return $this->_imagePath;
    }

    /
     * @param string $imagePath
     */
    public function setImagePath(string $imagePath) {
        $this->_imagePath = $imagePath;
    }

    /
     * @return string
     */
    public function getLink(): string {
        return $this->_link;
    }

    /
     * @param string $link
     */
    public function setLink(string $link) {
        $this->_link = $link;
    }
/
     * @return string
     */
    public function getRepoLink(): string {
        return $this->_repoLink;
    }

    /
     * @param string $repoLink
     */
    public function setRepoLink(string $repoLink) {
        $this->_repoLink = $repoLink;
    }

    /
     * @return \DateTime
     */
    public function getCreationDate(): \DateTime {
        return $this->_creationDate;
    }

    /
     * @param string $creationDate
     */
    public function setCreationDate(string $creationDate) {
        $this->_creationDate = new \DateTime($creationDate);
    }
}

And finally, here is the SQL request :

<?php

namespace App\Model;

class ProjectManager extends Manager {

    /**
     * return a collection of Project objects
     * @return Project[]
     * @throws \Exception
     */
    public function getProjects() {
        $db = $this->getDb();
        $q = $db->query(
            'SELECT id,
                     title,
                     description,
                     image_path AS imagePath,
                     link,
                     repo_link AS repoLink,
                     creation_date AS creationDate
            FROM my_website_projects
            ORDER BY creationDate'
        );

        $q->setFetchMode(\PDO::FETCH_CLASS | \PDO::FETCH_PROPS_LATE, 'App\Entity\Project');
        $projects = $q->fetchAll();

        return $projects;
    }
}

The only thing that seems to work is to add PDO::FETCH_ASSOC in the fetchAll() but then it doesn't return an object but an array....

Your help would be much appreciated on this problem ! :)

  • First of all I would recommend to create an [MCVE](https://phpdelusions.net/pdo/mcve). Posting several code snippets is one thing but posting the fully working example is quite another. – Your Common Sense May 31 '19 at 09:01
  • By the way, you can omit setFetchMode call and pass these parameters into fetchAll() already. Not that I think it would help but just saying. – Your Common Sense May 31 '19 at 09:11

1 Answers1

0

As far as I know, there is no solution to this problem. There is no fetch mode that creates an object passing the returned row as a constructor parameter.

So I would change the code to this a bit clumsy but working solution

public function getProjects() {
    $db = $this->getDb();
    $q = $db->query(
        'SELECT id,
                 title,
                 description,
                 image_path AS imagePath,
                 link,
                 repo_link AS repoLink,
                 creation_date AS creationDate
        FROM my_website_projects
        ORDER BY creationDate'
    );
    $projects = [];
    while($row = $q->fetch(PDO::FETCH_ASSOC)) {
        $projects[] = new App\Entity\Project($row);
    }
    return $projects;
}

Your mistake is that you slightly confused the way PDO creates objects.

This way is rather blunt, PDO just takes an object and fills it properties, the process is not much different from filling an associative array.

So now you can tell how did your other code work:

  • first, the constructor parameter in your other class is optional, it means PHP won't complain for it.
  • second, in your other class properties are spelled equal to column names in the database, and PDO happily fills them as described above.

So, as another solution you can fix these 2 issues: make the constructor parameter optional and remove the underscore from the property names.

Some time ago I wrote an article on fetching obejects with PDO, you may find it useful.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • I'm sorry to say that but as I said previously, on my projects it is working ! I am returning an object. So what we are not understanding here is why it isn't working on my friend's project... – Virginie P. May 31 '19 at 09:46
  • You must post here *what exactly* this "it" is. PDO doesn't send a returned row as a constructor parameter, which is required by this code. So your code is just different – Your Common Sense May 31 '19 at 09:49
  • Here is a SQL request from one of my projects that is working well : http://sandbox.onlinephpfunctions.com/code/d29601046e8a73d2fe616ed495aa48c2dfca920c Everything is the same as far... – Virginie P. May 31 '19 at 09:55
  • what are constructor parameters for App\Entity\User? I assume there are none. This is *the* difference. – Your Common Sense May 31 '19 at 10:39
  • No, here is the __construct() function : `public function __construct(array $data = []) { $this->hydrate($data); }` – Virginie P. May 31 '19 at 12:43
  • First, your parameter is not mandatory. Second, I suppose your properties are spelled equal to field names, so PDO can fill them directly. and here properties are underscored while column names are not. Means even if you even make the parameter optional, PDO will just create a duplicated set of public properties. So now you can tell your other code is quite different. – Your Common Sense May 31 '19 at 12:49