访问fetchObject()中的PDO连接 - 实例

I'm refactoring parts of my application and replace some fetch(PDO::FETCH_ASSOC) to dedicated classes with fetchObject().

Is there a way to access the pdo instance inside the generated class? See this example:

class User
{ 
    private $id;
    private $username;
    private $firstname;
    private $lastname;

    public function setFirstname($newValue)
    { 
        if (empty($newValue) || !is_string($newValue)) throw new Exception('Wrong');
        $this->firstname = $newValue;
    } 

    public function save()
    {
        // IMPORTANT PART:
        // I don't want to use the global object container here  
        $dbh = Application::getInstance()->getPDO();

        $sth = $dbh->prepare('UPDATE main_user SET firstname = :firstname WHERE id = :id');
        $sth->execute([ 
            'id'        => $this->id,
            'firstname' => $this->firstname,
        ]);
    } 
} 

$dbh = $application->getPDO();
$sth = $dbh->prepare('SELECT * FROM main_user WHERE id = ?');
$sth->execute([ 10 ]);
$user = $sth->fetchObject('User');
$user->setFirstname('Test');
$user->save();

Parts of my application use multiple databases and thus multiple pdo objects. To get reuseable code, I'd like to prevent using my global container class - and global of course.

You can pass the PDO Instance so you don't have to call Application::getInstance()->getPDO() again.

public function save(PDO $dbh)
{
    $sth = $dbh->prepare('UPDATE main_user SET firstname = :firstname WHERE id = :id');
    $sth->execute([ 
        'id'        => $this->id,
        'firstname' => $this->firstname,
    ]);
} 
// and pass the (already available) $dbh to the save-Method at the buttom
$user->save($dbh);

or as CD001 mentioned in the comments, you can also pass it to the constructor:

class User
{
    // ...
    private $conn;

    public function __construct(PDO $conn)
    {
        $this->conn = $conn;
    }
}
// ... later at the bottom when fetching the Object:
$user = $sth->fetchObject('User', [ $dbh ]);

You should have an user repository which will save the user to db:

use \Path\To\User\Model\User;

class UserRepository extends AbstractRepository
{
    public function save(User $user)
    {
        $sth = $this->db->prepare('UPDATE main_user SET firstname = :firstname WHERE id = :id');
        $sth->execute([ 
            'id'        => $user->id, // make sure these properties are public on the model
            'firstname' => $user->firstname,
        ]);
    }
}

class AbstractRepository
{
    protected $db;

    // inject it using Dependency Injection
    public function __construct(DbInterface $db)
    {
        $this->db = $db;
    }
}

Then, in your Model you can use the user repository to save it to db:

namespace \Path\To\User\Model;

class User
{
    public $id;
    public $username;
    public $firstname;
    public $lastname;

    protected $userRepository;

    // inject it with DI
    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function setFirstname($firstname)
    { 
        if (empty($firstname) || !is_string($firstname)) {
            throw new Exception('Wrong firstname!');
        }

        $this->firstname = $firstname;

        return $this;
    }

    public function save()
    {
        return $this->userRepository->save($this);
    } 
}

Now you need to register these classes to a Dependency Injection Framework such as PHP-DI and to use them appropriately..

$user = $container->get('User');
$user->setFirstname('MyUser');
$user->create();

If you are refactoring your app then you should do it right.

Please note that the examples above are just informative and they need many improvements to be used in production.