Archive for the ‘Doctrine’ category

zend cache und registry. effinzent arrays cachen

July 24th, 2009

Eines meiner Projekte habe ich modular mit Zend und Layouts erstellt. Nun habe ich mich für datenbankbasiertes I18N(uebersetzungsstandard) entschieden, da ich meinen Produktmanagern nicht zumuten muss languagefiles zu pflegen, sondern ich setze hierzu eine schickes Adminformular auf.

Sämtliche Nabvigationselemente meiner Seite kommen nun mehrsprachig daher. Damit nicht alle Elemente der Navigationsleiste pro request neu übersetzt werden müssen  lesen ich sämtliche Elemente der eingestellten Sprache in den Cache und schreibe diese in die Registry um später aus allen Layouts darauf zugreifen zu können. Das ganze geschieht in meinem Layoutplugin, welches den Predispatcher überschreibt um bei späteren Updates nicht alles neu aufsetzen zu müssen. Am Ende schafft mir das 2 Requests pro Sekunde mehr, als wenn ich die Layoutübersetzungen pro Request neu mache.

So cached man unter zend arrays.

1) Bootstrap erweitern (public/index.php)

hier hab e den Autolader aktiviert und das Verzeichnis für die Cachefiles festgelegt. Anschliessend schreibe ich das Cacheobject in die Registy. Ja Ja werden manche sagen. Registry ist ein anderes Wort für Globals, Welche verpöhnt sind.

Aber nur so kann ich im layout auch auf den cache zugreifen.



/** autoloader aktivieren */
Zend_Loader::loadClass("Zend_Loader_Autoloader");
Zend_Loader_Autoloader::getInstance();

/**
*  activate variable caching
**/
$frontendOptions = array('lifetime' => 120,

// cache lifetime of 1 minute
 'automatic_serialization' => true
);

//zend cache Verzeichnis festlegen

$backendOptions = array(
 'cache_dir' => APPLICATION_PATH.'/cache/');
// getting a Zend_Cache_Core object
$cache = Zend_Cache::factory('Core',
 'File',
 $frontendOptions,
 $backendOptions);

//cache object in die Registry
Zend_Registry::set("zend_cache",$cache);

Dann übersetze ich meine Navielemente mit meinem Language_Plugin und schreibe anschliessend alle in meinem Laxoutplugin in den cache aus dem ich nun die navi laden kann .
Plugin (zend/Plugin/layout.php)


class Zend_Plugin_Layout extends Zend_Controller_Plugin_Abstract {
function __construct() {
$this->view = new Zend_View ( );
//sets current speech
new Zend_Plugin_Language ( );
}
/*
* Run at the beginning of the controller’s response object initialization
*/
public function preDispatch(Zend_Controller_Request_Abstract $request) {
//navigationItems in cache?
if (! $this->NavigationItems = Zend_Registry::get ( 'Zend_Cache' )->load ( 'NavigationItems' ))
{

//get Cacheobject from registry
$cache= Zend_Registry::get ( 'Zend_Cache' );
foreach($this->arr_unsortedItems as $item)
{
$item->label = Zend_Plugin_Language::translate ( $item->label);
$item->shortdescription = Zend_Plugin_Language::translate ( $item->shortdescription );
$this->NavigationItems[$item->type][]=$item;
echo"<br>label:".$item->label;
}
$cache->save($this->NavigationItems,"NavigationItems");
Zend_Registry::set("NavigationItems",$this->NavigationItems);
}else
{
Zend_Registry::set("NavigationItems",$this->NavigationItems);
}

}

und hier das language pluging. Die Datenbankzugriffe habe ich über doctrine realisiert.

Damit ich nicht für jedes Wort im Layout an die Datenbank muss, lade ich alle Strings der eingestellten Sprache als array in den cache.

Ob das Suchen im cache schneller ist als der DB zugriff zeigt ein benchmark.



<?php

/**
* Handles the header / footer by capturing the preDispatch and postDispatch of the
* response object
*
*/
class Zend_Plugin_Language extends Zend_Controller_Plugin_Abstract
{
    var $app_strings;
    var $lang;

    function __construct()
    {
        if(!session_id())
        {
            session_start();
        }

      	if(!isset($_SESSION['lang']) && $_SESSION['lang']=="")
        {
            if(!isset($_REQUEST['lang']) && $_REQUEST['lang']=="")
            {
                $_SESSION['lang'] = $this->lang = "de";
            }
        }
        else
        {
            if(isset($_REQUEST['lang']) && $_REQUEST['lang']!="")
            {
                $_SESSION['lang'] = $this->lang = $_REQUEST['lang'] ;
            }else
            {
                $this->lang=$_SESSION['lang'];
            }
        }

       echo"<br>lang:".$this->lang."/".$_SESSION['lang'];

    }

    /**
    * @desc translates a string on a set lannguage
    */
    function translate($LBL)
    {
        //if onöy if Strings not in cache. No need to stress the Database for each word
        if (!$this->app_strings = Zend_Registry::get ( 'Zend_Cache' )->load ( "app_strings_".$this->lang ))
{
$this->setAllStrings($this->lang);
}else
        {
            echo"cache var exists";
        }

       //throws a notice if label does not exist
       return $this->app_strings[$this->lang][$LBL];
    }

   /**
   * @desc save all translated strings of current language into cache to reduce Databaseload
   */
   function setAllStrings()
   {
          $sql = Doctrine_Query::create()
->select('value')
->from('ShopI18N')
            ->where("lang='".$this->lang."'");

$arr_language = $sql->execute();
            foreach($arr_language as $key=>$obj)
            {
                 $this->app_strings[$this->lang][$obj->label] = $obj->value;
            }
            $cache= Zend_Registry::get ( 'Zend_Cache' );
            $cache->save($this->app_strings,"app_strings_".$this->lang);
            Zend_Registry::set ( "app_strings_".$this->lang, $this->app_strings);
   }

}
?>
class Zend_Plugin_Layout extends Zend_Controller_Plugin_Abstract {
public $arr_unsortedItems=array();
public $NavigationItems=array();
private $view;function __construct() {
$this->view = new Zend_View ( );
//sets current speech
new Zend_Plugin_Language ( );
}
/*
* Run at the beginning of the controller’s response object initialization
*/
public function preDispatch(Zend_Controller_Request_Abstract $request) {
//save current module and action to registry
Zend_Registry::set ( “currentController”, Zend_Controller_Front::getInstance ()->getRequest ()->getControllerName ());
Zend_Registry::set ( “currentModule”, Zend_Controller_Front::getInstance ()->getRequest ()->getModuleName ());

if (true !== $this->getResponse ()->isException ())
{
//navigationItems in cache?
if (! $this->NavigationItems = Zend_Registry::get ( ‘Zend_Cache’ )->load ( ‘NavigationItems’ ))
{
$this->setNavigationItems();
}else
{
Zend_Registry::set(”NavigationItems”,$this->NavigationItems);
}
//standardlayouts
$this->setStandardLayouts ();
}
[]   }

Doctrine Objekte und der Zend Cache. – Eine performante Navigationsleiste

July 19th, 2009

Heute stand ich vor der Aufgabe meine Navigationselemente meiner 4 Navigationsleitsten(Topnav, mainnav, hilfsnav und footernav) datenbankbasiert, damit die Produktmanager dann über das CMS die Nav Manipulieren können, umzusetzen. Dabei soll nun nicht jeder Request die Links aus der Datenbank holen. Die Links werden in einem Doctrine – Object in eine Array gespeichert und im View ausgegeben.

Das Ganze habe ich in einem Plugin gelöst, welches die predispatch() Methode im Frontcontroller überschreibt.

Die Liste mit den Objekten speichere ich in die Zend_Registry und greife sie im View ab. Wer eine besser Möglichkeit kennt ein Object in den View weiterzugeben. her damit. Den kompletten Quellcode gibts unten.

Aber ersteinmal Step by Step

1) ist ein Cacheobject vorhanden?


$cache = Zend_Registry::get('Zend_Cache');

Als zweites schau ich im cache nach, ob das Navigationsobjekt nicht schon existiert.

Wenn es nicht existiert  sag ich Doctrine (meinem ORM) er soll doch bitte das Object aus der Datenbank neu generieren und im Cache ablegen.

Anschliessend wird das Object in der registry gespeichert, damit ich es im View verwenden kann.

<pre>
// item in cache?
 if(!$arr_items=$cache->load('arr_items'))
 {
 //if not get new doctrine object and safe to registry
 $sql = Doctrine_Query::create()
 ->select('*')
 ->from('ShopLayoutNavigation')
 ->where('type = \'mainnav\'');
 $arr_items = $sql->execute();
 $cache->save($arr_items ,'arr_items');
 }
 //objektlist in registry
 Zend_Registry::set('arr_items',$arr_items);
 }

Mein Layoutelement sieht dann so aus.


<a id="navigation" name="navigation"></a>
 <div>
 <ul>
 <?php
 //var_dump($this->layout()->arr_items);

 if (Zend_Registry::isRegistered('arr_items'))
 {
 foreach(Zend_Registry::get('arr_items') as $item)
 {
 echo"<li>";
 if($item->requesttype=="ajax")
 {
 echo"<a href='#' ".$item->eventhandler."='".$item->function."' class='".$item->label."' >".$item->label."</a>";
 }
 else
 {
 echo"<a href='".$item->action."' target='".$item->target."' class='".$item->label."'>".$item->label."</a>";
 }
 echo"</li>";
 }
 }
 ?>
 </ul>
 </div>

Hier der gesamte Code meines Plugins


<?php
/**
* Handles the header / footer by capturing the preDispatch and postDispatch of the
* response object
*
*/
class Zend_Plugin_Layout extends Zend_Controller_Plugin_Abstract
{

 public $arr_items;
 private $view;
 function __construct()
 {
 $this->view = new Zend_View();
 }
 /*
 * Run at the beginning of the controller’s response object initialization
 */
 public function preDispatch( Zend_Controller_Request_Abstract $request )
 {
 if(true !== $this->getResponse()->isException())
 {
 $navigation = array();
 $layout = Zend_Layout::getMvcInstance();
 $view = clone $layout->getView();
 $this->view->setScriptPath("../application/layouts/");
 //mainnav
 $this->getMainNavigation();
 }
 }

 public function getSearchHeader()
 {
 return "searchheader";
 }

/*
 * @desc bring back the mainnavigationitems as a list of objects
 * @Author peter Böthig
 */
 public function getMainNavigation()
 {
 $cache = Zend_Registry::get('Zend_Cache');
 // cache exists?
 if(!$arr_items=$cache->load('arr_items'))
 {
 //if not get new doctrine object and safe to registry
 $sql = Doctrine_Query::create()
 ->select('*')
 ->from('ShopLayoutNavigation')
 ->where('type = \'mainnav\'');
 $arr_items = $sql->execute();
 $cache->save($arr_items ,'arr_items');
 }
 //objektlist in registry
 Zend_Registry::set('arr_items',$arr_items);
 }
}
?>

Zend-Doctrine Models aus bestehender Datenbank

July 18th, 2009

Wer Doctrine über das Tutorial eingebunden hat möchte vielleicht die Modelklassen aus einer bestehenden Datenbank generieren.

Nichts leichter als das.

1) in der globals.php

<?php
Doctrine_Manager::connection("mysql://user:password@localhost/server", 'doctrine');
$options = array();
/** create Models from DB**/
Doctrine::generateModelsFromDb('../application/models', array(), $options);

Anschliessend findet Ihr unter application/models/ Eure Baseklassen.  Keine Ahnung wie lange man da manuell gechrieben hätte.