Autor Zpráva
Jirik1
Profil *
Ahoj, zkouším spustit insert pomoci PDO a nechce se mi to rozjet. Za pomoc budu moc rád.
Chybové hlášení:
Notice: Undefined property: Insert::$pdo in C:\wamp64\www\oop\pdo.php on line 32
Fatal error: Uncaught Error: Call to a member function prepare() on null in C:\wamp64\www\oop\pdo.php on line 32
Error: Call to a member function prepare() on null in C:\wamp64\www\oop\pdo.php on line 32

Kód:
<?php

class Dbi {

    private $pdo;

    public function __construct() {

        define('SQL_HOST', 'localhost');
        define('SQL_DBNAME', 'database');
        define('SQL_USERNAME', 'root');
        define('SQL_PASSWORD', '');

        $dsn = 'mysql:dbname=' . SQL_DBNAME . ';host=' . SQL_HOST . '';
        $user = SQL_USERNAME;
        $password = SQL_PASSWORD;

        try {
            $this->pdo = new PDO($dsn, $user, $password);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $e) {
            die('Spatne databazove spojeni: ' . $e->getMessage());
        }

    }

}

class Insert extends Dbi {

    public function vloz() { 
        $dotaz = $this->pdo->prepare("INSERT into uzivatele (id, jmeno, heslo, akce) VALUES(?, ?, ?, ?)");
        $vysledek = $dotaz->execute(array(NULL, '1', '1', '1'));           
    }

}

$connect = new Insert;
$connect->vloz();

Když vložím:
$dotaz = $this->pdo->prepare("INSERT into uzivatele (id, jmeno, heslo, akce) VALUES(?, ?, ?, ?)");
$vysledek = $dotaz->execute(array(NULL, '1', '1', '1'));        
hned za TRY ve třídě Dbi, pak vše funguje.
Moc díky.


Už to mám, chyba byla v tom private pdo.
Joker
Profil
Jirik1:
Problém je celkem zřejmý z chybové hlášky, ve třídě Insert metoda vloz používá nedefinovaný atribut pdo.

Atribut stejného jména sice je na rodičovské třídě Dbi, nicméně je private, takže pro třídu Insert není přístupný.

Mimo téma mi nepřijde moc praktické mít přihlašovací údaje natvrdo uvnitř databázové třídy (čímž pak ta třída nejde vzít a použít pro připojení k jiné databázi).
Jirik1
Profil *
Joker:
Ok, přepsal jsem to:
<?php

class DataDatabaseUzivatele {

    public function __construct() {

        define('SQL_HOST', 'localhost');
        define('SQL_DBNAME', 'database');
        define('SQL_USERNAME', 'root');
        define('SQL_PASSWORD', '');

    }

}

class Dbi extends DataDatabaseUzivatele {

    protected $pdo;

    public function __construct() {

        parent::__construct();

        $dsn = 'mysql:dbname=' . SQL_DBNAME . ';host=' . SQL_HOST . '';
        $user = SQL_USERNAME;
        $password = SQL_PASSWORD;

        try {
            $this->pdo = new PDO($dsn, $user, $password);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $e) {
            die('Spatne databazove spojeni: ' . $e->getMessage());
        }

    }

}
Joker
Profil
Jirik1:
To má dva problémy:
1. To o čem jsem psal to neřeší. Třída Dbi pořád nejde beze změny vzít a použít pro jiné připojení. Závislost se sice přesunula do rodičovské třídy (resp. ne tak docela, ale asi byl takový úmysl), jenže pak bych na změnu údajů třídě Dbi potřeboval změnit rodiče, což nejde jednoduše udělat.
2. Ta třída DataDatabaseUzivatele je zbytečná. Stačilo by ty define vyhodit před ní a zbytek smazat (včetně odkazů v Dbi). Definice konstant má globální platnost. Pak by to i trochu fungovalo, třídu Dbi by šlo beze změny použít i s jinými údaji: Okolní kód by prostě definoval jiné hodnoty těch konstant.

Ještě pár tipů:
• Objekty by měly být uzavřené, dá se říci „samonosné“. Vnitřek objektu by neměl záviset na okolním kódu a veškerá komunikace mezi objektem a okolí by se měla odehrávat přes volání metod nebo práci s atributy.
• Z předchozího bodu vyplývá, že není ideální uvnitř objektu používat globální konstanty. (Protože se nabourává hranice mezi objektem a okolím, funkčnost objektu závisí na okolním kódu.)
• Dál z toho vyplývá, že nejlepší cesta pro předání nějakých informací do objektu jsou parametry metod.
Čili lepší řešení by bylo definovat __construct($dsn, $user, $password) a pak můžu objekt používat s libovolnými údaji, které mu jednoduše předám ve volání konstruktoru.

• Kromě toho bych si položil otázku, jaký účel ten objekt má, resp. co je jeho odpovědnost.
• Přitom existuje tzv. „single responsibility principle“, tj. princip jedné odpovědnosti: Každý objekt má být odpovědný za právě jednu věc.
Dá se to poznat právě tím, že si zkusíte jednou větou říct, jakou odpovědnost ten objekt, má, co má na starosti, potažmo „co ten objekt vlastně dělá“.
Odpověď třeba: „Tenhle objekt slouží jako repozitář uživatelů, má na starosti získání objektů požadovaných uživatelů a případně ukládání změn“ vypadá vcelku dobře.
Jakmile to začne být „Tenhle objekt převezme požadavek od uživatele a dohledává data v databázi a zobrazuje nějaký výsledek a taky loguje chybové hlášky a ještě mi vaří kafe“, zjevně ten objekt toho má na starosti příliš.
• Na druhé straně když přidělíte konkrétní třídě konkrétní odpovědnost, je pak fajn se na tu třídu podívat, jestli skutečně dělá jen tu svou práci a „nefušuje“ i do jiných odpovědností.
Například řeknete, že tenhle objekt je repozitář uživatelů, a pak zjistíte, že kromě toho ještě prezentuje údaje uživateli, zpracovává případné chyby, atd.

• Čili bych si položil otázku, co má mít na starosti ta třída Dbi. Přitom „obecné rozhraní pro komunikaci s databází“ není dobrá odpověď, protože na to už tam je ta třída PDO, tak k čemu další?
Methez
Profil *
Hlavně tam na začátku chybí use PDO;
Joker
Profil
Methez:
Podle mě to zdaleka není hlavní problém toho kódu a dokud nezačne používat jmenné prostory (namespaces), je to celkem zbytečné.
Methez
Profil *
Joker:
Souhlas :)

Jirik1:
Stáhni si hotovou PDO knihovnu. Vše je tam již pořešené a hlavně bezpečně. Nemá cenu znovu vynalézat kolo :)
Jirik1
Profil *
Hoši, máte pravdu. Musel jsem se nad sebou zamyslet. Celé jsem to přepsal a teď to dává i smysl :-). Ještě to piluji.
Soubor dbi.php:
<?php

class Dbi {

    protected $pdo;

    public function __construct($sqlDbname, $sqlHost, $sqlUserName, $sqlPassword) {

        $dsn = 'mysql:dbname=' . $this->sqlDbname . ';host=' . $this->sqlHost . '';
        $user = $this->sqlUserName;
        $password = $this->sqlPassword;

        try {
            $this->pdo = new PDO($dsn, $user, $password);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $e) {
            die('Spatne databazove spojeni: ' . $e->getMessage());
        }

    }

}
Soubor lPDatabse.php:
<?php

require_once 'dbi.php';

class DataDatabaseUzivatele extends Dbi {

    protected $sqlHost = 'localhost';
    protected $sqlDbname = 'database';
    protected $sqlUserName = 'root';
    protected $sqlPassword = '';

    public function __construct() {

        parent::__construct($this->sqlHost, $this->sqlDbname, $this->sqlUserName, $this->sqlPassword);

    }

}
Soubor pdoInsert.php:
<?php

require_once 'lPDatabase.php';

class Insert extends DataDatabaseUzivatele {

    public function vloz($jmeno, $heslo, $akce) { 

        if ($this->pdo) {
            $dotaz = $this->pdo->prepare("INSERT into uzivatele (id, jmeno, heslo, akce) VALUES(?, ?, ?, ?)");
            $dotaz->execute(array(NULL, $jmeno, $heslo, $akce)); 
            echo 'Zaznam byl vlozen.';
        } else {
            echo 'Zaznam nebyl vlozen.';
        }   
               
    }

}

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0