Archive for the ‘Designpattern’ category

Zend_View_Helper einrichten und returnvalues als Objekte in einem Layouttemplate nutzen

December 19th, 2009

Ich stand vor dem Problem, in meinem Headerlayout einen Helper für jedes Modul contextbezogen aufrufen zu wollen, um jeder seite einen entsprechenden header zu präsentieren. Dabei wollte ich in meinem Actioncontroller einfach eine variable $headline setzen, die dann vom viewhelper ins layout geschrieben wird.

Wer sich einen Viewhelper einrichten will und in diesem auf die Viewvariablen zugreifen möchte sollte 2 Dinge beachten.
1) Der Viewhelper muss von Zend_View_helper_Abstract abgeleitet sein oder das Zend_View_Helper_Interface implementieren
2) Es wird empfohlen. das Viewobject explizit zu setzen und setView zu überschreiben.

ist ganz einfach.
1) Ihr legt den Viewhelper im Ordner “views/helpers” ab. Ich habe einen Helper “getMyViewHelper.php” verwendet.

2) Ihr definiert die Helperklasse. Sie heist demnach Zend_View_Helper_getMyViewHelper

<?php
/**
 * Description of getMyViewHelper
 *
 * @author peter
 */
class Zend_View_Helper_getMyViewHelper  extends Zend_View_Helper_Abstract {
protected $returnObject=NULL; // soll in einen ViewScript (siehe unten) ausgegeben werden.
    function  __construct() {
                //do some stuff
    }
        //den aktuellen view setzen
        public function setView(Zend_View_Interface $view)
        {
           $this->view = $view;
        }
        //diese methode wird aufegrufen, wenn ihr im layout $this->getMyViewHelper() einbindet. Beispiel siehe "headerlayout"
        function getViewPortItems()
    {
       $this->returnObject=(object)$empty; //wir wollen kein array oder eine einzelne variable , wir wollen ein object
       $this->returnObject->eintest="erster Test";
       $this->returnObject->nocheintest="noch ein Test";
       //hier werden die Variablen aus dem ActionController verwendet
       $this->returnObject->headline=$this->view->eindrittertest;

       return $this->returnObject;
    }
}
?>

Das layouttemplate “header.phtml”

<h1><?php echo this->getMyViewHelper()->eintest?></h1> //variable aus dem helper
<h2><?php echo this->getMyViewHelper()->nocheintest?></h2> //variable aus dem helper
<h3><?php echo this->getMyViewHelper()->headline?></h3> // und da ist die headline aus dem view

im controller cann man dann einfach die headline setzen.

<?php
class Admin_IndexController extends Zend_Controller_Action {
    public function init() {
        $this->translate=Zend_Registry::get("translate_mod");
    }
    /**
     * @desc generates and populates the Adminpanelform
     */
    public function indexAction() {

        $this->view->headline  = $this->translate->_("modHeadline");

    }
}

Zend_Db_Table_Abstract vs Zend_Db_Table_Row_Abstract Models mit Zend_Db_Table

December 12th, 2009
Wer nicht mit einem Zend-fremden ORM arbeitet kann natürlich auch den Zend_Db Adapter verwenden und seine eigenen Zend Models einhängen. Der Sinn dahinter ist keine Insert, Update etc Befehle mehr selbst abzusetzen, sondern dass elegante Methoden wie save() und fetchNew() machen zu lassen.

Ich arbeite mit einer Lösung, die Rob Allen und Co in Ihrem Buch “Zend Framework im Einsatz” aus dem Addison-Wesley Verlag praktizieren.
Das besiert zwar auf der 1.5 er Version ist aber auch fehlerfrei in der 1.9.6 im Einsatz.

Hier werden in einer modularen Struktur (siehe Bild) in je einem Ordner “models” für die Datenbanktabelle 2 Dateien angelegt.
In diesem Beispiel sind es “User.php” und “Users.php”. Erstere arbeitet auf RowEbene, um das Row-Gateway Pattern zu erfüllen. Sie erweitert Zend_Db_Table_Row_Abstract. Zweitere definiert die Datenbanktabelle und setz Primarkeys und bindet die User.php ein.

“Users.php” erweitert Zend_Db_Table_Abstract.
Wir wollen nun das Model “Users.php” verwenden.
Wir haben eine Mysql Datenbanktabelle Tabelle “Users”.

1) Bootrapdatei sagen, dass sie einen Mysql Adapter haben soll und der ist der DefaultAdapter. Das ist wichtig, sonst kennt die ModelKlasse den Adapter nicht und quittiert den Dienst mit einer Wception. Datei->index.php.


/*********************************************************
 * we want to user mysql as database
 */
$conf=array(
// Databaseconfig
'host'        => 'localhost',
'username'    => 'root',
'password'    => '########',
'dbname'      => '########',
'tableSuffix' => 't_survey_', //thats the tablesuffix in the databse

)
$db_Adapter = Zend_Db::factory('Pdo_Mysql',$conf);
//lets set this adapter as default
Zend_Db_Table::setDefaultAdapter($db_Adapter);

2) Wir legen 2 Dateien im Ordner “modules/user/models/” an.
User.php und Users.php
2.1) User.php

<?php
/*
 * Simply enhance Zend_Db_Table_Row and overwrtite __get
 */
 * @author peter
 */
class User extends Zend_Db_Table_Row_Abstract {
//wenn die eine methode zum key existiert wird die aufgerufen anonsten wird die Property verwendet
    function __get($key) {
        if(method_exists($this, $key)) {
            return $this->$key();
        }
        //wenn nicht wird die objekteigenschaft angezeigt
        return parent::__get($key);
    }

}
?>

2.2 Datei Users.php
Wichtig ist hier zu wissen, dass $_name die Datenbanktabelle ist.
$_rowClass ist die die Erweiterung von Zend_Db_Table_Row_Abstract. Das ist unsere User.php in dem Fall. In unserem Fall ist die userID der Primarykey

<?php
require_once APPLICATION_PATH."/modules/user/models/User.php";
class Users extends Zend_Db_Table_Abstract {

    protected $_name        ="user";
    protected $_rowClass    ="User";
    protected $_primary     = "userID";

    function init() {

    }
}
?>

3) Die Klassen verwenden
Ich habe im IndexController ein Beispiel zur Verwendung eingehangen.
Wir instantiieren ein Objekt von Users() und wollen alle Datensätze in einem var_dump() ausgeben. Weitere Objektmethoden sind fetchNew(), find(), current(),save() etc. Mehr dazu in der Onlinereferenz von Zend.
Datei: modules/user/controllers/IndexController.php

function indexAction(){
require_once APPLICATION_PATH.'/modules/user/models/Users.php';
        $_users = new Users();
        var_dump($_users->fetchAll());

}

Thats all. So bekommt Ihr saubere Datenbankobjekte automatisch erstellt und braucht keine Sql Statements mehr selbst absetzen.

Factory Klassen , Factory methoden. Dynamische Objekte zur Laufzeit

November 22nd, 2009

Ein oftbenötigtes Pattern der OOP / OOD ist das Factorypattern.
Es ist in der Lage zur Initialisierung der Anwendung alle benötigten Objekte zur Verfügung zu stellen.
Klar braucht man für einfache Instantiierung keine Factorymethode.

Möchte man jedoch zur Laufzeit viele Objekte Instantiieren und weis man nichts über die Konstruktoren etc. Bietet sich eine Factoryklasse mit Einbindung der Reflectionmethoden der Reflektionklasse an. Das verhindert Probleme mit komplexen Konstruktoren.

Folgendes Beispiel erstellt Objekte anhand einer Liste. Dabei wird einer der Reflectionklassemethoden(getNumberOfRequiredParameters()) benutzt um die Instantiierungsparameter der Konstruktoren abzufangen. Zusätzlich wird für jedes Object eine Helpermethode aufgerufen, in der man das Objekt vorkonfigurieren kann.

Bitte beachten, dass die Sourcen dynamisch geladen werden. Die müssen natürlich existieren.
und die ERROR Klasse ist eine eingene Implementierung.


class factory {

function __construct()
{
   $this->built;
}

private function built() {
        $arr_Classnames             = array(
            '_error'             =>'C_ERROR',
            '_survey'          =>'C_SURVEY',
            '_company'      =>'C_COMPANY',
            '_layout'           =>'C_LAYOUT'
        );
        C_ERROR::arrayEmpty(get_class()."::factory_Keys()","\$arr_Classnames",$arr_Classnames);
        //durch die liste zu erstellender Objecte iterieren
        foreach($arr_Classnames as $objname=>$class) {
            require($className.".php");
            $className=$class;
            //wenn der Konstruktor Parameter braucht / ansonsten einfache Instatiierung
            $reflectionMethods = new ReflectionMethod($className, '__construct');
            if($reflectionMethods->getNumberOfRequiredParameters()>0) {
                $_refClass = new ReflectionClass($className);
                $this->$objname = $_refClass->newInstanceArgs(array($this));
            }else {
                $this->$objname = new $className;
            }
            //call helpermethode
            $HelperFunction="factory".$objname;

            if(is_object($this->$objname)) {

                if(!method_exists($this,$HelperFunction)) {
                    C_ERROR::isFunction(get_class(),$HelperFunction);
                }

            }else {
                C_ERROR::object(get_class()."::built()","\$this->$objname",$this->$objname);
            }
        }
    }
}