4. Zend Tutorial

Nachdem ich im dritten Tutorial erklärt hatte, wie die (vorläufige) Struktur der Datenbank aussieht, müssen wir noch ein paar Vorbereitungen schaffen, damit wir einfach unsere Datenbank aktualisieren können. Das ganze habe ich bereits in meinem Artikel Zend Framework und Liquibase beschrieben. Ich möchte dazu auch nur die Abweichungen niederschreiben.

Ant Build Datei

Die Ant-Build-Datei sieht folgendermaßen aus:

<?xml version="1.0" encoding="UTF-8"?>
<project name="liquibase-test">
  <description>
    Zend Framework Tutorial
  </description>
 
  <!-- prepare -->
  <target name="prepare">
    <path id="library.classpath">
      <fileset dir="library/3rdparty/mysql">
        <include name="*.jar" />
      </fileset>
      <fileset dir="library/3rdparty/liquibase">
        <include name="*.jar" />
      </fileset>
    </path>
 
    <taskdef resource="liquibasetasks.properties">
      <classpath refid="library.classpath" />
    </taskdef>
 
    <!-- set global properties for this build -->
    <property name="changelog.file" location="db/changelog.xml" />
    <property name="base.path" location="." />
  </target>
 
  <!-- properties -->
  <target name="properties_development" depends="prepare">
    <property name="database.driver" value="com.mysql.jdbc.Driver" />
    <property name="database.url" 
       value="jdbc:mysql://localhost/zend_volleyball" />
    <property name="database.user" value="root" />
    <property name="database.password" value="" />
  </target>
 
  <!-- development -->
  <target name="development" depends="properties_development">
    <updateDatabase changeLogFile="${changelog.file}" 
      driver="${database.driver}"
      url="${database.url}" username="${database.user}" 
      password="${database.password}"
      classpathref="library.classpath" />
  </target>
</project>

Damit das ganze funktioniert müssen folgende Voraussetzungen erfüllt sein:

  • Im Ordner library/3rdparty/mysql muß ein MySQL-Connector verfügbar sein,
  • Im Ornder library/3rdparty/liquidbase muss Liquidbase verfügbar sein,
  • Ant muss installiert sein. Das habe ich in Ant unter Windows 7 installieren bereits beschrieben.
  • Java muß auf dem System installiert sein.
  • Die Datei db/changelog.xml muss angelegt werden. Inhalt wie nachfolgend beschrieben.

Liquidbase

Die Datei changelog.xml stellt den Einstiegspunkt  für Liquidbase dar. Dort referenzieren wir bisher nur eine weitere Datei:

<?xml version="1.0" encoding="UTF-8"?>
 
<databaseChangeLog
  xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
    http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
 
 <include file="/db/001.setup.xml" /> 
 
</databaseChangeLog>

Nun formulieren wir unser Datenbank Model in Liquidbase:

<?xml version="1.0" encoding="UTF-8"?>
 
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd">
 
  <preConditions>
    <dbms type="mysql" />
  </preConditions>
 
  <changeSet id="10" author="set">
 
    <!-- Team -->
    <createTable tableName="volleyball_team">
      <!-- common -->
      <column name="id" type="int" autoIncrement="true">
        <constraints primaryKey="true" nullable="false" />
      </column>
      <column name="name" type="varchar(255)">
        <constraints nullable="false" />
      </column>
      <column name="available" type="boolean" />
      <column name="fee_paid" type="boolean" />
      <column name="competition_id" type="int" />
      <column name="group_id" type="int" />
    </createTable>
 
    <!-- Competition -->
    <createTable tableName="volleyball_competition">
      <!-- common -->
      <column name="id" type="int" autoIncrement="true">
        <constraints primaryKey="true" nullable="false" />
      </column>
      <column name="name" type="varchar(255)">
        <constraints nullable="false" />
      </column>
      <column name="start" type="datetime" />
      <column name="single_duration" type="time" />
      <column name="number_of_sets" type="int" />
    </createTable>
    
    <!-- Group -->
    <createTable tableName="volleyball_group">
      <!-- common -->
      <column name="id" type="int" autoIncrement="true">
        <constraints primaryKey="true" nullable="false" />
      </column>
      <column name="name" type="varchar(255)">
        <constraints nullable="false" />
      </column>
    </createTable>
    
    <!-- Fremdschlüssel -->
    <addForeignKeyConstraint constraintName="fk_competition_team" 
      baseTableName="volleyball_team" baseColumnNames="competition_id" 
      referencedTableName="volleyball_competition" 
      referencedColumnNames="id" />
    <addForeignKeyConstraint constraintName="fk_group_team" 
      baseTableName="volleyball_team" baseColumnNames="group_id" 
      referencedTableName="volleyball_group" 
      referencedColumnNames="id" />
  </changeSet>
</databaseChangeLog>

An sich nichts spannendes. Wenn man sich ein bisschen in die Dokumentation von Liquidbase einliest, ist das kein Problem mehr. Wenn alles richtig gemacht wurde können wir das Ant-Skript in der Konsole starten und sollten folgende Ausgabe erhalten:

Ant Skript ausführen

So sollte die Ausgabe in der Konsole aussehen.

3. Zend Tutorial

Im ersten und zweiten Tutorial haben wir die Grundlagen geschaffen, nun will ich mit der Planung bzw. Architektur zu der Beispiel-Anwendung beginnen. Ich habe mir eine Turnier-Verwaltung ausgedacht, da man sich darin gleich reindenken kann und die Anforderungen zu Beginn nicht zu hoch sind. Also was brauchen wir auf Datenbank-Ebene? Eine Tabelle für die Mannschaften und eine für die Turniere. Außerdem möchte ich die Mannschaften einer Gruppe zuordnen können (Gruppierung). Somit sieht das erste Datenbank-Diagramm folgendermaßen aus:

Erstes Datenbankschema der Beispielanwendung

Erstes Datenbankschema der Beispielanwendung

Zur Tabelle Competition: Auch ein Turnier hat einen Namen und ein geplanten Startermin. Für das Turnier wird auch festgelegt, wie lange ein Spiel gehen soll (single_duration) und wieviele Sätze pro Spiel gemacht werden (number_of_sets). Das wären z. B. beim Volleyball die einzelnen Sätze eines Spiels.

Zur Tabelle Team: Ein Team besteht aus einem Namen und muß am Tag des Turniers auch da sein (available). Außerdem muß vorher eine Gebühr bezahlt werden (fee_paid). Diese zwei Spalten speichern lediglich Bool-Werte ab.

Zur Tabelle Group: Eine Gruppe hat lediglich einen Namen. Dadurch kann z. B. gekennzeichnet werden, dass ein Verein mehrere Mannschaften stellen kann. Dies kann dann später im Spielplan berücksichtigt werden.

Der Zend-Server kann gleich mit einer mySql-Datenbank installiert werden. Deswegen werde ich diese Datenbank zugrunde legen.

Datenbankverbindung

Eine Datenbankverbindung kann über das zf-Tool eingerichtet werden. Ich habe das schon in dem Artikel Datenbank unter Zend ausführlich beschrieben.

ORM

ORM steht für Object-Relational Mapping und ist nichts anderes, als dass rationale Datenbanken auf Objekte abgebildet werden. Im offiziellen Zend-Tutorial werden dazu Model-, Mapper-, und Table-Klassen verwendet. Nachfolgende Skizze soll das verdeutlichen:

Zend Datenbank Abbildungen

Prinzip des Zend Datenbank ORM

Im Pseudocode der Funktion fetchAll sehen wir, dass im Mapper eine Instanz der Datenbank-Abstraktion geholt wird (z. B. eine MySql-Adapter-Klasse) über die Daten von der Datenbank geholt wird. Anschließend werden die Daten auf das Model abgebildet und zurückgegeben.

zf-Tool

Den Adapter konfigurieren und die ersten Klassen anlegen können wir einfach mit dem zf-Tool erledigen:

zf configure dbadapter "adapter=Pdo_Mysql&username=test&
                        password=test&dbname=zend_volleyball"
zf create db-table Team team
zf create db-table Competition competition
zf create db-table Group group
zf create model Team
zf create model Competition
zf create model Group

 

2. Zend Tutorial

Heute will ich die Grundlage des Layouts auf ordentliche Füße stellen. In meinen letzten Projekten habe ich mit dem HTML Kickstarter 99limes gearbeitet und möchte nun etwas anderes probieren. Ich habe von Twitter Bootstrap gelesen und die Webseite macht für mich einen guten Eindruck. Wir laden im ersten Schritt den Quellcode von der Webseite. Das ist eine einzelne Zip-Datei, die wir im public verzeichnis unserer Applikation entpacken – genauer gesagt werden die Dateien im Verzeichnis /public/3rdparty/twitter/bootstrap entpackt.

Twitter Bootstrap Verzeichnisstruktur

Wie integrieren wir das nun in unser Projekt? Richtig! In der Bootstrap.php ergänzen wir die folgenden zwei Zeilen:

// css
$view->headLink()->appendStylesheet('/3rdparty/twitter/bootstrap/css/bootstrap.min.css');
// js
$view->headScript()->appendStylesheet('/3rdparty/twitter/bootstrap/js/bootstrap.min.js');

Wir haben noch das Problem, dass wir mit dem falschen Doctype arbeiten. Wir müssen das also in der settings.ini anpassen:

resources.view.doctype=HTML5

Es lohnt sich übrigens in die Framework-Datei /Zend/View/Helper/Doctype.php zu schauen. Dort sind diese Konstanten vordefiniert.

Nun können wir noch etwas Beispiel HTML-Code in unsere index.phtml einfügen und schon erhalten wir eine schöne Ausgabe. Der Quellcode für die Datei /application/views/scripts/index/index.phtml hab ich aufgrund der Menge mal hier hinterlegt.

end Beispiel Inhalt

1. Zend Tutorial

Mal sehen wie weit ich komme, aber ich möchte für mich den Einstieg ins Zend Framework dokumentieren. Das ist der erste Artikel eine hoffentlich längeren Serie.

Voraussetzung

Die Voraussetzung für dieses Tutorial ist eine Zend Server CE Installation mit einer MySQL Installation und ein wenig Grundwissen in PHP.

Einrichtung

Wir beginnen mit der Einrichtung des Vhost-Eintrags zu meinem Beispiel-Projekt:

<VirtualHost *:80>
   ServerAdmin kontakt@tobias-seckinger.de
   DocumentRoot "C:/wwwroot/zend_volleyball/public"
   ServerName volleyball.local   
   <Directory "C:/wwwroot/zend_volleyball/public">
        Options Indexes FollowSymLinks Includes +ExecCGI
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

Ich habe also im Verzeichnis

C:/wwwroot/

erstmal nur ein Ordner zend_volleyball angelegt. Anschließend erstellen wir im Verzeichnis

C:\Program Files (x86)\Zend\ZendServer/etc/sites.d/

eine Datei mit dem Namen vhost_volleyball.conf mit dem obigen Inhalt. Die Datei wird nach dem Apache-Neustart ausgelesen, weil der Zend-Server in der httpd.conf folgende Zeile enthält:

Include "C:\Program Files (x86)\Zend\ZendServer/etc/sites.d/vhost_*.conf"

Nun öffnen wir ein Windows Konsole und wechseln in das Verzeichnis

C:/wwwroot/

und tippen:

zf create project zend_volleyball

Anschließend aktivieren wir noch ein Standard-Layout. Dazu wechseln wir aber erst ins Verzeichnis zend_volleyball und tippen:

zf enable layout

Wir müssen ggf. noch die hosts Datei von Windows ergänzen, damit die Seite lokal geladen wird. Wir öffnen die Datei

C:\Windows\System32\drivers\etc\hosts

und ergänzen folgende Zeile

127.0.0.1           volleyball.local

Achso: Den Apache Neustart nicht vergessen.

Grundgerüst

Wir wollen natürlich ein schönes HTML Grundgerüst haben inklusive eigenen Javascript und CSS-Dateien. Zuerst ergänzen wir unsere Bootstrap-Datei um folgende Methode (/application/Bootstrap.php):

protected function _initApplication()
{
  $this->bootstrap('view');
  $view=$this->getResource('view');
 
  $view->headMeta()->appendHttpEquiv('Content-Type',
                                     'text/html;charset=utf-8');
  // meta
  $view->headMeta()->appendName('keywords', 'Zend, Tutorial, Volleyball');
  // css
  $view->headLink()->appendStylesheet('/css/default.css');
  // js
  $view->headScript()->appendFile('/js/common.js');
  // title
  $view->headTitle()->setSeparator(' - ');
  $view->headTitle('Volleyball - Zend-Tutorial');
}

Damit das auch ausgegen werden kann, müssen wir in unserem Layout auch die passenden Anweisungen integrieren (/application/layouts/scripts/layout.phtml):

<?php echo $this->doctype(); ?>
<html xmlns="http://www.w3.org/1999/xhtml" lang="de">
<head>
  <?php echo $this->headTitle(); ?>
  <?php echo $this->headMeta(); ?>
  <?php echo $this->headLink(); ?>
  <?php echo $this->headStyle(); ?>
  <?php echo $this->headScript(); ?>
</head>
<body>
  <?php echo $this->layout()->content; ?>
</body>
</html>

Wenn wir nun die Seite neu laden erhalten wir erstmal eine Fehlermeldung. Wir müssen noch eine Kleinigkeit in der application.ini ergänzen:

resources.view[] =
; Doctype setzen
resources.view.doctype=xhtml1_strict

Außerdem müssen wir die CSS und Javascript-Dateien anlegen:

  • /public/css/common.css
  • /public/js/common.js

Die Datei /application/views/scripts/index/index.phtml können wir aufräumen:

<div>
  Übersicht
</div>

Anschließend sollten wir ungefähr folgende Ausgabe im Browser erhalten:

Weiterführende Informationen:

Doctype unter Zend setzen

Über die application.ini kann dies leicht gemacht werden:

resources.view[] =
resources.view.doctype=xhtml1_strict

Es gibt auch die Möglichkeit das in der Bootstrap.php zu machen, wobei ich erste Methode bevorzuge:

protected function _initDoctype() 
{ 
  $this->bootstrap('view'); 
  $view = $this->getResource('view'); 
  $view->doctype('XHTML1_STRICT'); 
}

Es sollte natürlich ein Layout aktiv sein. Das macht man am besten mit der Konsole:

zf enable layout

In der layout.phtml kann dann die Doctype-Anweisung ausgegeben werden:

<!-- application/layouts/scripts/layout.phtml -->
 
<?php echo $this->doctype() ?>
 
....

 

Datenbank unter Zend

In fast allen Anwendungen wird eine Datenbank für die Datenhaltung benötigt. In diesem Artikel möchte ich einen Weg beschreiben, um eine Anbindung an eine Datenbank unter einer Zend-Anwendung zu realisieren.

Voraussetzung für diese Artikel ist eine bereits eingerichtete Anwendung. Dies habe ich bereits im dem Artikel Zend Framework – Ein Startversuch beschrieben.

Verbindung konfigurieren

Im ersten Schritt benötigen wir einen korrekt konfigurierten Adapter. Das setzt z. B. eine lokal eingerichtete Datenbank voraus. Die Konfiguration kann direkt über die Konsole vorgenommen werden:

zf configure db-adapter "adapter=PDO_MYSQL&host=localhost&dbname=test&
username=testuser&password=testpasswort&charset=utf8" production

Das Zend-Tool passt durch diesen Aufruf die application.ini im Projekt an:

resources.db.adapter = "PDO_MYSQL"
resources.db.params.host = "localhost"
resources.db.params.dbname = "test"
resources.db.params.username = "testuser"
resources.db.params.password = "testpasswort"
resources.db.params.charset = "utf8"

Für den Microsoft Server könnte der Aufruf z. B. folgendermaßen aussehen:

zf configure db-adapter "adapter=Sqlsrv&host=mydbhost&dbname=mydbname&
username=myusername&password=mypassword&charset=utf-8"

Damit unter Mssql der Zeichensatz korrekt gesetzt ist, mußte ich die application.ini um eine Zeile ergänzen:

resources.db.adapter = "Sqlsrv"
resources.db.params.host = "mydbhost"
resources.db.params.dbname = "mydbname"
resources.db.params.username = "myusername"
resources.db.params.password = "mypassword"
resources.db.params.charset = "utf-8"
; Zeichensatz über die Driver-Options setzen
resources.db.params.driver_options.CharacterSet = "UTF-8"

Datenbank konfigurien und Tabellen anlegen

Da ich hier nicht die Datenbank-Seite, sondern die Zend-Seite betrachten möchte, gehe ich an dieser Stelle nicht weiter auf das Anlegen einer Tabelle ein. Ich möchte aber auf meinen Artikel Zend Framework und Liquibase verweisen.

Datenbankzugriff über ein Table Data Gateway

Der Zugriff auf eine Datenbank wird über ein sogenanntes Table Data Gateway ermöglicht. Das ist einfach gesagt eine PHP-Klasse, über die alle Zugriff auf die Tabelle abstrahiert werden. Die Klasse leitet von Zend_Db_Table_Abstract ab, die bereits Methoden für den Zugriff mitbringt (find, fetchAll, insert, update, delete). Über das zf-Tool kann diese einfach erstellt werden:

zf create db-table Project my_project_table_name

Im Ordner application/models/DbTable wird eine neue Klasse Project.php angelegt, die sehr überschaubar ist:

class Application_Model_DbTable_Protocol
  extends Zend_Db_Table_Abstract
{
  // Name der Datenbanktabelle
  protected $_name = 'my_project_table_name';
}

Erstellen einer Mapper-Klasse

Die Mapper-Klasse ist für die Übersetzung zwischen den PHP-Objekten und dem Table Data Gateway verantwortlich. Beispielsweise übergeben wir der Mapper-Klasse eine Instanz eines Models, damit dieses im Datenbank persistent gespeichert wird.

Im Ordner application/models erstellen wir einen neuen Ordner Mappers und legen dort eine neue Klasse Base.php an, die für alle künftigen Mapper-Klassen als Basisklasse dient. Dort können wir gemeinsam genützte Funktionalität implementieren.

Beispiel:

<?php
abstract class Application_Model_Mapper_Base
{
  // accessors/mutators
  public function setDbTable($dbTable)
  {
    if($this->_dbTable)
      return $this;
    if(is_string($dbTable))
      $dbTable=new $dbTable();
    if(!$dbTable instanceof Zend_Db_Table_Abstract)
      throw new Exception('Invalid table data gateway');
    $this->_dbTable=$dbTable;
    return $this;
  }
  /**
   * @return Zend_Db_Table_Abstract
   */
  public function getDbTable()
  {
    if(null === $this->_dbTable)
      $this->setDbTable($this->getDbTableClass());
    return $this->_dbTable;
  }
  //------------------------------------------------------------------
 
  // IMPLEMENTATION
  /**
   * @var Zend_Db_Table_Abstract
   */
  protected $_dbTable;
  //-----
 
  protected function getDbTableClass()
  {
    $path=explode('_', get_class($this));
    $name=array_pop($path);
    return 'Application_Model_DbTable_'.$name;
  }
}

Eine konkrete Implementierung könnte dann beispielsweise folgendermaßen aussehen:

<?php
class Application_Model_Mapper_Protocol
  extends Application_Model_Mapper_Base
{
  public function findAll()
  {
    $resultSet=$this->getDbTable()->fetchAll();
    $entries=array();
    foreach($resultSet as $row)
    {
      $entry=new Application_Model_Protocol();
      // TODO: Eigenschaften des Models setzen
      $entries[]=$entry;
    }
    return $entries;
  }
}

Wir haben hier eine Methode findAll implementiert und sehen dort sehr schön, wie in der Schleife die Model-Objekte erzeugt werden und anschließend zurückgegeben werden.

Ein Model anlegen

Das bereits im vorherigen Abschnitt erwähnte Model muß noch erzeugt werden. Wir können das wiederum mit dem zf-Tool vornehmen:

zf create model Project

Ich empfehle auch an dieser Stelle eine Basisklasse anzulegen, in der gemeinsam genützte Funktionalität implementiert wird (z. B. Setter/Getter für die id-Eigentschaft). Nachfolgend eine exemplarische Implementierung:

<?php
abstract class Application_Model_Base
{
  // properties
  /**
   * @var Zend_Db_Table_Row
   */
  public $dbentry;
  //------------------------------------------------------------------
 
  // construction
  public function __construct(array $options=null)
  {
    if (is_array($options))
      $this->setOptions($options);
  }
  //------------------------------------------------------------------
 
  // setters/getters
  public function __set($name, $value)
  {
    $method='set' . $name;
    if (('mapper'==$name) || !method_exists($this, $method)) {
      throw new Exception('Invalid '.__CLASS__.' property');
    }
    $this->$method($value);
  }
  public function __get($name)
  {
    $method='get' . $name;
    if (('mapper'==$name) || !method_exists($this, $method)) {
      throw new Exception('Invalid '.__CLASS__.' 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=$id;
    return $this;
  }
 
  public function getId()
  {
    return $this->_id;
  } 
  //------------------------------------------------------------------
 
  // IMPLEMENTATION
  protected $_id;
}
class Application_Model_Project extends Application_Model_Base
{
  // weitere Eigenschaften und Methoden
}

Tests

Wir sollten die erstellten Klassen frühzeitig testen. Wenn wir eine Zend Server Installation haben und PEAR und PhpUnit korrekt installiert ist, dann können wir im Ordner tests/application/models eine neue PHP-Datei anlegen und grundlegende Funktionalität testen. Die Einrichtung beinhaltet einige Fallstricke und ich werde bei Gelegenheit einen separaten Blog-Eintrag dazu verfassen. Nachfolgend nur ein Auszug, wie eine einfache Test-Klasse aussehen kann:

class ProjectTest extends Zend_Test_PHPUnit_ControllerTestCase
{
  public function setUp()
  {
    $this->bootstrap = new Zend_Application(APPLICATION_ENV, 
      APPLICATION_PATH . '/configs/application.ini');
    parent::setUp();
  }
 
  public function testProjectModel()
  {
    $model=new Application_Model_Project();
    $model->setId(1);
    $this->assertTrue($model->getId()===1);    
  }
}

Fazit

Es ist einiges an Vorarbeit notwendig, um Daten in einer Zend-Anwendung persistent zu bekommen. Es kommt auch entscheidend auf die Umsetzung an, da der Entwickler sehr frei in der Implementierung ist. Bei großen Datenmengen ist ein Objektmapping meistens nicht geeignet, da dies Zulasten der Performance und des Speicherverbrauchs geht – ein direkter Zugriff läßt sich jedoch auch direkt über den Adapter realisieren. In den meisten Szenarien ist das Arbeiten mit Modellen und Mappern jedoch zu empfehlen, weil es in der Verwendung einfacher zu Handhaben ist.

 

Typo3 4.6.4 Update und Zend Server

Ein kleines Update ist heute veröffentlicht worden. Alle Änderungen sind im Release-Log aufgeführt.

Ich habe seit neustem auf meinem Windows x64 System ein Problem mit dem Zend Server CE (PHP 5.2) 5.6.0 und Typo3 Version 4.6.3. Mit Version 5.5 hat noch alles problemlos funktioniert. Ich weiß nicht, ob es an den “Security Enhancements” in PHP 5.3.9 liegt, aber der Login in den Backend-Bereich von Typo3 ist in dieser Umgebung nicht mehr möglich. Nach Absenden des Login-Formulars erschient eine Meldung ala “Wollen Sie die index.php Datei speichern oder öffnen…”. Darin ist ein 500-HTTP-Status-Code enthalten, jedoch erscheint in den Apache und PHP-Log-Dateien keinerlei Hinweis darauf. In der Ereignisanzeige von Windows sieht das ganz anders aus. Dort scheint die php-cgi.exe einen Fehler zu verursachen.

Ich werde das morgen mit der neuen Typo3-Version testen und auch Bilder und genaue Fehlermeldungen nachliefern.

Update

Eine Aktualisierung auf Typo3 4.6.4 brachte wie vermutet keinen Erfolg. Wie versprochen ein paar Details zu dem Fehler:

Typo3 4.6.3 Login Screen

Typo3 4.6.3 Login Screen

Fehlermeldung nach Login

Fehlermeldung nach Login

 

Inhalt der Datei index.php

Inhalt der Datei index.php
Windows Ereignisanzeige Fehlermeldung

Windows Ereignisanzeige - Fehlermeldung

Nochmal als Text (damit Google das findet):

Name der fehlerhaften Anwendung: php-cgi.exe, Version: 5.3.9.0, Zeitstempel: 0x4ef33bca
Name des fehlerhaften Moduls: php5.dll, Version: 5.3.9.0, Zeitstempel: 0x4ef33bc8
Ausnahmecode: 0xc0000005
Fehleroffset: 0x00092b20
ID des fehlerhaften Prozesses: 0xd1c
Startzeit der fehlerhaften Anwendung: 0x01ccdb33ed105389
Pfad der fehlerhaften Anwendung: C:\Program Files (x86)\Zend\ZendServer\bin\php-cgi.exe
Pfad des fehlerhaften Moduls: C:\Program Files (x86)\Zend\ZendServer\bin\php5.dll
Berichtskennung: f2e4d78f-4728-11e1-bd4d-005056c00008
Noch eine Meldung in der Windows Ereignisanzeige

Noch eine Meldung in der Windows Ereignisanzeige

 

Fehlerbucket , Typ 0
Ereignisname: APPCRASH
Antwort: Nicht verfügbar
CAB-Datei-ID: 0
 
Problemsignatur:
P1: php-cgi.exe
P2: 5.3.9.0
P3: 4ef33bca
P4: php5.dll
P5: 5.3.9.0
P6: 4ef33bc8
P7: c0000005
P8: 00092b20
P9:
P10:

 Update 2:

Das Problem ist vorübergehend gelöst, indem man die Datei /typo3/contrib/RemoveXSS/RemoveXSS.php in der Größe verändert. Schuld daran ist ein PHP-Bug. Einen herzlichen Dank an Jürgen für den Tip (siehe Kommentare).

Formular unter Zend validieren

Ich nehme mal an, dass wir den Rumpf eines Formulars vorliegen haben (zf create form…) und eines der Felder validieren wollen:

class Application_Form_MyForm extends Zend_Form
{
  // initialization
  public function init()
  {
    $this->setMethod('post');
 
    $this->addElement(
      'text', 'myelement', array(
        'label' => 'Mein Element*:',
        'required' => true,
        'filters' => array('StringTrim'),
        'validators' => array(array('stringLength', 
                                    false, array(8, 8)), 
                        array('digits'), 
               array(new Application_Form_Validator_MyValidator()))
      )
    );

Dem Element myelement werden unterschiedliche Validatoren hinzugefügt z. B. das nur Zahlen erlaubt sind (digits). Die Validatoren, die das Zend Framework bereits mitbringt, findet man im Verzeichnis Validate. Interessant ist an dieser Stelle der eigene Validator. Damit das ZF den Validator findet muß man zuerst den Autoloader anpassen. Bei mir liegt die Klasse Application_Form_Validator_MyValidator im Verzeichnis application/forms/validators und ist als MyValidator.php abgespeichert. Die Autoloader-Anweisung in der Bootstrap-Klasse lautet wie folgt:

$loader=$this->getResourceLoader();
$loader->addResourceType('validators', 'forms/validators', 
                         'Form_Validator');

Der Validator selbst leitet von der abstrakten Klasse Zend_Validate_Abstract ab und sieht exemplarisch folgendermaßen aus:

class Application_Form_Validator_MyValidator 
                                 extends Zend_Validate_Abstract
{
  // consts
  const INVALID='invalid';
  //----
  
  // properties
  protected $_messageTemplates = array(self::INVALID=>
                               "'%value%' ist kein gültiger Wert");
  //----
  
  // validation
  public function isValid($value, $context = null)
  {
    $this->_setValue($value);
    // validierung
    if(true) // pseudo code
      return true;
 
    $this->_error(self::INVALID);
    return false;
  }
}

Zugriff auf mehrere Datenbanken unter Zend

Wenn man innerhalb einer Zend Anwendung auf verschiedenen Datenbanken (mysql, mssql, etc.) zugreifen möchte, kann die Verbindungen in der application.ini hinterlegen:

resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "xxx"
resources.db.params.password = "yyy"
resources.db.params.dbname = "zzz"
resources.db.isDefaultTableAdapter = true
 
resources.multidb.db1.adapter = "Sqlsrv"
resources.multidb.db1.host = "server-1"
resources.multidb.db1.dbname = "xxx"
resources.multidb.db1.username = "yyy"
resources.multidb.db1.password = "zzz"
resources.multidb.db1.charset = utf8
...

Anschließend kann die Instanz des Adapters geholt werden:

$front=Zend_Controller_Front::getInstance();
$bootstrap=$front->getParam('bootstrap');
$resource=$bootstrap->getPluginResource('multidb');
/**
 * @var Zend_Db_Adapter_Sqlsrv
 */
$db=$resource->getDb('db1');

Super, oder?

Zend Controller produziert JSON

Damit ein Controller in einer Zend Applikation kein Layout ausgibt bzw. auch keine View-Dateien verarbeitet, genügen zwei Zeilen im Controller:

$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);

Dadurch wird keinerlei Markup ausgegeben.

Ein möglicher Anwendungsfall sind Webseiten, die Inhalte per AJAX nachladen. Das kann mittels jQuery leicht über folgende Funktion realisiert werden:

jQuery.getJSON( url [, data] [, success(data, textStatus, jqXHR)] )

Der Parameter url ruft eine Aktion eines Controllers auf, der eben nur JSON zurückliefert. Dies kann anschließend im Javascript weiterverarbeitet werden.

Update:

Noch einfacher geht es über folgenden Helper:

$this->_helper->json($data);