20 – 24 February featured databases in the form of InnoDB table configuration and Doctrine nested sets, plus a Puppet security recommendation.
Ronny explained how to configure InnoDB tables correctly with the help of tools such as phpMyAdmin.
Continuing the database theme, Eric gave a quick rundown on applying nested sets in Doctrine 1.2 for those who are also running an older version.
Thomas recommended Puppet users to upgrade to 2.6.14 or 2.7.11 and protect themselves against security vulnerabilities.
NETWAYS Blog
Doctrine 1.2 und Nested Sets
Über Doctrine schreibe ich ja gerne mal was, so auch heute. Da Doctrine 2.2 die letzte aktuelle Version ist, hoffe ich, dass es noch andere gibt, die ältere Versionen einsetzen und folgende Informationen vielleicht nützlich finden.
Kindknoten hinzufügen
Ein Nested Set zu speichern, bei dem die Sortierung der Knoten der Reihenfolge entspricht, wie sie hinzugefügt werden, sieht auf den ersten Blick ganz einfach aus:
$node->getNode()->insertAsLastChildOf($parent);
Aber: Doctrine ändert bei diesem Funktionsaufruf automatisch die Grenzen der Teilmenge (links und rechts), ausgehend von $parent, in der Datenbank. Die Grenzen vom Objekt $parent werden aber nicht aktualisiert. Das führt dazu, dass neue Knoten nicht als letztes, sondern als erstes Kind hinzugefügt werden. Ein simples
$parent->refresh();
nach jeder Änderung, löst das Problem.
Teilbäume
Zum Abschluss, stell ich noch Code zur Verfügung, der einen Teilbaum aus einem Nested Set, mit dem Namen Tree, selektiert:
$table = Doctrine_Core::getTable('Tree');
$tree = $table->getTree();
$query = $table->createQuery('t, Tree p')
->select('t.*')
->orderBy('t.lft ASC')
->where('p.id = ?', $id)
->andWhere('t.root_id = p.root_id')
->andWhere('t.level >= p.level')
->andWhere('t.lft BETWEEN p.lft and p.rgt');
$tree->setBaseQuery($query);
$hierarchy = $tree->fetchTree();
$tree->resetBaseQuery();
Automatische Datenbankgenerierung mit Doctrine
Die Doctrine CLI ist ein komfortables Mittel um sich z.B. Datenbank, Modelle, Tabellen und Daten automatisch erstellen zu lassen. Voraussetzung dafür ist ein entsprechendes Skript welches die Doctrine CLI aufruft und YAML Schema Dateien. Folgendes Szenario soll Prozesse in einer SQLite Datenbank hierarchisch ablegen.
Beispielumgebung unter /tmp/blog vorbereiten:
# cd /tmp
# mkdir blog
# cd blog
# mkdir lib
# wget
# tar xzf Doctrine-1.2.4.tgz -C lib
# mkdir sqlite models sql schema fixtures
Doctrine CLI Skript:
/tmp/blog# cat doctrine.php
<?php
$dir = realpath(dirname(__FILE__));
require_once($dir . '/lib/Doctrine-1.2.4/Doctrine.php');
spl_autoload_register(array('Doctrine', 'autoload'));
spl_autoload_register(array('Doctrine', 'modelsAutoload'));
Doctrine_Manager::connection(
'sqlite:///' . $dir . DIRECTORY_SEPARATOR . 'sqlite' . DIRECTORY_SEPARATOR . 'data.db',
'connection_name');
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine::ATTR_MODEL_LOADING,
Doctrine::MODEL_LOADING_CONSERVATIVE);
Doctrine_Core::loadModels($dir . DIRECTORY_SEPARATOR . 'models');
$config = array(
'models_path' => $dir . DIRECTORY_SEPARATOR . 'models',
'sql_path' => $dir . DIRECTORY_SEPARATOR . 'sql',
'yaml_schema_path' => $dir . DIRECTORY_SEPARATOR . 'schema',
'data_fixtures_path' => $dir . DIRECTORY_SEPARATOR . 'fixtures');
$cli = new Doctrine_Cli($config);
$cli->run($_SERVER['argv']);
Doctrine erwartet oder schreibt jetzt Modelle im Ordner models, Beispieldaten in fixtures, Datenbankschema in schema und die Datenbank in sqlite/data.db.
Eine Liste von verfügbaren Kommandos erhält man, wenn man das Skript ohne Argumente aufruft:
/tmp/blog# php doctrine.php
Doctrine Command Line Interface
doctrine.php compile
doctrine.php generate-yaml-models
doctrine.php rebuild-db
doctrine.php load-data
...
Unsere Datenbank soll aus 2 Tabellen bestehen - einmal der Tabelle process, in der die Prozesse gespeichert werden und process_hierarchy, in der diese verschachtelt werden:
/tmp/blog# cat schema/schema.yml
---
# schema/schema.yml
# Global parameters
connection: connection_name
# Tables
Process:
columns:
id:
type: integer
primary: true
autoincrement: true
name:
type: string(255)
notnull: true
relations:
Trees:
type: many
class: ProcessHierarchy
local: id
foreign: view_id
ProcessHierarchy:
actAs:
NestedSet:
hasManyRoots: true
rootColumnName: root_id
columns:
id:
type: integer
primary: true
autoincrement: true
process_id:
type: integer
relations:
Process:
local: process_id
foreign: id
Beispieldaten:
/tmp/blog# cat fixtures/data.yml
---
# fixtures/data.yml
Process:
order:
name: Order
logistics:
name: Logistics
support:
name: Support
ProcessHierarchy:
root:
Process: order
children:
logistics:
Process: logistics
children:
support:
Process: support
Zum Abschluss erstellen wir die Datenbank:
/tmp/blog# php doctrine.php build-all-load
build-all-load - Generated models successfully from YAML schema
build-all-load - Successfully created database for connection named 'connection_name'
build-all-load - Created tables successfully
build-all-load - Data was successfully loaded
/tmp/blog# php doctrine.php dql "from ProcessHierarchy h join h.Process p"
dql - executing: "from ProcessHierarchy h join h.Process p" ()
dql - id: '4'
dql - process_id: '4'
dql - root_id: '4'
dql - lft: '1'
dql - rgt: '6'
dql - level: '0'
dql - Process:
dql - id: '4'
dql - name: Order
dql - -
dql - id: '5'
dql - process_id: '5'
dql - root_id: '4'
dql - lft: '2'
dql - rgt: '5'
dql - level: '1'
dql - Process:
dql - id: '5'
dql - name: Logistics
dql - -
dql - id: '6'
dql - process_id: '6'
dql - root_id: '4'
dql - lft: '3'
dql - rgt: '4'
dql - level: '2'
dql - Process:
dql - id: '6'
dql - name: Support