Willkommen zu DC_General!¶
Dies ist die offizielle Dokumentation des DC_General, eine Erweiterung für das Contao CMS.
Das Handbuch richtet sich an Entwickler, die Erweiterungen für Contao programmieren oder bestehende Erweiterungen auf Basis des DC_General anpassen möchten.
Die Erweiterung DC_General ist eine Alternative für den im Contao-Core enthaltenen DC_Table. Der DC_Table ist als „Driver“ in erster Linie für die Datenmanipulation wie z.B. Datensatz speichern, kopieren und löschen zuständig - zudem ist im DC_Table auch das Mehrfachbearbeiten eigenständig implementiert. Weiterhin kümmert sich der DC_Table um die Anzeige der Daten im Backend z.B. für die Listenansichten oder die Eingabemasken.
Der aktuelle DC_Table ist im Verlauf seiner Entwicklung zu einem recht „monolithischem Gebilde“ herangewachsen, so dass die Erweiterung und Modernisierung sehr schwierig bis unmöglich ist. Zudem ist es nicht möglich, den Code per Unittest zu prüfen.
Aufgrund dieser Schwierigkeiten wurde der DC_General als moderner „Data container“ entwickelt und nutzt die Möglichkeiten, die sich aus einer modernen OOP sowie dem Einsatz von Symfony ergeben, bestens aus. Der DC_General ist gegenüber dem DC_Table „Event driven“, hat eine Abstraktion der Datenquelle sowie eine verbesserte Konfiguration der Abhängigkeiten zwischen Datacontainers.
Wird der DC_General in eigene Erweiterungen implementiert stehen vielfältige Manipulationsmöglichkeiten für die Daten und das Contao-Backend über Events zur Verfügung. Mehr zu den zu den Funktionen unter…
Der DC_General ist in einigen großen Erweiterungen wie MetaModels, Avisota, syncCto oder Language2File implementiert.
Die gängige Abkürzung für den DC_General ist ‚DCG‘.
Diese Dokumentation gliedert sich in drei Bereiche:
Unterstützung und Spenden¶
Die Erweiterung DC_General ist eine kostenfreie Erweiterung auf der Basis von OpenSource und benötigt für eine kontinuierliche Weiterentwicklung die Unterstützung einer aktiven Community. Mitarbeit in Form von Programmierungen oder die Meldung von Fehlern sowie eigenen Workflows werden gern angenommen.
Handbuch¶
Vorstellung des DCG¶
Das Handbuch richtet sich an Entwickler, die Erweiterungen für Contao programmieren oder bestehende Erweiterungen auf Basis des DC_General anpassen möchten.
Die Erweiterung DC_General ist eine Alternative für den im Contao-Core enthaltenen DC_Table. Der DC_Table ist als „Datencontainer-Treiber“ in erster Linie für die Datenmanipulation wie z.B. Datensatz speichern, kopieren und löschen zuständig - zudem ist im DC_Table auch das Mehrfachbearbeiten eigenständig implementiert. Weiterhin kümmert sich der DC_Table um die Anzeige der Daten im Backend z.B. für die Listenansichten oder die Eingabemasken.
Der DCG im Vergleich zum DC_Table¶
Der DCG hat im Vergleich zum DC_Table folgende Vorteile:
- Event Driven
- Objektbasierte Abstraktion der Definitionen
- Abstraktion der Datenquelle
- Verbesserte Konfiguration der Abhängigkeiten zwischen Datacontainers
- modularer Aufbau
- Prüfung der Daten vor Speicherung - nur wenn Daten konsistent sind, wird gespeichert
- Deep-Delete ohne anladen der referenzierten Datacontainer, mehr Variabilität da nicht abhängig von ptable-Bezug
Der DCG unterstützt alle Callback-Aufrufe von Contao und leitet diese in die eigenen Events um.
Ressourcen¶
DCG installieren und aktualisieren¶
Der DCG wird üblicher Weise in einem eigenen Projekt über die Angabe „require“ in der composer.json eingebunden:
... "require": { "php": "^7.1", "contao-community-alliance/dc-general": "^2.1", ... } ...Es ist aber auch möglich, den DCG manuell über den Contao Manager oder über die Konsole zu installieren. Für die Konsole erfolgt die Installation mit
php web/contao-manager.phar.php composer require contao-community-alliance/dc-general
Über den Contao Manager bzw. „composer update“ wird der DCG aktuell gehalten.
Data-Container erstellen¶
Der Data-Container mit dem DCG wird analog wie mit dem DC_Table über die Konfiguration des DCA erstellt.
Als erster Punkt wird im Knoten
config
der Data-Container auf den DCG perGeneral
umgestellt.$GLOBALS['TL_DCA']['tl_my_table'] = array ( // Config 'config' => array ( // Replace the data container Table with General. 'dataContainer' => 'General' 'notCopyable' => true, // wird erst ab DCG 2.2 untertützt 'enableVersioning' => true, // wird erst ab DCG 2.2 untertützt 'sql' => array ( 'keys' => array ( 'id' => 'primary' ) ) // *_callback per Event ), ...Die übrigen, möglichen Parameter bei
config
können wie bei DC_Table eingesetzt werden. Der Knotenctable
für Kindtabellen ist nicht notwendig und wird dafür im Knotendata_provider
behandelt (s.u.).Die Callbacks könnten wie gehabt in
config
eingetragen werden und werden von dem DCG-Legacy-Builder mit in die Abarbeitung übernommen - besser ist es, die Aufgaben einen entsprechenden Event-Listener zu übergeben (siehe Callbacks als Event).Der Standard-Datenprovider (Datentabelle) wird Knoten
data_provider
indata_provider
angegeben.$GLOBALS['TL_DCA']['tl_my_table'] = array ( ... // Add the data container configuration. 'dca_config' => array ( // Configure the data provider and all child data provider. 'data_provider' => array ( // The default data provider, for this data container. 'default' => array ( 'source' => 'tl_my_table' ),Gibt es Kindtabellen, werden ebenfalls diese im Knoten
data_provider
angegeben - beim DC_Table würde das inctable
erfolgen.$GLOBALS['TL_DCA']['tl_my_table'] = array ( ... // Add the data container configuration. 'dca_config' => array ( // Configure the data provider and all child data provider. 'data_provider' => array ( ... // The child data provider for all children are has related to this and has their child relation. // This must configure so when you delete this theme are all relations deletes too. (deep delete) 'tl_my_child' => array ( 'source' => 'tl_my_child' ), ...Mit der Konfiguration der Kindtabelle(n) wird automatisch ein deep delete konfiguriert, d.h. wenn der Elterndatensatz gelöscht wird, werden automatisch auch alle Kind-Datensätze gelöscht.
Die Beziehung zwischen einer Kind- zur Eltern-Tabelle wird in den
childCondition
definiert.$GLOBALS['TL_DCA']['tl_my_table'] = array ( ... // Add the data container configuration. 'dca_config' => array ( ... // Add the child condition. This will announce the relations. 'childCondition' => array ( array ( 'from' => 'tl_my_table', 'to' => 'tl_my_child', 'setOn' => array ( array ( 'to_field' => 'pid', 'from_field' => 'id', ), ), 'filter' => array ( array ( 'local' => 'pid', 'remote' => 'id', 'operation' => '=', ), ), 'inverse' => array ( array ( 'local' => 'pid', 'remote' => 'id', 'operation' => '=', ), ) ), ...Der Knoten
setOn
definiert die Relation zwischen Eltern- zu Kindtabelle.Der Knoten
filter
definiert ein Array von möglichen Filterungen, um die Kinddatensätze einzugrenzen - eine Filterung ist Pflicht.Der Knoten
inverse
ist optional, aber beschleunigt die Datenbankabfrage für eine Abfrage vom Kind- zur Elterntabelle.Die Konfiguration für eine Kindtabelle ist analog der Elterntabelle. Beim
data_provider
wird stattdefault
die Tabelle fürparent
angegeben.$GLOBALS['TL_DCA']['tl_my_child'] = array ( // Config 'config' => array ( 'dataContainer' => 'General', ), // Add the data container configuration. 'dca_config' => array ( // Configure the data provider and all child data provider. 'data_provider' => array ( // The default data provider, for this data container. 'parent' => array ( 'source' => 'tl_my_table' ) ), // Add the child condition. This will announce the relations. 'childCondition' => array ( array ( 'from' => 'tl_my_table', 'to' => 'tl_my_child', 'setOn' => array ( array ( 'to_field' => 'pid', 'from_field' => 'id', ), ), 'filter' => array ( array ( 'local' => 'pid', 'remote' => 'id', 'operation' => '=', ), ), 'inverse' => array ( array ( 'local' => 'pid', 'remote' => 'id', 'operation' => '=', ), ) ) ) ),Die übrigen Parameter im DCA werden analog dem üblichen Vorgehen wie bei einem „DC_Table-Projekt“ vorgenommen. Die Einstellungen können an einer Beispielkonfiguration für tl_theme nachvollzogen werden.
Mehrfachbearbeitung (edit/overrideAll)¶
Der DCG bringt eine eigene Mehrfachbearbeitung mit, welche einen erweiterten Funktionsumfang gegenüber der Standardmethode von Contao hat. Die Mehrfachbearbeitung wird automatisch mit der Definition general im DCA aktiviert (siehe Data-Container erstellen).
Die Vorteile sind:
- Vor-, Zurück- und Beenden-Button
- es werden nur die Properties für die Bearbeitungsauswahl angezeigt, die für die Bearbeitung relevant sind; werden verschiedenartige Widgets ausgewählt, ist nur noch die gemeinsame Schnittmenge an Properties sichtbar
- es stehen zwei weitere eval-Parameter für das DCA zur Verfügung als doNotEditMultiple und doNotOverrideMultiple für Ansteuerung der Sichtbarkeit für den jeweiligen Bearbeitungstyp.
Die zusätzlichen DCA-Parameter für die Mehrfachbearbeitung haben folgende Einsatzmöglichkeit:
- doNotEditMultiple - verhindert für ein Property das Editieren in der Mehrfachberabeitung
- doNotOverrideMultiple - verhindert für ein Property das Überschreiben in der Mehrfachberabeitung; z.B. Properties, die Unique bleiben müssen wie Spaltennamen für Tabellen von MetaModels.
Ein Beispiel für einen DCA-Eintrag:
$GLOBALS['TL_DCA']['tl_my_table'] = array ( ... 'fields' => array ( 'my_field' => array ( ... 'eval' => array( 'doNotEditMultiple' => true, // Hide at editAll. 'doNotOverrideMultiple' => true, // Hide at overrideAll. ...
Kochbuch¶
DCG „Kochbuch“¶
In dem MetaModels „Kochbuch“ sind verschiedene Snippets, Tipps und Tricks rund um den Einsatz mit dem DCG zusammengefasst.
In die Auflistung können gern interessante oder ungewöhnliche Lösungen aufgenommen werden - bitte eigene „Rezepte“ oder Links zum Forum bzw. andere Webseiten an die folgende E-Mail senden: manual@metamodel.me
Eingabemaske ohne Tabelle¶
Wir erstellen eine Eingabemaske, die direkt durch einen Navigationslink im Backend aufgerufen wird und die Daten aber in keiner Tabelle gespeichert werden.
In unserem Beispiel nutzen wir das für eine eigene Importmöglichkeit z.B. von CSV-Dateien.
Als erstes benötigen wir einen Navigationslink in der Backend-Navigation in der config.php. Anschließen manipulieren wir den Link, so dass wir direkt in einer Eingabemaske landen. Die Manipulation erfolgt über einen Hook von Contao getUserNavigation und binden den Hook als Service über die `service.yml`ein.
Im letzten Schritt erstellen wir die Widgets für die Eingabemaske per DCA-Definition in einer dcaconfig.php und verarbeiten den Aufruf in einem EventListener. Das Besondere an der DCA ist, dass hier der NoOpDataProvider zum Einsatz kommt, der die übliche Speicherung in einer zugehörigen Tabelle unterbindet.
Die Dateien sind in einem Beispiel zusammengefasst.
Referenz¶
DCG Referenz¶
Die Referenzen sind für Entwickler gedacht, die Funktionen aus dem DCG nutzen möchten.
DC_General API¶
Die DC_General API bildet die Schnittstelle zur eigenen Programmierung und Erweiterung.
DC_General Events¶
Callbacks als Event¶
Die Callbacks aus DC_Table werden über den Legacy-Builder abgefangen und im DCG verarbeitet. Es ist aber zu Empfehlen, statt des Callback- Aufrufes den entsprechenden Event direkt zu verwenden.
Die Events werden als Service angesprochen und können damit auch mit einer Priorität der Verarbeitungsreihenfolge versehen werden.
Folgend eine Auflistung, welcher Callback mit welchem Event seine Ersetzung hat:
Callback in
config
¶
Callback Event onload_callback dc-general.factory.create-dc-general onsubmit_callback dc-general.model.post-persist ondelete_callback dc-general.model.post-delete oncut_callback dc-general.model.post-paste oncopy_callback dc-general.model.post-duplicate Callback in
list/sorting
¶
Callback Event header_callback dc-general.view.contao2backend.get-parent-header paste_button_callback dc-general.view.contao2backend.get-paste-root-button paste_button_callback dc-general.view.contao2backend.get-paste-button child_record_callback dc-general.view.contao2backend.parent-view-child-record Callback in
list/label
¶
Callback Event group_callback dc-general.view.contao2backend.get-group-header label_callback dc-general.view.contao2backend.model-to-label Callback in
list/global_operations
¶
Callback Event button_callback dc-general.view.contao2backend.get-global-button Callback in
list/operations
¶
Callback Event button_callback dc-general.view.contao2backend.get-global-button Callback in
fields
¶
Callback Event load_callback dc-general.view.contao2backend.decode-property-value-for-widget save_callback dc-general.view.contao2backend.encode-property-value-from-widget options_callback dc-general.view.contao2backend.get-property-options input_field_callback dc-general.view.contao2backend.build-widget wizard dc-general.view.contao2backend.manipulate-widget DCA Mapping¶
In der folgenden Auflistung sind alle bekannten Angaben zum DCA aufgeführt:
$GLOBALS['TL_DCA']['tl_example'] = array ( // Config 'config' => array ( 'label' => &$GLOBALS['TL_LANG']['tl_example']['headline'], 'dataContainer' => 'General', 'ptable' => 'tl_parent', 'dynamicPtable' => true, // require 'ptable'=>'' 'ctable' => array('tl_child1', 'tl_child2'), 'validFileTypes' => 'jpg,png,gif', 'uploadScript' => '', 'closed' => true, 'notEditable' => true, 'notDeletable' => true, 'switchToEdit' => true, 'enableVersioning' => true, 'doNotCopyRecords' => true, 'doNotDeleteRecords' => true, 'onload_callback' => array ( array('<class name>', '<method name>') ), 'onsubmit_callback' => array ( array('<class name>', '<method name>') ), 'ondelete_callback' => array ( array('<class name>', '<method name>') ), 'oncut_callback' => array ( array('<class name>', '<method name>') ), 'oncopy_callback' => array ( array('<class name>', '<method name>') ), 'sql' => array ( 'keys' => array ( 'id' => 'primary', 'pid' => 'index', 'alias' => 'index' ) ) ), // DcGeneral config 'dca_config' => array ( 'callback' => 'DcGeneral\Callbacks\ContaoStyleCallbacks', 'controller' => 'DcGeneral\Controller\DefaultController', 'view' => 'DcGeneral\View\DefaultView', 'data_provider' => array ( 'default' => array ( 'type' => '...\ContaoDataProviderInformation', 'factory' => '...\ContaoDataProviderInformationFactory', 'class' => 'DcGeneral\Data\DefaultDriver', 'source' => 'tl_example' ), 'parent' => array ( 'type' => '...\ContaoDataProviderInformation', 'factory' => '...\ContaoDataProviderInformationFactory', 'class' => 'DcGeneral\Data\DefaultDriver', 'source' => 'tl_parent' ) ), 'rootEntries' => array( 'tl_example' => array( 'setOn' => array ( array( 'property' => 'id', 'value' => 0 ), ), 'filter' => array ( array ( 'property' => 'id', 'value' => 0, 'operation' => '=' ) ) ) ), 'childCondition' => array( array( 'from' => 'tl_parent', 'to' => 'tl_example', 'setOn' => array ( array( 'from_field' => 'id', 'to_field' => 'pid' ), ), 'filter' => array ( array ( 'remote' => 'id', 'local' => 'pid', 'operation' => '=' ) ), 'inverse' => array ( array ( 'local' => 'pid', 'remote' => 'id', 'operation' => '=' ) ) ) ) ), // List 'list' => array ( 'sorting' => array ( 'mode' => 6, 'flag' => 6, 'panelLayout' => 'filter;search,limit', 'fields' => array('published DESC', 'title', 'author'), 'headerFields' => array('title', 'headline', 'author'), 'header_callback' => array('<class name>', '<method name>'), 'icon' => 'path/to/icon.png', 'root' => 6, 'filter' => array(array('status=?', 'active')), 'disableGrouping' => true, 'paste_button_callback' => array('<class name>', '<method name>'), 'child_record_callback' => array('<class name>', '<method name>'), 'child_record_class' => 'css_class_name' ), 'label' => array ( 'fields' => array('title', 'inColumn'), 'format' => '%s <span style="color:#b3b3b3">[%s]</span>', 'maxCharacters' => 255, 'group_callback' => array('<class name>', '<method name>'), 'label_callback' => array('<class name>', '<method name>') ), 'global_operations' => array ( 'all' => array ( 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], 'href' => 'act=select', 'class' => 'header_edit_all', 'attributes' => 'onclick="Backend.getScrollOffset()"', 'button_callback' => array('<class name>', '<method name>') ) ), 'operations' => array ( 'delete' => array ( 'label' => &$GLOBALS['TL_LANG']['tl_example']['delete'], 'href' => 'act=delete', 'icon' => 'delete.gif', 'attributes' => 'onclick="Backend.getScrollOffset()"', 'button_callback' => array('<class name>', '<method name>') ), ) ), // Palettes 'palettes' => array ( '__selector__' => array('protected'), 'default' => '{title_legend},title,alias,author;...' ), // Subpalettes 'subpalettes' => array ( 'protected' => 'groups' ), // Fields 'fields' => array ( 'title' => array ( 'label' => &$GLOBALS['TL_LANG']['tl_example']['title'], 'default' => 'default value', 'exclude' => true, 'search' => true, 'sorting' => true, 'filter' => true, 'flag' => 12, 'length' => 3, 'inputType' => 'text', 'options' => array('a', 'b', 'c'), 'options_callback' => array('<class name>', '<method name>'), 'foreignKey' => 'tl_other_table.name', 'reference' => &$GLOBALS['TL_LANG']['tl_example']['title'], 'explanation' => &$GLOBALS['TL_LANG']['tl_example']['title'], 'input_field_callback' => array('<class name>', '<method name>'), 'wizard' => array('<class name>', '<method name>'), 'relation' => array('type'=>'hasOne', 'load'=>'eager'), 'load_callback' => array ( array('<class name>', '<method name>') ), 'save_callback' => array ( array('<class name>', '<method name>') ), 'eval' => array( 'helpwizard' => true, 'mandatory' => true, 'maxlength' => 255, 'minlength' => 255, 'fallback' => true, 'rgxp' => 'friendly', 'cols' => 12, 'rows' => 6, 'wrap' => 'hard', 'multiple' => true, 'size' => 6, 'style' => 'border:2px', 'rte' => 'tinyFlash', 'submitOnChange' => true, 'nospace' => true, 'allowHtml' => true, 'preserveTags' => true, 'decodeEntities' => true, 'doNotSaveEmpty' => true, 'alwaysSave' => true, 'spaceToUnderscore' => true, 'unique' => true, 'encrypt' => true, 'trailingSlash' => true, 'files' => true, 'filesOnly' => true, 'extensions' => 'jpg,png,gif', 'path' => 'path/inside/of/contao', 'fieldType' => 'checkbox', 'includeBlankOption' => true, 'blankOptionLabel' => '- none selected -', 'chosen' => true, 'findInSet' => true, 'datepicker' => true, 'colorpicker' => true, 'feEditable' => true, 'feGroup' => 'contact', 'feViewable' => true, 'doNotCopy' => true, 'hideInput' => true, 'doNotShow' => true, 'isBoolean' => true, 'disabled' => true, 'readonly' => true, 'doNotEditMultiple' => true, 'doNotOverrideMultiple' => true, ), 'sql' => 'varchar(255) NOT NULL default ''' ) ) ); -> `Data provider mapping`_ -> `Listing mapping`_ -> *ignored* -> `Data provider mapping`_ -> *ignored* -> *ignored* -> *ignored* -> *ignored* -> -> -> -> -> -> -> *ignored* -> ^ ^ ^ -> ^ ^ ^ -> ^ ^ ^ -> ^ ^ ^ -> ^ ^ ^ -> *ignored* ^ ^ ^ ^ ^ ^ ^ ^ -> -> `Deprecated DcGeneral config`_ -> `Deprecated DcGeneral config`_ -> `Deprecated DcGeneral config`_ -> `Data provider mapping`_ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ -> `Root entries mapping`_ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ -> `Parent-child condition mapping`_ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ -> `Backend view mapping`_ -> `Basic config mapping`_ -> `Listing mapping`_ -> `Panel layout mapping`_ -> `Listing mapping`_ ^ ^ ^ -> -> -> `Listing mapping`_ -> -> `Listing mapping`_ ^ -> `Listing mapping`_ ^ ^ ^ ^ ^ ^ -> `Global operations mapping`_ -> `Operation mapping`_ ^ ^ ^ ^ ^ ^ ^ -> `Model operations mapping`_ -> `Operation mapping`_ ^ ^ ^ ^ ^ ^ ^ -> `Palettes mapping`_ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ -> `Properties (fka fields) mapping`_ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ Hide at editAll. Hide at overrideAll. -> *ignored*Impressum, Datenschutz, Lizenz, Quellenangaben¶
Impressum¶
Die Angaben zum Impressum und rechtliche Hinweise finden Sie auf der Seite https://c-c-a.org/impressum
Datenschutz¶
Die Angaben zum Datenschutz finden Sie auf der Seite https://c-c-a.org/datenschutz
Lizenz der Dokumentation¶
DC_General Dokumentation/Handbuch ist lizenziert unter einer Creative Commons Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 4.0 International Lizenz.