Ramapriya
@Ramapriya

Как работать с PDO внутри класса?

Всем привет.

Осваиваю PDO параллельно с ООП.

Создал класс DB, в котором прописал следующие настройки:

namespace App\Core;

use PDO;

class DB {
    protected $host;
    protected $user;
    protected $password;
    protected $dbname;
    protected $charset;
    protected $dsn;
    protected $opt;
    

    public function __construct() {
        $this->host = "localhost";
        $this->user = "root";
        $this->password = "";
        $this->dbname = "dbtest";
        $this->charset = "utf8";
        
        $this->dsn = "mysql:host=$this->host;dbname=$this->dbname;charset=$this->charset";
        
        $this->opt = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => FALSE
        ];
        
        $pdo = new PDO($this->dsn, $this->user, $this->password, $this->opt);
    }
    
    public function SELECT($Query) {
        $stmt = $pdo->query("$Query");
    }

    public function __destruct() {
        $pdo = NULL;
    }
}


Перед этим сделал функцию автозагрузки, чтобы не инклюдить кучу файлов:

function classLoad($Class) {
    include $_SERVER["DOCUMENT_ROOT"]."/".mb_strtolower($Class).".php";
}

spl_autoload_register("classLoad");


Потом в другом классе наследовал класс DB и попытался создать метод с использованием метода из DB:

namespace App;
use App\Core;

class Settings extends Core\DB {
    
    public function getSiteName() {
        $this->SELECT("SELECT Value FROM configs WHERE Name = 'SiteName'");
        while ($result = $this->SELECT()->fetch()) {
            echo $result["Value"];
        }
    }
}


И индексная страница, где по идее должен вызываться нужный метод:

require_once $_SERVER["DOCUMENT_ROOT"]."/autoload.php";

$TEST = new App\Settings();
$TEST->getSiteName();


Однако когда я попытался вывести этот метод, то браузер выдал такую ошибку:

5cdbe21951b31956685733.png

Я пока не особо хорошо знаю PHP, поэтому допускаю вариант, что скорее всего, где-то я мог допустить ошибку (99,9%) )))

В общем, буду рад, если расскажете, какие ошибки я умудрился допустить, а также покажете, в каком направлении копать, чтобы их исправить.

Заранее спасибо.
  • Вопрос задан
  • 647 просмотров
Решения вопроса 2
FanatPHP
@FanatPHP
Чебуратор тега PHP
Не нужно осваивать всё сразу. У тебя не будет работать тоже все сразу. И ты не будешь знать, что именно.

Всегда надо решать только одну задачу за раз

А у тебя тут и ООП, и ПДО, а неймспейсы ,и автолоад, и ни одну из этих вещей ты не понимаешь.

Собрался писать класс для работы с БД? Отлично, пиши класс для работы с БД. БЕЗ неймспейсов и автолоадов. Не переломишься, добавишь один инклюд. Но зато хотя бы не будешь бегать по коду и искать, в каком из 10 мест у тебя ошибка.

Только после того, как класс у тебя за заработает, добавляешь неймспейсы. И мучаешься уже с ними.
После того как заработает с неймспейсами, осваиваешь автолоад.

Свой велосипед, чтобы он был хоть на что-то годен и добавлял хоть что-то к оригинальному ПДО, переписываешь так
class DB
{
    public $pdo;

    public function __construct() {
        $host = "localhost";
        $user = "root";
        $password = "";
        $dbname = "dbtest";
        $charset = "utf8";
        
        $dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
        $this->opt = [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => FALSE
        ];
        $this->pdo = new PDO($dsn, $user, $password, $opt);
    }
    
    public function query($sql, $args = NULL)
    {
        if (!$args)
        {
             return $this->pdo->query($sql);
        }
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($args);
        return $stmt;
    }
    public function __destruct() {
        $pdo = NULL;
    }
}

Объяснения по коду (на английском) - Детские болезни моего первого класса для работы с БД

Дальше.
Класс Settings не должен наследовать классу БД. Это совершенно разные сущности. Класс человек не должен наследовать классу Карман только потому что у всех людей есть карманы обычно.

Если человеку нужен карман, то карман передается в конструктор добавляется в свойства класса. Поэтому

сlass Settings {
    public function __construct($db) {
        this->db = $db;
    }
    public function getSiteName() {
        $stmt = $this->db->query("SELECT Value FROM configs WHERE Name = 'SiteName'");
        return $stmt->fetchColumn();
    }
}
Ответ написан
skobkin
@skobkin
Гентушник, разработчик на PHP и Symfony.
У вас внутри конструктора есть вот эта строчка:
$pdo = new PDO($this->dsn, $this->user, $this->password, $this->opt);

Здесь создаётся переменная $pdo находящаяся исключительно в скоупе самого конструктора и после того как завершается его выполнение, сборщик мусора уничтожает объект, который вы создали.

В методе SELECT:
public function SELECT($Query) {
        $stmt = $pdo->query("$Query");
    }

Вы обращаетесь к переменной, которая как бы существует только в скоупе этого метода (на самом деле, её нет). Естественно, что ничего не работает.

Вам нужно:
  1. Создать свойство, в котором будет храниться после инициализации в конструкторе объект PDO у вашего недоабстрактного класса DB
  2. В методе SELECT обращаться к этому объекту как к $this->pdo (или как вы его назовёте)


Почитайте про скоупы переменных. А ещё про ООП и области видимости. И не лепите protected для всего, что под руку попадётся. Вполне вероятно, что в наследниках вам нужен доступ только к SELECT(), а свойство содержащее объект PDO и остальные не нужны. В этом плане можете ещё про deffensive programming (пример).

P.S. Вам, кстати, эта толпа свойств dsn, user, password, opt и прочие, скорее всего не нужны после инициализации PDO. Вполне вероятно, что можно их не сохранять вообще.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы