Zend Tutorial – 02 – Datenbankhandling

1. Die Dateien, die für den Datenbankzugriff verantwortlich sind, sollen sich im Modulordner „bibo“ im Unterordner „models“ befinden. Dieser Ordner ist aktuell noch leer. Das wird sich recht schnell ändern. Legt im Ordner application/modules/bibo/models einen Unterordner „DbTable“ an. Achtet auch auf die casesensitive Schreibweise. In diesem Ordner machen wir jede Tabelle seperat bekannt.

Wir legen dazu eine Datei Buecher.php an und befüllen sie wie folgt:

class Bibo_Model_DbTable_Buecher extends Zend_Db_Table_Abstract
{
	protected $_name = 'buecher';
}

Analog dazu legen wir eine Exemplare.php an und schreibe folgendes in die Datei:

class Bibo_Model_DbTable_Exemplare extends Zend_Db_Table_Abstract
{
	protected $_name = 'exemplare';
}

Damit sind die Tabellen beim ZendFramework bekannt und können nun auf entsprechende Objekte gemappt werden. Dafür legen wir im Ordner application/modules/bibo/models die Dateien Buecher.php, BuecherMapper.php, Exemplare.php und ExemplareMapper.php an.

class Bibo_Model_Buecher
{
	protected $_id;
	protected $_name;
	protected $_author;
	protected $_beschreibung;

	public function __construct(array $options = null)
	{
		if (is_array($options)) {
			$this->setOptions($options);
		}
	}

	public function __set($name, $value)
	{
		$method = 'set' . $name;
		if (('mapper' == $name) || !method_exists($this, $method)) {
			throw new Exception('Invalid buecher property');
		}
		$this->$method($value);
	}

	public function __get($name)
	{
		$method = 'get' . $name;
		if (('mapper' == $name) || !method_exists($this, $method)) {
			throw new Exception('Invalid buecher property');
		}
		return $this->$method();
	}

	public function setOptions(array $options)
	{
		$methods = get_class_methods($this);
		foreach ($options as $key => $value) {
			$method = 'set' . ucfirst($key);
			if (in_array($method, $methods)) {
				$this->$method($value);
			}
		}
		return $this;
	}

	public function setId($id)
	{
		$this->_id = (int) $id;
		return $this;
	}

	public function getId()
	{
		return $this->_id;
	}

	public function setName($name)
	{
		$this->_name = (string) $name;
		return $this;
	}

	public function getName()
	{
		return $this->_name;
	}

	public function setAuthor($author)
	{
		$this->_author = (string) $author;
		return $this;
	}

	public function getAuthor()
	{
		return $this->_author;
	}
		
	public function setBeschreibung($beschreibung)
	{
		$this->_beschreibung = (string) $beschreibung;
		return $this;
	}

	public function getBeschreibung()
	{
		return $this->_beschreibung;
	}
}
class Bibo_Model_Exemplare
{
	protected $_id;
	protected $_exem_buch_id;
	protected $_ausgeliehen;
	protected $_kunde;
		
	public function __construct(array $options = null)
	{
		if (is_array($options)) {
			$this->setOptions($options);
		}
	}
	 
	public function __set($name, $value)
	{
		$method = 'set' . $name;
		if (('mapper' == $name) || !method_exists($this, $method)) {
			throw new Exception('Invalid exemplare property');
		}
		$this->$method($value);
	}
	 
	public function __get($name)
	{
		$method = 'get' . $name;
		if (('mapper' == $name) || !method_exists($this, $method)) {
			throw new Exception('Invalid exemplare property');
		}
		return $this->$method();
	}
	 
	public function setOptions(array $options)
	{
		$methods = get_class_methods($this);
		foreach ($options as $key => $value) {
			$method = 'set' . ucfirst($key);
			if (in_array($method, $methods)) {
				$this->$method($value);
			}
		}
		return $this;
	}
	 
	public function getId()
	{
		return $this->_id;
	}
		
	public function setId($id)
	{
		$this->_id = (int) $id;
		return $this;
	}
	 
	public function getexem_buch_id()
	{
		return $this->_exem_buch_id;
	}
		
	public function setexem_buch_id($exem_buch_id)
	{
		$this->_exem_buch_id = (int) $exem_buch_id;
		return $this;
	}
		
	public function getAusgeliehen()
	{
		return $this->_ausgeliehen;
	}
		
	public function setAusgeliehen($ausgeliehen)
	{
		$this->_ausgeliehen = $ausgeliehen;
		return $this;
	}
		
	public function getKunde()
	{
		return $this->_kunde;
	}
		
	public function setKunde($kunde)
	{
		$this->_kunde = (string) $kunde;
		return $this;
	}
}

Diese zwei Dateien beinhalten jeweils den Konstruktor, sowie die Getter- und Setter-Methoden der jeweiligen Klassen.

In die Dateien BuecherMapper.php und ExemplareMapper.php schreiben wir die Methoden zum Datenbankhandling wie folgt:

class Bibo_Model_BuecherMapper
{
	protected $_dbTable;

	public function setDbTable($dbTable)
	{
		if (is_string($dbTable)) {
			$dbTable = new $dbTable();
		}
		if (!$dbTable instanceof Zend_Db_Table_Abstract) {
			throw new Exception('Invalid table data gateway provided');
		}
		$this->_dbTable = $dbTable;
		return $this;
	}
 
	public function getDbTable()
	{
		if (null === $this->_dbTable) {
			$this->setDbTable('Bibo_Model_DbTable_Buecher');
		}
		return $this->_dbTable;
	}

	public function save(Bibo_Model_Buecher $buecher)
	{
		$data = array(
			'name'   => $buecher->getName(),
			'author' => $buecher->getAuthor(),
			'beschreibung' => $buecher->getBeschreibung(),
		);
	 
		if (null === ($id = $buecher->getId())) {
			unset($data['id']);
			$insertId = $this->getDbTable()->insert($data);
			return $insertId;
		} else {
			$this->getDbTable()->update($data, array('id = ?' => $id));
		}
	}
	 
	public function find($id, Bibo_Model_Buecher $buecher)
	{
		$result = $this->getDbTable()->find($id);
		if (0 == count($result)) {
			return;
		}
		$row = $result->current();
		$buecher  ->setId($row->id)
				  ->setName($row->name)
				  ->setAuthor($row->author)
				  ->setBeschreibung($row->beschreibung);
	}
	 
	public function fetchAll(array $where = null)
	{		
		$resultSet = $this->getDbTable()->fetchAll($where);
		$entries   = array();
		foreach ($resultSet as $row) {
			$entry = new Bibo_Model_Buecher();
			$entry->setId($row->id)
				  ->setName($row->name)
				  ->setAuthor($row->author)
				  ->setBeschreibung($row->beschreibung);
			$entries[] = $entry;
		}
		return $entries;
	}
	
	public function getBuchByBuchId($buchId)
	{
		$resultSet = $this->getDbTable()->fetchAll();
		$entries   = array();
		foreach ($resultSet as $row) {
			if($buchId == $row->id)
			{
				$entry = new Bibo_Model_Buecher();
				$entry->setId($row->id)
					  ->setName($row->name)
					  ->setAuthor($row->author)
					  ->setBeschreibung($row->beschreibung);
				$entries[] = $entry;
			}
		}
		return $entries;
	}
}
class Bibo_Model_ExemplareMapper
{
	protected $_dbTable;

	public function setDbTable($dbTable)
	{
		if (is_string($dbTable)) {
			$dbTable = new $dbTable();
		}
		if (!$dbTable instanceof Zend_Db_Table_Abstract) {
			throw new Exception('Invalid table data gateway provided');
		}
		$this->_dbTable = $dbTable;
		return $this;
	}
	 
	public function getDbTable()
	{
		if (null === $this->_dbTable) {
			$this->setDbTable('Bibo_Model_DbTable_Exemplare');
		}
		return $this->_dbTable;
	}

	public function save(Bibo_Model_Exemplare $exemplare)
	{
		$data = array(
			'exem_buch_id' => $exemplare->getexem_buch_id(),
			'ausgeliehen'   => $exemplare->getAusgeliehen(),
			'kunde' => $exemplare->getKunde(),
		);
 
		if (null === ($id = $exemplare->getId())) {
			unset($data['id']);
			$this->getDbTable()->insert($data);
		} else {
			$this->getDbTable()->update($data, array('id = ?' => $id));
		}
	}
	 
	public function find($id, Bibo_Model_Exemplare $exemplare)
	{
		$result = $this->getDbTable()->find($id);
		if (0 == count($result)) {
			return;
		}
		$row = $result->current();
		$exemplare->setId($row->id)
				  ->setexem_buch_id($row->exem_buch_id)
				  ->setAusgeliehen($row->ausgeliehen)
				  ->setKunde($row->kunde);
	}
		
	public function fetchAll()
	{
		$resultSet = $this->getDbTable()->fetchAll();
		$entries   = array();
		foreach ($resultSet as $row) {
			$entry = new Bibo_Model_Exemplare();
			$entry->setId($row->id)
				  ->setexem_buch_id($row->exem_buch_id)
				  ->setAusgeliehen($row->ausgeliehen)
				  ->setKunde($row->kunde);
			$entries[] = $entry;
		}
		return $entries;
	}
		
	public function getExemplareByBuecherId($buecherId)
	{
		$resultSet = $this->getDbTable()->fetchAll();
		$entries   = array();
		foreach ($resultSet as $row) {
			if($buecherId == $row->exem_buch_id)
			{
				$entry = new Bibo_Model_Exemplare();
				$entry->setId($row->id)
					  ->setexem_buch_id($row->exem_buch_id)
					  ->setAusgeliehen($row->ausgeliehen)
					  ->setKunde($row->kunde);
				$entries[] = $entry;
			}
		}
		return $entries;
	}
		
	public function getExemplarByExemplarId($exemId)
	{
		$resultSet = $this->getDbTable()->fetchAll();
		$entries   = array();
		foreach ($resultSet as $row) {
			if($exemId == $row->id)
			{
				$entry = new Bibo_Model_Exemplare();
				$entry->setId($row->id)
					  ->setexem_buch_id($row->exem_buch_id)
					  ->setAusgeliehen($row->ausgeliehen)
					  ->setKunde($row->kunde);
				$entries[] = $entry;
			}
		}
		return $entries;
	}
		
	public function getExemplarAnzahlByBuchId($buchId)
	{
		$zaehler=0;
		$resultSet = $this->getDbTable()->fetchAll();
		foreach ($resultSet as $row) {
			if($buchId == $row->exem_buch_id)
			{
				$zaehler++;
			}
		}
		return $zaehler;
	}
		
	public function getFreieExemplarAnzahlByBuchId($buchId)
	{
		$zaehler=0;
		$resultSet = $this->getDbTable()->fetchAll();
		foreach ($resultSet as $row) {
			if($buchId == $row->exem_buch_id && $row->ausgeliehen =='FALSE')
			{
				$zaehler++;
			}
		}
		return $zaehler;
	}
}

Somit können wir jetzt über die Methoden aus den Mapper-Dateien auf unsere Datenbank zugreifen und müssen nun unseren Controller und unsere View anpassen.

2. In der Datei application/modules/bibo/controllers/IndexController.php ändern wir unsere indexAction() wie folgt:

public function indexAction()
{
	$buechermapper = new Bibo_Model_BuecherMapper();		
	$this->view->entries = $buechermapper->fetchAll();
}

Damit übergeben wir unserer View ein Array entries mit allen unseren Büchern aus der Datenbank.

Nun wechseln wir in den Ordner application/modules/bibo/views/scripts/index/ und bearbeiten die Datei index.phtml

<h1>Hello Bibo-Modul</h1>
<div id="divtabelle">
	<table cellspacing=0 cellpadding=5 id="buchtabelle">
		<thead>
			<tr>
				<th>Buchname</th>
				<th>Author</th>
				<th>Beschreibung</th>
				<th>Verfügbare Exemplare</th>
				<th>Aktionen</th>
			</tr>
		</thead>
		<tbody>
			<?php
			foreach ($this->entries as $entry)
			{?>
				<tr>
					<td><?php echo $entry->name;?></td>
					<td><?php echo $entry->author?></td>
					<td><?php echo $entry->beschreibung?></td>
					<td></td>
					<td></td>
				</tr>
								
	 <?php }?>
		</tbody>
	 </table>
</div>

3. Wir haben nun unsere erste Ausgabe, sofern wir unsere Tabelle bereits mit Daten gefüllt haben. Falls dies nicht der Fall sein sollte, so wechselt in euren SQLite Manager und klickt auf die Tabelle „buecher“ und klickt im Reiter „Durchsuchen“ auf „Datensatz hinzufügen“.

4. Als Abschluss bearbeiten wir im Ordner application/layouts/scripts die Datei bibo.phtml um unser Layout erstmals an unsere Bedürfnisse anzupassen

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Bibliothek</title>
</head>
<body>
<?php echo $this->layout()->content; ?>
</body>
</html>

Link: Weiter zu Teil 3 des Tutorials

2 Kommentare

  1. Hey,

    zunächst einmal Danke für das Tutorial, ich weis den Aufwand zu schätzen, da der Umfang sehr groß ist.

    Allerdings ist es leider – genau wie das Zend-Quickstart – DB-technisch nicht erklärend genug..

    Erkläre bitte, was die find()-Methode in der Mapper-Class macht.

    Erkläre mir bitte, wieso du für „findById()“ ALLE Datensätze in einer Schleife selektierst – dies KANN NICHT performant sein. Um einen Datensatz zu selektieren gibt es find()

    Methodik wäre
    $wantedRow = $table->find($id)->current();
    Zumindest so verstehe ich das. DB-technisch ist das Quickstart leider – meiner Ansicht nach – unglaublich schlecht dokumentiert…

    Weiterhin ist der Ansatz für getExemplareByBuchId($id) der selben Schleifenmethodik zum Opfer gefallen. Um Abhängigkeiten zu selektieren werden die References der einzelnen Tabellen in der Table-Class definiert, so dass wir ein Konstrukt bilden können ala

    $exmpl = $buecher->find($id)->findDependentRowset(‚Exemplare‘);

    Verstehe dies bitte nicht als „Meckern“ wie schlecht dein Tutorial ist, es funktioniert und ist in Ordnung und wird vielen helfen!
    Ich hätte nur gerne meine eigenen Wissenslücken geklärt, da ich den Ansatz hier als nicht performant genug ansehe 🙂

    lieben Gruß
    sam

  2. Danke für deinen Hinweis 🙂 Stimmt auf Performance hab ich noch nicht wirklich geachtet. Allerdings ging es mir um die wesentlichen Funktionsweisen von Zend. Bei Gelegenheit werde ich meine Funktionen nochmal überarbeiten / anpassen.

Kommentar verfassen