Eigentlich arbeite ich gerade an einigen anderen Baustellen, zum Beispiel am Meta-Projekt, dass gute Fortschritte macht und an der Infrastruktur drüben beim Social-Media-Vermarkter adnation. Irgendwie lässt mich aber seit Tagen die Idee nicht mehr los, mich mal kurz hinzusetzen und ein Doctrine-Schema analog zu unserem Produktionsablauf bei der Produktion des t3n Magazins in einer ersten Version zusammen zu stellen.
Heute morgen habe ich mir dann endlich mal 30min Zeit genommen eine erste Version zu bauen. Ist noch nicht getestet und wahrscheinlich fallen mir auf dem Weg ins Büro noch 10 weitere Verbesserungen ein, aber für eine erste Version sollte das schon ganz gut funktionieren.
Mir war es wichtig, dass die vorhandenen Eigenschaften von Doctrine verwendet werden (z.B. versionable und timstampable) und von Anfang an eine korrektes Verhalten des Models beim Löschen einzelnen Elemente definiert wurde (onDelete).
UPDATE: Was ist Doctrine und warum ist das cool? Doctrine ist ein Object-Relationship-Manager in PHP, der es ermöglicht Objekte und deren Beziehungen zueinander einmalig in einem Schema (meist im yml-Format) zu definieren und daraus dann die Models zu generieren. Das tolle an dieser Vorgehensweise ist, dass man sich nicht mehr mit SQL rumschlagen muss und zum Beispiel beim Löschen eines Objekts, verknüpfte Unterobjekt je nach Definition mit gelöscht werden (falls man das möchte). Doctrine bietet aber noch viele weitere Funktionen.. einfach mal anschauen.
Jetzt aber genug der langen Worte. Hier die erste Version eines Doctrine-Models zum Aufbau eines webbasierten Workflows zur Produktion eines Magazins (hier in Verbindung mit Symfony 1.2):
# User-Model 1:1 Relation zu sf_guard
yeeCoreUser:
columns:
sf_guard_user_id: integer(4)
firstname: string(255)
lastname: string(255)
title: string(255)
position: string(255)
email: string(255)
fon: string(255)
birthday: string(255)
mobile: string(255)
description: string
picture: string(255)
token: string(255)
actAs:
Timestampable:
relations:
User:
class: sfGuardUser
local: sf_guard_user_id
foreignType: one
foreignAlias: User
onDelete: CASCADE
# Dateien
yeeCoreFile:
actAs:
Timestampable:
columns:
name: string(255)
type: string(255)
size: integer
copyright_mode: string(255)
copyright_value: string(255)
metainfo: string
relations:
User:
class: yeeCoreUser
local: user_id
foreignAlias: Issues
onDelete: CASCADE
# Alle Magazine
yeeMagIssue:
actAs:
Timestampable:
columns:
type: string(255)
status: string(255)
name: string(255)
description: string
user_id: integer
xml_cache: clob
relations:
User:
class: yeeCoreUser
local: user_id
foreignAlias: Issues
onDelete: CASCADE
Users:
class: yeeCoreUser
refClass: yeeMagIssueUser
foreignAlias: Issues
local: issue_id
foreign: user_id
# Die Kategorien in Magazinen
yeeMagIssueCategory:
columns:
name:
type: string(255)
notnull: true
issue_id:
type: integer
notnull: true
# Kreuztabelle für die Verknüpfung von Magazinen und Benutzern = diese Leute arbeiten an einem Magazin
yeeMagIssueUser:
actAs:
columns:
issue_id:
type: integer
primary: true
user_id:
type: integer
primary: true
relations:
Issue:
class: yeeMagIssue
local: issue_id
onDelete: CASCADE
User:
class: yeeCoreUser
local: user_id
onDelete: CASCADE
# Article
yeeMagArticle:
actAs:
Timestampable:
Versionable:
columns:
type: string(255)
name: string(255)
description: string
xml_cache: clob
element_cache: clob
# Alle Inhalte eines Artikels (z.B. Textblock, Foto, Auflistung,...)
yeeMagElement:
actAs:
Timestampable:
Versionable:
columns:
type: string(255)
header: string(255)
content: clob
relations:
Files:
class: yeeMagFile
refClass: yeeMagElementFile
foreignAlias: Elements
local: element_id
foreign: file_id
# Kreuztabelle für Artikel-Elemente und Dateien
yeeMagElementFile:
actAs:
columns:
element_id:
type: integer
primary: true
file_id:
type: integer
primary: true
relations:
Element:
class: yeeMagElement
local: element_id
onDelete: CASCADE
File:
class: yeeCoreFile
local: file_id
onDelete: CASCADE
# Alle Autoren (priority definiert die Reihenfolge)
yeeMagAuthor:
columns:
name:
type: string(255)
notnull: true
article_id:
type: integer
notnull: true
description: clob
priority: integer
relations:
Articles:
class: yeeMagArticle
local: article_id
onDelete: SET NULL
# Die Kategorien in der Artikel-Verwaltung
yeeMagArticleCategory:
columns:
name:
type: string(255)
notnull: true
parent_category_id: integer
issue_id:
type: integer
notnull: true
relations:
# Softlinks (nur die ID)
yeeMagArticleSoftlinks:
actAs:
Timestampable:
columns:
content: clob
article_id: integer
relations:
Article:
class: yeeMagArticle
local: article_id
onDelete: CASCADE
# Soflink-Links
yeeMagArticleSoftlinks:
actAs:
Timestampable:
columns:
url: string
description: string
softlink_id: integer
relations:
Softlink:
class: yeeMagSoftlink
local: softlink_id
onDelete: CASCADE






















coole Sache. Ich hätte jedoch ein paar Fragen - bin ziemlich neu in Symfony...
Und zwar hast du in Tabelle yeeCoreUser bei der User-Relation onDelete: Cascade. Das heißt ja, wenn in sfGuardUser der Datensatz gelöscht wird wird auch der in yeeCoreUser gelöscht. Was passiert dann aber anders herum? Kann man das auch irgendwie festlegen?
Wenn man nur actAs: Timestampable: (da fehlt die Tilde ~?!?!), werden die Felder created_at und updated_at und expires_at automatisch angelegt?
Worin liegt denn genau der Vorteil von versionable? Habe das bisher nur auf englisch gelesen und nicht so wirklich kapiert...
Ich hoffe, du kannst mir da weiterhelfen.
Vielen Dank und Grüße,
Dirk
> Was passiert dann aber anders herum? Kann man das auch irgendwie festlegen?
Müsste man genauso im sfGuardUser-Model definieren. Ich hab das da im yeeCoreUser-Model mehr prophylaktisch definiert. Im Prinzip ist das Löschen eines Users aus einem Produktiv-System nicht besonders sinnvoll. Besser man macht den "blank", also überschreibt die Daten, deaktiviert ihn und markiert ihn als gelöscht (sonst kann man unter Umständen überhaupt nicht mehr nachvollziehen, was wer wann getan hat.) Ist aber auch ein Bißchen Geschmacksache.
> Wenn man nur actAs: Timestampable: (da fehlt die Tilde ~?!?!), werden die Felder created_at und updated_at und expires_at automatisch angelegt?
Ja. Die Tilde mache ich übrigens nie und der yaml-Parser hat mir das bisher immer verziehen.
> Worin liegt denn genau der Vorteil von versionable? Habe das bisher nur auf englisch gelesen und nicht so wirklich kapiert...
Das coole bei versionable ist, dass diese Eigenschaft dein Model automatisch versioniert und sich im Hintergrund um alles kümmert. D.h. es wird eine zweite Tabelle angelegt, wo die verschiedenen Versionen eines Datensatzes vorgehalten werden und du kannst bequem auf die Versionierungs-API zugreifen (Versionen switchen,Unterschiede anzeigen,...). Bei der Verwaltung von Artikeln ist das extrem wichtig, da mehrere Autoren sich sonst gegenseitig überschreiben könnten und man ohne Versionierung Gefahr läuft Daten zu verlieren.
Gruß,
Martin
vielen Dank für deine ausführliche Antwort! Jetzt bin ich wieder ein Stück schlauer... ;)
Vielen Dank und Grüße,
Dirk