Задать вопрос
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%) )))

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

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

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

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

Собрался писать класс для работы с БД? Отлично, пиши класс для работы с БД. БЕЗ неймспейсов и автолоадов. Не переломишься, добавишь один инклюд. Но зато хотя бы не будешь бегать по коду и искать, в каком из 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. Вполне вероятно, что можно их не сохранять вообще.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

Похожие вопросы