Empfehlungen: Erweiterte Suche

Seitenkonfiguration mit XML-Datei

Anleitungen und Quellcode-Auszüge die den Start vereinfachen sollen.

Seitenkonfiguration mit XML-Datei

Beitragvon havanna » Mo 22. Jun 2009, 04:42

Da ich gerade nicht schlafen kann (haben wir Vollmond, und ich sehe ihn nicht?) und ich ein Problem ganz witzig gelöst habe, dachte ich mir ich schreibe ein kleines Tutorial.
Vielleicht dient es jemanden als Anregung…

Mein Problem:
Ausgangspunkt aller Lösungen ist ein Problem. In meinem Fall war mein Problem dies, dass ich unterschiedliche Layouts setzen wollte. Außerdem wollte ich noch zusätzliche Informationen je http-Request abfragen wie z.B. Description, Keywords, etc.
Wer Typo3 kennt weiß, dass man grundsätzlich ein Default-Layout hat. Dann gibt es aber je Seite die Möglichkeit ein spezielles Layout zu setzen.
Ich wollte es gerne noch variabler haben. Im ersten Moment war der Gedanke dies in eine Tabelle zu speichern – ähnlich wie pages bei Typo3. Doch dies wäre mit einer Lösung die Rekursion unterstützen soll, zum einen nicht leicht gewesen und zum anderen hätte es viele Datenbankabfragen mit sich gebracht. Viel performanter ist es eine Datei zu lesen.

Betrachten wir nur einmal die Anforderung, unterschiedliche Layouts zu verwenden. Unser http-Request kann z.B. so aussehen:
http://localhost/immobilien/search > Default Layout
http://localhost/immobilien/getDetail/4711 > Zweispaltiges Layout
oder wenn wir den Admin-Bereich aufrufen:
http://localhost/admin/administration/getlog > Admin Layout

Mit $this->here erhalten wir jeweils den markierten Substring. Diesen könnte man nun genauso als Array darstellen:
Code: Alles auswählen
[immobilien]
    [getDetail]
        [4711]
 


Eine XML-Datei ist auch nichts anderes, als ein Array:

Code: Alles auswählen
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<PROFIRMA>
  <CONTROLLER>
    <immobilien>
      <getDetail>
        <LAYOUT>admin</LAYOUT>
      </getDetail>
      <THEME>immoplain</THEME>
      <LAYOUT>default</LAYOUT>
      <search>
        <LAYOUT>default</LAYOUT>
      </search>
    </immobilien>
    <setups>
      <index>
        <LAYOUT>admin</LAYOUT>
      </index>
      <testxml>
        <LAYOUT>default</LAYOUT>
      </testxml>
      <LAYOUT>admin</LAYOUT>
    </setups>
  </CONTROLLER>
</PROFIRMA>
 


Wahrscheinlich versteht jetzt jeder, worauf ich hinaus möchte: Ich möchte die Möglichkeit haben, für jede Seite ein spezielles Layout zu definieren. Aber ich möchte auch nicht dazu gezwungen sein. Wenn ich für den speziellen Aufruf kein Layout finde, dann suche eine Stufe oder zwei Stufen tiefer. Wenn überhaupt nichts gefunden wird, soll das Standard-Layout verwendet werden.
Genauso könnte ich mit weiteren Dingen verfahren, die ich auf einer HTML-Seite benötige wie eben die Keywords, Description und andere Meta-Tags.

Also, was brauchen wir dazu, um das zu realisieren?
Zuerst benötigen wir eine XML-Datenquelle. Die Datei haben wir ja schon eben definiert.

Unter /app/datasources erstellen wir die Datei xml_source.php.
Code: Alles auswählen
<?php

  class XmlSource extends DataSource {
 
    var $description = 'XML DataSource';
   
    function construct($config = null) {
   
      parent::__construct($config);
      $this->connected = $this->connect();
      $this->setConfig($config);
      return $config;
    }
   
    function __destruct() {
      $this->connected = $this->close();
      parent::__destruct();
    }
       
    function findController() {
      App::import('Core','Xml');
      $svDatasource = VENDORS . 'meinefirma' . DS . $this->config['file'];
      $xml = new Xml($svDatasource);
      $controllers = $xml->children[0]->child('CONTROLLER');
      return $controllers;
    }

    function connect() {
    }
  }
?>
 


Die xml-Datei liegt dann im Verzeichnis /vendors/meinefirma.
Jetzt sollten wir noch wissen, wie unsere XML-Datei heißen soll. Das ergibt sich aus der Config-Datei.

In der Datei /app/config/database.php machen wir folgenden zusätzlichen Eintrag:

Code: Alles auswählen
  var $xml = array(
    'datasource' => 'Xml',
    'file' => 'meine_config.xml'
  );
 


Damit wäre nun auch klar, unter welchem Dateinamen unsere XML-Datei gespeichert werden muss.

Wichtig ist hierbei: der Wert des Schlüssels datasource entspricht dem Klassennamen der Datenquelle.

In der Definition der Datenquelle haben wir gleich die Funktion zum auslesen aufgenommen. Dabei verwenden wir den einfachen XML-Parser, der von CakePHP verwendet wird. Die Funktion findController() liest das Element CONTROLLER komplett aus und übergibt es an die aufrufende Funktion.

Jetzt erstellen wir uns noch ein Modell. Das nenne ich setup.php:
Code: Alles auswählen
<?php

  class Setup extends AppModel {  
 
    var $name = 'Setup';  
    var $useTable = false;
   
   
    function xmlFindController() {
      $this->setDataSource('xml');
      $xml = $this->getDataSource();
      return $xml->findController();
    }

}

?>


Die Funktion setzt die Datenquelle xml (aus der database.php) und ruft die Funktion findController() der Datenquelle auf.

Damit haben wir jetzt schon einen großen Teil geschafft. Was uns jetzt noch fehlt, ist eine Komponente - denn wir benötigen die Funktion ja auf jeder Seite.
Erstellen wir also in /app/controllers/components die Datei myconfig.php.
Da wir unsere Funktion ganz allgemein auch für andere Dinge verwenden möchten, übergeben wir dieser zwei Parameter: den Pfad und den Schlüssel.

Code: Alles auswählen
class MyconfigComponent extends Object {

/******************************************************************************
 Function   : startup
 Arguments : $controller
 Description:

*******************************************************************************/

  function startup(&amp;$controller) {
  }

/******************************************************************************
 Function   : getValue
 Arguments : $path, $key
 Description:

*******************************************************************************/
 
  function __getValue($path, $key) {

    $svPath = '';
    $result = '';
   
    $controllers = $this->Setup->xmlFindController();
    $saController = $this->__xmltoArray($controllers);

    // der erste Slash muss weg
    if(substr($path,0,1) == '/') {
      $path = substr($path,1,strlen($path));
    }
    $searchpath = $path . '/' . $key;
    $saResult = $this->__xmltoArray($controllers);
    $searchpath = str_replace('/', '.', $searchpath);

    $saResult = Set::extract($this->__xmltoArray($controllers),$searchpath);
   
    if(!empty($saResult)) {
      $result = $saResult['#text'];
    } else
    {  // Funktion so lange aufrufen bis wir auf der untersten Ebene sind
      // wenn nicht gefunden muss der erste slash weg
      if(substr($path,0,1) == '/') {
        $path = substr($path,1,strlen($path));
      }
      // dann den String in ein Array zerlegen denn wir suchen bis max. auf die dritte Ebene (erster Parameter)
      $saPath = explode('/',$path);
      $nPath  = count($saPath);
      if(count($saPath) > 1) {
        $pfadneu = array_pop($saPath);
        foreach ($saPath as $value) {
          $svPath = $svPath . '/' . $value;
        }
        $result = $this->getValue($svPath,$key);
      } else {
        return null;
      }
    }
    return $result;
  }
 


In __getValue rufen wir die Funktion xmlFindController() aus unserem Modell. Damit erhalten wir das XML-Objekt. Aus diesem müssen wir erst einmal ein Array machen. Die Funktion xmltoArray habe ich bei Felix Geisendörfer gesehen.

Mit der Cake-Funktion Set::extract können wir prüfen, ob wir den Schlüssel finden. Dazu müssen die Schrägstriche / durch Punkte ersetzt werden.
Die Funktion rufen wir so lange rekursiv auf, bis wir was finden oder wir geben null zurück. Dann muss ein Default-Wert ziehen.

Hier noch die Funktion von Felix, die wir auch in unsere Komponente packen:
Code: Alles auswählen
/******************************************************************************
 Function   : __xmltoArray
 Arguments : $node
 Description:

*******************************************************************************/

    function __xmltoArray($node) {
   
      $array = array();
     
      foreach ($node->children as $child) {
        if (empty($child->children)) {
          $value = $child->value;
        }
        else {
          $value = $this->__xmltoArray($child);
        }
        $key = $child->name;
       
        if (!isset($array[$key])) {
          $array[$key] = $value;
        }
        else {
          if (!is_array($array[$key]) || !isset($array[$key][0])) {
            $array[$key] = array($array[$key]);
          }
          $array[$key][] = $value;
        }
      }
      return $array;
    }
 


Ebenfalls in unsere Komponente können wir jetzt unsere Funktion aufnehmen, mit der wir das Layout ermitteln. Diese ist recht einfach:

Code: Alles auswählen
/******************************************************************************
 Function   : getLayout
 Arguments : $path
 Description:

*******************************************************************************/

  function getLayout($path) {

    $svPath = '';
   
    App::import('Model', 'setup');
    $this->Setup = new Setup();
   
    // falls der Pfad mit Slash endet, muss dieser entfernt werden
    if (substr($path, -1) == '/') {
      $path = substr($path,0,strlen($path)-1);
    }
    //echo $path;
    $svLayout = $this->__getValue($path, 'LAYOUT');
    //echo "<br/>Layout nach Funktion: " . $svLayout;
    return $svLayout;
  }
 


Ich habe hier noch ein paar Kommentare belassen, mit der man an einigen Stellen prüfen kann, was übergeben wird.

In der Funktion beforeRender des AppController können wir jetzt unsere Funktion aufrufen und erhalten das Layout – oder auch nicht. Wenn nicht, setzen wir das default Layout.
Genauso kann die Funktion verwendet werden, um weitere Seiteneigenschaften zu setzen, die in der xml-Datei konfiguriert sind.

Code: Alles auswählen
class AppController extends Controller {

  var $components = array('Session', 'RequestHandler', 'Myconfig');

  function beforeRender(){
    $svLayout = $this->Diacsconfig->getLayout($this->here);
    if(empty($svLayout)) {
      $this->layout = 'default';
    } else {
      $this->layout = $svLayout;
    }
}
 


So, die Nacht ist rum, der Tag ist angebrochen. Schnell unter die Dusche und los geht's.
Ich hoffe ich konnte mit dem Beispiel ein paar Anregungen geben.

Ciao Thomas
Benutzeravatar
havanna
 
Beiträge: 191
Registriert: Mi 15. Okt 2008, 23:12
Wohnort: Bodman-Ludwigshafen
CakePHP-Version: 1.2.x
OS: WIN

Zurück zu Tutorials und Snippets

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast

cron