Das Zend Framework
#1
Da es im netz noch recht wenig Tutorials zum ZEND Framework [1] gibt, habe ich mir gedacht, dass ich hier mal die wichtigsten Sachen kurz zusammenfasse.

Part I | Wie ist Zend aufgebaut ?
Das Zend Framework ist ja nach dem MVC [2] Prinzip entwickelt worden. Das schlägt sich auch schon in der Ordnerstrucktur nieder.
Die folgende Strucktur ist die am weitesten verbreitete [S1]. Man kann das aber auch so umarbeiten, wie man es für sich am liebsten hat. Die hier vorgestellte Bootstrap- Datei ist jedoch auf diese Strucktur ausgelegt.

Code:
/htdocs
->index.php ( Bootstrap Datei )
->.htaccess
->/application
->->/models
->->->noch leer
->->/modules
->->->/default
->->->->/controllers
->->->->->indexController.php
->->->->/views
->->->->->/scripts
->->->->->->/index
->->->->->->->index.phtml
->/library
->->.htaccess
Die Ordnerstrucktur + Datein für Einstieg



Inhalt der .htaccess im htdocs Ordner :
Code:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ /index.php [NC,L]
mod_rewrite [3] MUSS aktiviert sein

[S1] = Wenn du Ordner außerhalb des Rootverzeichnises anlegen kannst, so wird empfohlen den Application- und den Librarayordner dort zu platzieren.


Last Update : 25.09
  Zitieren
#2
Die in Part I erstellte .htaccess Datei leitet ja nun alle Anfragen an die index.php weiter. Und diese Datei muss dann ja folglich alle Anfragen bearbeiten. Dazu müssen wir ein paar Sachen im Kopf behalten :

Wir wollen unsere Anwendung immer erweitern können ohne die Bootstrap großartig umschreiben zu müssen.
Nicht nur wir, sondern auch PHP arbeitet von oben nach unten. D.h. wir können nicht erst die Usererkennung machen und dort eine Datenbankverbindung benutzen und erst danach die Datenbankverbindung herstellen.

Also als Schema sollte das ganze so aussehen :

  1. Included Path von PHP erweitern
  2. ZEND Klassen laden
  3. Configurationen machen
  4. Datenbankverbindung herstellen
  5. Userauthentifizierung
  6. Layout
  7. Zend Front Controller statklar machen & starten
    [/list=1]

    Included Path :
    [code=php]
    set_include_path('.' . PATH_SEPARATOR . 'library'
    . PATH_SEPARATOR . get_include_path());
    [/code]
    Da der komplette Zend Library Ordner ja im libraray Ordner liegt und wir nicht alle Klassen extra über require_once einfügen möchten fügen wir diesen Ordner zum include_path von PHP hinzu.
    Später werden hier noch einige Sachen folgen.
    Wenn z.B. eigene Plugins geschrieben werden, oder Datenbank"models" folgen.
    Doch dazu später mehr.

    ZEND Klassen laden :
    [code=php]
    require_once "Zend/Loader.php";
    Zend_Loader::registerAutoload();
    [/code]
    Die Klasse Zend_Loader kann mithilfe der Funktion registerAutoload alle benötigten Klassen includen ohne das wir auch nur eine weitere Zeile schreiben müssen
    Magic Wink

    Configurationen machen :
    [code=php]
    $cfg = new Zend_Config_Ini('application/etc/app.ini', 'dev');
    [/code]
    Dieser Abschnitt ist besonders interessant für alle Leute die lokal auf Ihrem PC entwickeln und das ganze dann nur noch hochladen wenns fertig ist.
    Die lokale Datenbank hat aber andre Logindaten als die Onlinedatenbank Ihres Hosts.
    Was tun ?
    Zend schafft Abhilfe. Mit Zend Config lässt sich der Aufwand auf ein minimum redizieren.
    Wir erstellen also folgende Datein und Ordner
    Code:
    /htdocs
    -/application
    --/etc
    ---app.ini
    [...]
    Inhalt der app.ini :
    Code:
    database.adapter = "pdo_mysql"
    database.params.username = "onlineusername"
    database.params.password = "onlinepasswort"
    database.params.dbname = "datenbankname"
    [dev : live]
    database.params.username    = "lokalerusername"
    database.params.password    = "lokalespasswort"

    Datenbankverbindung herstellen :
    [code=php]
    $db = Zend_Db::factory($cfg->database);
    Zend_Db_Table_Abstract:ConfusedetDefaultAdapter($db);
    $db->getConnection();
    [/code]
    Nun können wir eine Instanz von Zend_Db ganz einfach erstellen.
    Wir übergeben einfach die Logindaten der Datenbank aus unserer Konfiguartionsdate an die function factory.
    Nun setzten wir diese Datenbankverbindung noch als Standart für spätere Models etc. und schließlich stellen wir eine öffnen wir eine Verbindung mit getConnection().

    Userauthentifizierung :
    [code=php]
    $auth = Zend_Auth::getInstance();
    $acl = new MyWork_myacl($auth);
    [/code]
    Dazu ein extra Part später Wink

    Layout :
    [code=php]
    Zend_Layout:ConfusedtartMvc(array(
    'layoutPath' => 'application/layouts',
    'layout' => 'frontend'));
    $view = Zend_Layout::getMvcInstance()->getView();
    $view->doctype('XHTML1_TRANSITIONAL');
    [/code]
    Auch hierzu folgt später noch eine genauere Beschreibung.
    Nur soviel :
    Wir erstellen einen neuen Ordner namens layout und dort eine Datei namens frontend.phtml
    In dieser Datei können wir dann das Design coden ohne viel PHP Code im Weg zu haben.

    Front Controller :
    [code=php]
    try{
    $front = Zend_Controller_Front::getInstance();
    $front->addModuleDirectory('application/modules')
    // Folgendes Plugin hat mit der Userauthentifizierung zusammen und wird später genauer beleuchtet
    ->registerPlugin(new MyWork_myauth($auth, $acl))
    ->dispatch();
    }catch(Zend_Controller_Exception $e){
    echo"ControllerError<br />".$e;
    }catch(Exception $e){
    echo"Error<br />".$e;
    }
    [/code]
    Vorab eine kleiner Hinweis :
    Vom Frontcontroller hängt die ganze Seite ab. Dieser steuert das Routing ( also welche Seite im Endeffekt aufgerufen wird ) Dementsprechend gibt es hier ein vielzahl von Kongurationsmöglichkeiten.
    Ich empfehle also einen Blick ins Manual [de] zu werfen.

    Eine Instanz der Klasse erhalten wir mit dem Aufruf von getInstance(). Nun definieren wir nun den Pfad zu dem Modules und mit dem Aufruf von dispatch() wird die ganze Sache gestartet.
    Wenn ich Zeit und Lust hab folgt evtl. später noch eine Part nur zum Frontcontroller Wink

    Ich hab nochmal die komplette Datei so wie sie nun aussehen sollte hochgeladen.
    So wie die datei nun ist, so ist das noch lange nicht perfekt.
    Später werden noch sinnvolle Erweiterungen gemacht.
    In den späteren Parts wird dann eine neue Version der Bootstrap Datei hochgeladen !


    Last Update : 26.09


Angehängte Dateien
.zip   index.zip (Größe: 679 Bytes / Downloads: 438)
  Zitieren
#3
Was brauchen wir ?

Folgende Datein :
IndexController.php ( aus application/modules/default )
index.phtml ( aus application/modules/default/views/scripts/index )

Inhalt IndexController.php
[code=php]
class IndexController extends Zend_Controller_Action {
public function indexAction()
{
// Programmlogik hier
$this->view->neuevariable = 'Inhalt von IndexController.php';
}
}
[/code]

Inhalt von index.phtml
[code=php]
<h1>Startseite</h1>
<?php echo $this->neuevariable ?>
[/code]

Dann sollte die Ausgabe so sein :

Startseite
Inhalt von IndexController.php

-----------------------------------
That´s it Wink


Was lernen wir ?
Um eine neue Seite auf unserer Webseite brauchen wir nur :
1a) Neuen Controller anlegen
ODER
1b) Eine neue Action im Controller hinzufügen
2) Wir brauchen einen neuen Ordner (wenn wir auch nen neuen Controller erstellt haben sonst sollte der ordner schon da sein Wink ) im Verzeichnis application/modules/default/views/script/{name des Controllers}/{name der action}.phtml

Im Controller könnt Ihr auch einfach fröhlich Sachen Ausgeben, doch das bitte nur zu debugg Zwecken, denn schließlich besagt das MVC Prinzip das im Controller nur Programmlogik sein soll.

Das wärs auch schon Wink
  Zitieren
#4
Für komplexere Internetauftritte

Jeder der eine mehr, oder minder komplexe Webbaplication schreiben will, der wird nach kurzer Zeit bemerken, dass er mit einem Module nicht weit kommt. Zumindest nicht wenn er keine bescheuerten Controllernamen einführen will, oder einige Controller mit 20 Funktionen bestückt.
Um genau sowas zu verhindern ist eine modulare Strucktur gut.
In unserem Verzeichnis modules befindet sich bis dato nur ein Ordner : default.
Hier fügen wir nun einen neuen Ordner hinzu den wir special nennen.
In diesem Verzeichnis erstellen wir nun wieder einen IndexController und die dazugehörigen Viewscripts ( siehe Part III -> Was lernen wir).
nun sollten wir im Ordner Modules folgende Ordnerstrucktur haben :

Code:
/modules
->/default
->->/controllers
->->->[..]
->->/views
->->->/[..]
->/special
->->/controllers
->->->[..]
->->/views
->->->/[..]

Achtung :
Die Klasse im Controller darf hier nun nicht nur IndexControler heißen, sondern muss den Modulnamen als Präfix haben. Also :
[code=php]
class special_IndexController extends Zend_Controller_Action {
public function IndexAction()
{
// Programmlogik hier
}
}
[/code]

Das Standart Routing ist :
/{modul}/{controller}/{action}
Also in unserem Fall : special/index/index. Es wird immer zuerst überprüft ob der erste Parameter in der URL ein Modul ist.
/{controller}/{action}
Hier wird alsModul 'default' benutzt.
  Zitieren
#5
Für eine komplette Userauthentifizierung benötigen wir 4 Sachen :
  • Eine eigene Klasse die von Zend_Acl abgeleitet wird
  • Ein Zend_auth Adapter
  • und ein Front Controller Plugin der die User bei jedem Seitenaufruf authentifiziert
  • dann noch ein Loginformular

Also erstmal legen wir einen neuen Ordner im Verzeichnis library an den ich hier MyWork nennen werde.
In diesem Ordner legen wir nun 3 Datein an :
  • myacl.php
  • myauth.php
  • myauthadapter.php

Arbeiten wir uns nun mal von oben nach unten dutch die Datein.
Inhalt von myacl.php
[code=php]
<?php
class MyWork_myacl extends Zend_Acl
{
public function __construct(Zend_Auth $auth)
{
$this->add(new Zend_Acl_Resource('index'));
$this->addRole(new Zend_Acl_Role('gast'))
->addRole(new Zend_Acl_Role('admin'),'gast');
$this->allow('gast','index','index');
->allow('admin','index',array('neuenuser','userlöschen'));
}
}
[/code]

Die Klasse muss so benannt werden damit sie von Zend_Auoload im Unterordner gefunden wird ( wer sich also bis jetzt gewundert hat warum alle Zend Klassen mit dem Präfix 'Zend_' beginnen, dem geht jetzt wohl ein Licht auf Wink

Was passiert in dieser Date ?
Erst definieren wir Resourcen. Wir können mit Zend_Acl nur den Zugriff von Usern auf Resourcen kontrollieren. Es macht daher sinn diese Resourcen sinnig zu bennenen. Ich benutze hier den Controllernamen.
Den Actionnamen sollten wir nicht nehmen, da man später die Action als zusätzlich behandeln kann.

Dann müssen wir Rollen ( Benutzergruppen ) anlegen.
Eine Rolle sollte immer 'gast' o.ä. heißen. Schließlich brauchen wir eine Klasse die alle nicht angemeldeten User bekommen.
Zusätzlich definiere ich nun noch eine Admingruppe.
Durch den 2. Parameter 'gast' lasse ich der Admingruppe alle Erlaubnisse von der Gruppe gast erben.
Also wenn Gast nun Zugriff auf xyz hat, dann hat die Admingruppe automatisch auch Zugriff auf jene Resource.

Als nächstes sagen wir der Klasse welche Usergruppen auf welche Resource Zugriff hat.
[code=php]allow('gast','index','index')[/code]
Gast hat nun Zugriff auf die Resource 'index' und dort auf die Action 'index'.
Admin hat nun automatisch durch die Vererbung auch Zugriff auf diese Action.
[code=php]allow('admin','index',array('neuenuser','userlöschen')[/code]
Admin hat nun Zugriff auf die Resource 'index' und dort auf die beiden Action´s 'neuenuser' und 'userlöschen'.

Inhalt von myauth.php
[code=php]
<?php
class MyWork_myauth extends Zend_Controller_Plugin_Abstract
{
private $_auth;
private $_acl;
private $_noauth = array('module' => 'default',
'controller' => 'user',
'action' => 'nouser');

private $_noacl = array('module' => 'default',
'controller' => 'error',
'action' => 'noaccess');
public function __construct($auth, $acl) {
$this->_auth = $auth;
$this->_acl = $acl;
}
public function preDispatch(Zend_Controller_Request_Abstract $request) {
$controller = $request->getControllerName();
$action = $request->getActionName();
$module = $request->getModuleName();
$resource = $controller;
if ($this->_auth->hasIdentity()) {
$role = $this->_auth->getIdentity()->role;
} else {
$role = 'gast';
}

if (!$this->_acl->has($resource)) {
$resource = null;
}
if (!$this->_acl->isAllowed($role, $resource, $action)) {

if (!$this->_auth->hasIdentity()) {
$module = $this->_noauth['module'];
$controller = $this->_noauth['controller'];
$action = $this->_noauth['action'];
} else {
$module = $this->_noacl['module'];
$controller = $this->_noacl['controller'];
$action = $this->_noacl['action'];
}
}
$request->setModuleName($module);
$request->setControllerName($controller);
$request->setActionName($action);
}
}
[/code]
Was passiert in dieser Datei ?

Die ersten Zeilen sollten sich von selbst erklären . OOP in PHP möchte ich hier nicht näher erlätern.
Die Funktion preDispatch wird vor dem Abarbeiten des Controllers gestartet. Als parameter wird hier das Request-Objekt übergeben. Aus diesem können wir nun auslesen auf welche Seite der User gerade zugreifen möchte :
[code=php]
$controller = $request->getControllerName();
$action = $request->getActionName();
$module = $request->getModuleName();
[/code]
Nun müssen wir noch prüfen ob der User eingeloggt ist, und festlegen das unsere Resource aus Acl hier gleichzusetzten ist mit dem Controller, welcher aufgerufen werden soll.
[code=php]
$resource = $controller;
if ($this->_auth->hasIdentity()) {
$role = $this->_auth->getIdentity()->role;
} else {
$role = 'gast';
}
[/code]
Wenn der User nicht eingeloggt ist, so wird ihm die Rolle 'gast' gegeben.

Die wichtigsten Zeilen :
[code=php]
if (!$this->_acl->isAllowed($role, $resource, $action)) {

if (!$this->_auth->hasIdentity()) {
$module = $this->_noauth['module'];
$controller = $this->_noauth['controller'];
$action = $this->_noauth['action'];
} else {
$module = $this->_noacl['module'];
$controller = $this->_noacl['controller'];
$action = $this->_noacl['action'];
}
}
[/code]
Hier wird geprüft ob der User mit der Rolle $role auf die Resource $resource und dort auf die Action $action keinen Zugriff hat.
Wenn dies der Fall ist wird entschieden ob er auf die Loginseite weitergeleitet werden sollte, oder ob ihm eine Seite angezeigt werden soll, die ihm sagt, dass er keinen Zugriff auf diese Seite hat.

Schließlich werden noch die eventuell neuen Angaben an den Request weitergegeben.

So einfach ist der Hauptteil Wink

Nun der Inhalt von myauthadapter.php
[code=php]
<?php
require_once 'Zend/Auth/Adapter/DbTable.php';

class MyWork_myauthadapter extends Zend_Auth_Adapter_DbTable
{
public $authAdapter;

public function __construct() {

$db = Zend_Registry::get('db');
parent::__construct($db);

$this->setTableName('tabelle_mit_userdaten');
$this->setIdentityColumn('spalte_mit_username');
/* Passwörter sollten verschlüsselt sein */
$this->setCredentialColumn('spalte_mit_passwort');
/* Falls verschlüsselt dann hier sagen wie */
$this->setCredentialTreatment('MD5(?)'');
}
}
[/code]
Es gibt auch andere Adapterklassen. Wer also nicht MySQL nutzt kann sich mal in dem Teil des Manuals umsehen [1]

So das wären auch schon alle wichtigen Sachen.
Als letztes noch das Loginformular. Wir bauen das Formular am besten so ein das in der selben Funktion auch der tatsächliche Loginvorgang abläuft.
[code=php]
// Klasse usw. hier erstellen
private function loginForm()
{
$login = new Zend_Form();
$login->setAction('/user/login')
->setMethod('post');
$username = $login->createElement('text', 'username')
->setRequired(true)
->addValidator('regex', false, array('/^[a-z0-9]*$/i'))
->addValidator('stringLength', false, array(4,15));
$password = $login->createElement('password', 'password')
->setRequired(true)
->addValidator('regex', false, array('/^[a-z0-9*]*$/i'))
->addValidator('stringLength', false, array(5,10));
$login->addElement($username)
->addElement($password)
->addElement('submit', 'login', array('label' => 'Login'));
return $login;
}
public function indexAction()
{
echo $this->loginForm();
}
public function loginAction()
{
if($this->getRequest()->isPost()){
$form = $this->loginForm();
if (!$form->isValid($_POST)) {
$this->view->message = "<font style=\"color:red; font-weight:bold;\">Invalid Input</font><br />";
$this->view->badlogin=$form;
}else{
$erg=$form->getValues();
$authAdapter = new MyWork_myauthadapter();
$authAdapter->setIdentity($erg['username']);
$authAdapter->setCredential($erg['password']);
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
$storage = $auth->getStorage();
$storage->write($authAdapter->getResultRowObject(array('spalte_mit_username','spalte_mit_rolle'),'spalte_mit_passwort'));
$this->view->message = 'Login success.';
} else {
switch ($result->getCode()){
case Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND:
$this->view->message = '<b>Anmeldung fehlgeschlagen.</b><br />
<br />
Benutzername und/oder Passwort falsch.';
break;
case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID:
$this->view->message = '<b>Anmeldung fehlgeschlagen.</b><br />
<br />
Inaktiv oder gebannt.';
break;
default:
$this->view->message = '<b>Anmeldung fehlgeschlagen.</b><br />
<br />
Es ist ein Fehler aufgetreten.';
break;
}
}
}
}else{
return $this->_forward('index');
}
[/code]

Alle Fragen zum Formular an sich sollten hier geklärt werden können.
Ich werde hier nur kurz sagen, was folgende Zeilen bedeuten :
[code=php]
$authAdapter = new MyWork_myauthadapter();
$authAdapter->setIdentity($erg['username']);
$authAdapter->setCredential($erg['password']);
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
$storage = $auth->getStorage();
$storage->write($authAdapter->getResultRowObject(array('spalte_mit_username',
'spalte_mit_rolle'),'spalte_mit_passwort'));
[/code]
Also erstmal hohlen wir uns unsere Adapterklasse und übergeben dieser die Benutzereingaben. Dann lassen wir prüfen ob diese Eingaben korrekt sind. Wenn ja, dann werden die beiden Spalten spalte_mit_username und i]spalte_mit_rolle[/i] noch gespeichert, damit unser Plugin auch bei jedem Seitenaufruf die Indentität wiederfindet.

Fertig ist das komplette Usermanagement

Letztes Update : 27.09.2008
  Zitieren
#6
Hi

sieht echt gut aus - hab nur leider noch keine Zeit gehabt mir das ganze genau anzuschaun.
Vielleicht ists ja auch möglich, dass du irgendwo eine Demo Version online stellst.

Mfg
  Zitieren
#7
Jo danke für das positive Feedback.

Also eine Demoversion ist bei nem Framework ja net so einfach. Spontan fällt mir nur meine eigene Website ein.
Noch sieht diese auch mehr nach Demoversion aus, da es noch kein Design gibt.
Also wer wissen will wie weit ich selbst bin mit der Entwicklung der kann sich mal auf http://cashiers-check.de umsehen
  Zitieren
#8
Ich hab extra nichts geschrieben um die schöne Tutorialkette nicht zu unterbrechen aber auch ich kann nur sagen geile Anleitung =)
  Zitieren
#9
So und nun melden wir uns wieder nach dieser kleinen Werbeunterbrechung Big Grin

Part V | Daten auslesen aus Datenbank ( MySQL )

Alles was in diesem Part beschrieben wird gehört dem M aus MVC an.
Ich werde manchmal andre Ausdrücke als Model für hier gezeigte Möglichekuten nennen, doch trotzdem ist es der Modelkomponente zuzuordnen.
Es gibt mehrere Möglichkeiten Daten aus der Datenbank auszulesen.

[bTabellen-Models :[/b]
Im Ordner Models legen wir eine neue Datei nach folgendem Muster an :
{tabellenname}Models.php
in dieser Datei legen wir eine neue Klasse an
[code=php]
class tabelle extends Zend_Db_Table_Abstract{
protected $_name = 'tabelle';
protected $_primary = 'primary_key';
}
[/code]
Nun können wir in jedem Controller einfach eine Instanz dieser Klasse erzeugen und dann können wir beispielsweise ganz einfach Datensätze in dieser Tabelle anlegen.
[code=php]
$tabelle = new tabelle();
$insertwerte = array('col' => 'value');
$tabelle->insert($insertwerte);
[/code]
Auf änliche Art und Weise kann man auch Datensätze auslesen und aktualisieren.
Mehr dazu im Manual [LINK]

Eine andre Möglichkeit ist es einfach den in der Index.php ( Bootstrap ! ) erzeugten MySQL Adapter in der Zend Registry zu speichern und nun wieder auszulesen.

Bootsrapr
[code=php]
Zend_Registry:Confusedet('db',$dbadapter);
[/code]
Controller
[code=php]
$db = Zend_Registry::get('db');
$getrow=$db->select();
$temp=$getrow->from('tabelle',array('col1','col2'))
->order('primary ASC');
$tmp=$getdetails->query();
$res=$tmp->fetchAll();
[/code]
Diese Anfrage würde Beispielsweise alle Werte aus den Spalten col1 und col2 auslesen. Sortiert würden die Wertepaare nach dem Wer der in der Spalte primary steht ( wahrscheinlich der Primärschlüssel Wink ). Das Ergebnis wird in der Variable $res gespeichert.
$res ist ein verschachteltes Array
[code=php]
echo $res[0]['col1'];
[/code]
Dies würde den Wert aus col1 aus der ersten Reihe ausgeben.

MySQL ist natürlich ein großes Thema und es gibt auch eine Vielzahl von Möglichkeiten Daten aus einer Datenbank auszulesen.
Das Framework stellt dementsprechend auch viele Möglichkeiten bereit mit der Datenbank zu kommunizieren.
Ein Blick ins Manual zu Zend_db ist daher für jeden Zend Framework Benutzer Pflicht.
  Zitieren
#10
An dieser Stelle mal ne Frage an die kleine aber feine Community Big Grin
Wollt ihr mehr ?
Wollt ihr Beschreibung einzelner Komponenten ?

Würde gerne noch mehr schreiben, weiß aber nicht ob hier wer überhaupt intereese dran hat ^^
  Zitieren


Gehe zu:


Benutzer, die gerade dieses Thema anschauen: 1 Gast/Gäste