Klasse + MySQL + Performance :: Wie gehts richtig?

String

Erfahrenes Mitglied
Hallo zusammen,

so eine ähnliche Frage habe ich schon einmal gestellt, doch nun etwas konkreter. Im Netz findet man viele Antworten und natürlich ist seine immer richtig. Begründen, kann es allerdings keiner von denen so wirklich..

Also nehmen wir einfachheitshalber einen User in einer PHP-Community, Forum oder sonst was..
Der User loggt sich ein und surft auf der Seite herum.

(Beispielhaft(!!))
PHP:
session_start();
# DB Verbindung aufbauen

if($_SESSION['login'] === true) {
   $myUser = new User($_SESSION['loginID']);
}

Nun muss ich ja an irgendwo alle "User-Daten" aus der Datenbank laden. Das könnte ich BEVOR ich das User-Objekt erstelle:
PHP:
$userData = mysql_query("SELECT * FROM user WHERE id={$_SESSION['loginID']}");
$myUser = new User($userData['id'], $userData['name'], ..., , ,);

Ich könnte aber auch einfach IN der User-Klasse die Daten laden.
PHP:
__construct($id) {
$userData = mysql_query("SELECT * FROM user WHERE id={$id}");
$this->name = $userData...
}

Leider komme ich ja kaum darum herum bei jedem Seitenaufruf alles aus der DB zu laden.
Doch manch mal bereitet mir OOP Kopfschmerzen..

Könnte mir BITTE jemand erklären WO ich die Daten aus der DB holen und vorallem WARUM ich es an dieser Stelle machen?
Rein instinktiv würde ich es IM Objekt machen, denn sonst müsste ich es bei Bedarf in hunderten Dateien ändern.

Und wie ich die Daten aus der DB hole, also ob mit oder ohne "SQL-Klasse" sei erstmal dahin gestellt ;)

Grüße

paD
 
Natürlich im Objekt selbst ;)
Warum solltest du alle Daten schonmal vorher aus der Datenbank holen, wenn du sie garnicht brauchst? Ist unwahrscheinlich (in diesem beispiel), aber klingt doch irgendwie logisch die daten für ein Objekt es zu laden, wenn sebiges instanziiert wird und nicht vorher.
 
Okay, so habe ich es mir auch gedacht ;)

Sollte man eigentlich einen Unterschied darin machen, den eigenen oder einen anderen User zu laden?
Denke ich z.B. an ein "Profil-Update". Diese Funktion wäre total unsinnig für einen "anderen" User..!?

paD
 
Kommt drauf an...

Du könntest eine basisklasse user erstellen und dann Vererbungsklassen wie RegisteredUser und UnknownUser oder so erstellen. oder du pakcst alles in eine Klasse. Je nachdem, wieviel Abstraktion du brauchst.
 
Leuchtet mir ein.. okay :)

Und noch so ne Frage..:

Lade ich beim erstellen gleich alle User-Daten?
(Wie z.B. hier steht oben rechts immer mein Name) mehr wird eigentlich nicht gebraucht. (Vielleicht ID,...)
beim __construct() könnte ich also nur die ID und den Namen laden und bei bedarf mehr.

Aber dann hab ich auf jeder Seite gleich mehrere DB-Querys, sollte ich doch mal ein paar mehr User-Infos haben wollen..?

paD
 
Hi, ich hab was für dich, was dich interessieren könnte. Wenn du PDO benutzt, kannst du ein Fetch mittels Mapper-Klasse machen. Das geht so:

PHP:
<?php
class DB extends PDO
{
  private static $_connection;
  
  /**
   * Create a new database instance
   * @return PDO
   */
  public static function getConnection()
  {
    if(!self::$_connection)
      self::$_connection = new PDO('mysql:host=localhost;dbname=test', 'test', null, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
    
    return self::$_connection;
  }
}

class User
{
  public $id;
  public $name;
  public $passwd;
  public $email;
  
  public function __construct($id)
  {
    $db = DB::getConnection();
    $statement = $db->prepare("SELECT * FROM users WHERE id = ?");
    $statement->bindValue(1, $id);
    $statement->setFetchMode(PDO::FETCH_INTO, $this);
    $statement->execute();
    $statement->fetch();
  }
}

$a = new User(1);
echo "<pre>";
var_dump($a);
echo "</pre>";

Test-Daten:
Code:
--
-- Tabellenstruktur für Tabelle `users`
--

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `passwd` varchar(64) NOT NULL,
  `email` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

--
-- Daten für Tabelle `users`
--

INSERT INTO `users` (`id`, `name`, `passwd`, `email`) VALUES
(1, 'testuser_a', 'passwd1234', 'someone@somewhere.tld'),
(2, 'testuser_b', '9876dwssap', 'anotherguy@nongov.tld');

Ausgabe

Code:
object(User)#1 (4) {
  ["id"]=>
  string(1) "1"
  ["name"]=>
  string(10) "testuser_a"
  ["passwd"]=>
  string(10) "passwd1234"
  ["email"]=>
  string(21) "someone@somewhere.tld"
}

Nachteil hier ist, das die Klassen-Member von "User" public sein müssen, sonst funktioniert der FETCH_INTO nicht. Allerdings kann man das auch prima anders lösen. Ungefähr so:

PHP:
class User
{
  protected $id;
  protected $name;
  protected $passwd;
  protected $email;
  
  public function __construct($id)
  {
    $db = DB::getConnection();
    $statement = $db->prepare("SELECT * FROM users WHERE id = ?");
    $statement->bindValue(1, $id);
    $statement->setFetchMode(PDO::FETCH_OBJ);
    $statement->execute();
    $data = $statement->fetch();
    foreach ($data as $propName => $propValue)
    {
      $this->{$propName} = $propValue;
    }
  }
}

Wenn du abstrahieren willst, kannst du so vorgehen. Angenommen, du hast eine Basis-Klasse "User" die nur ID und Name beinhalten soll. Dann hast du eine Klasse "LoginUser", die auch Passwort und Email beinhalten soll. Beispiel:

PHP:
class User
{
  protected $id;
  protected $name;
  
  public final function __construct($id)
  {
    $sql_columns = '';
    foreach (get_class_vars(get_class($this)) as $columnname => $dummy)
    {
      $sql_columns .= (strlen($sql_columns) ? ',' : '') . $columnname;
    }    
    
    $db = DB::getConnection();
    $statement = $db->prepare("SELECT " . $sql_columns . " FROM users WHERE id = ?");
    $statement->bindValue(1, $id);
    $statement->setFetchMode(PDO::FETCH_OBJ);
    $statement->execute();
    $data = $statement->fetch();
    foreach ($data as $propName => $propValue)
    {
      $this->{$propName} = $propValue;
    }
  }
}

class LoginUser extends User
{
  protected $email;
  protected $passwd;
}

$a = new User(1);
echo "<pre>";
var_dump($a);
echo "</pre>";

$b = new LoginUser(2);
echo "<pre>";
var_dump($b);
echo "</pre>";

Ausgabe:

Code:
object(User)#1 (2) {
  ["id":protected]=>
  string(1) "1"
  ["name":protected]=>
  string(10) "testuser_a"
}

object(LoginUser)#4 (4) {
  ["email":protected]=>
  string(21) "anotherguy@nongov.tld"
  ["passwd":protected]=>
  string(10) "9876dwssap"
  ["id":protected]=>
  string(1) "2"
  ["name":protected]=>
  string(10) "testuser_b"
}

Das kann man natürlich noch verbessern, insbesondere den Teil, der die Spaltennamen zusammen setzt. Das könnte man statt dessen mit Reflection lösen.
 
Lade ich beim erstellen gleich alle User-Daten?
...
Aber dann hab ich auf jeder Seite gleich mehrere DB-Querys, sollte ich doch mal ein paar mehr User-Infos haben wollen..?
Wenns nur Sachen wie ID, Name usw. sind, gleich alles.
Wenn pro User ein paar MB Daten dabei sind, die nur sehr selten gebraucht werden,
schaut die Sache anders aus.
 
@saftmeister
Das ist doch mal ne Ansage!
Wieso findet man so etwas nicht, wenn man danach sucht? :D
Das hat mir sehr geholfen, Dankeschön :)

Werd mal sehen, wie weit ich damit komme ;)

paD
 
Das Stichwort für die Suche müsste lauten "OR Mapper". Wobei OR für Object Relationship steht. Also einen Mapper für Datenbank-Tabellen auf Objekte innerhalb deines PHP-Scripts.
 

Neue Beiträge

Zurück