Schnellnavigation:

Kategorien

« Oktober 2010»
S M T W T F S
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

Letzte Nachrichten

DSGVO
26.05.2018 18:39
Trackingtools und Datenschutzerklärung
14.03.2014 23:07
1:n und n:1 Relationen in Extbase
06.12.2013 12:04
Erste Abmahnungen wegen Google Analytics
04.10.2013 12:11

Kopieren Sie diesen Link in Ihren RSS-Reader

RSS 0.91Nachrichten
RSS 2.0Nachrichten

In eigener Sache

Peter Linzenkirchner, Lisardo EDV Beratung in Augsburg. Freelance und Partner für Design- und Webagenturen in Augsburg und München. Pixelgenaue Templates, valides HTML, barrierearm. TYPO3-Projekte, Extension-Programmierung und mehr ... 

Zur Zeit wird gefiltert nach: API
Filter zurücksetzen

Ziel ist die Erstellung einer Extension, die alle Features zur Mehrsprachigkeit bietet, die zur Ausstattung von TYPO3 gehören.

Die Datenbank-Struktur

Diese drei Felder müssen zusätzlich in der Extension in der »ext_tables.sql« angelegt werden:

  1. sys_language_uid int(11) DEFAULT'0'NOT NULL,
  2. l18n_parent int(11) DEFAULT'0'NOT NULL,
  3. l18n_diffsource mediumblob NOT NULL,

Die »ext_tables.php« muss so erweitert werden:

  1. "ctrl"=> Array (
  2.   [--snip--]
  3.   "delete"=>"deleted",   
  4.   'transOrigPointerField'  =>'l18n_parent',
  5.   'transOrigDiffSourceField'=>'l18n_diffsource',
  6.   'languageField'      =>'sys_language_uid',
  7.   [--snip--]

Und das »columns«-Array in der »tca.php« ebenfalls:

  1. "columns"=> Array (
  2.   [--snip--]
  3.   'sys_language_uid'=> array(
  4.     'exclude'=>1,
  5.     'label' =>'LLL:EXT:lang/locallang_general.xml:LGL.language',
  6.     'config'  => array(
  7.         'type'                =>'select',
  8.         'foreign_table'   =>'sys_language',
  9.         'foreign_table_where'=>'ORDER BY sys_language.title',
  10.         'items'       => array(
  11.             array('LLL:EXT:lang/locallang_general.php:LGL.allLanguages', -1),
  12.             array('LLL:EXT:lang/locallang_general.php:LGL.default_value',0)
  13.         )
  14.     )
  15.   ),
  16.   'l18n_parent'=> array(
  17.     'displayCond'=>'FIELD:sys_language_uid:>:0',
  18.     'exclude'  =>1,
  19.     'label'   =>'LLL:EXT:lang/locallang_general.xml:LGL.l18n_parent',
  20.     'config'      => array(
  21.         'type'  =>'select',
  22.         'items'=> array(
  23.             array('',0),
  24.         ),
  25.         'foreign_table'   =>'tx_meineExtension',
  26.         'foreign_table_where'=>'AND tx_meineExtension.uid=###CURRENT_PID### AND tx_meineExtension.sys_language_uid IN (-1,0)',
  27.     )
  28.   ),
  29.   'l18n_diffsource'=> array(
  30.     'config'=> array(
  31.         'type'=>'passthrough'
  32.     )
  33.   ),
  34.   [--snip--]
  35. ),

Wichtig: Damit die Zuordnung der Sprach-Overlays funktioniert, muss oben bei »foreign_table« die korrekte Tabellenbezeichnung der Extension eingegeben werden, ebenso bei »foreign_table_where« – insgesamt also dreimal.

Damit die Redakteure später nicht nur Language-Overlay-Datensätze anlegen, sondern einen Datensatz direkt einer Sprache zuordnen können (um z. B. Datensätze in einer Fremsprache anlegen zu können, ohne dass es zuvor einen Datensatz in der Standardsprache gibt), muss auch noch das »types«-Array in der »tca.php« erweitert werden:

  1. "types"=> Array (
  2.   "0"=> Array("showitem"=>"hidden;;1;;1-1-1, sys_language_uid;;1;;3-3-3,
  3.   [-- snip --]
  4. ),

Damit erscheint im Backend ein DropDown-Menü mit einer Auswahl von allen definierten Alternativ-Sprachen sowie der Auswahl »Alle«.

Im Backend müsste jetzt alles richtig ausgegeben werden: Die Redakteure können neue Datensätze anlegen, diese einer oder allen Sprachen zuordnen, oder nachträglich Datensätze in der Standardsprache übersetzen. Also genauso wie bei den normalen Inhaltselementen.

Natürlich muss die Ausgabe im Frontend das entsprechend berücksichtigen

Ausgabe von sprachabhängigen Texten ins Template

Damit sind nicht die Inhalte aus den Datentabellen gemeint sondern zum Beispiel Überschriften oder Labels für Formularfelder, die ebenfalls den Sprachen angepasst werden müssen. Diese Texte werden zunächst in die »locallang.xml« im pi1-Ordner der Extension eingetragen:

  1. <?xml version="1.0"encoding="utf-8"standalone="yes"?>
  2. <T3locallang>
  3.     <meta type="array">
  4.         <type>module</type>
  5.         <description>Language labelsforplugin tx_meineExtension_pi1</description>
  6.     </meta>
  7.     <data type="array">
  8.         <languageKey index="default"type="array">
  9.             <label index="eindeutigerName">Headline in Defaultsprache</label>
  10.         </languageKey>
  11.         <languageKey index="en"type="array">
  12.             <label index="eindeutigerName">Headline in English</label>
  13.         </languageKey>
  14.         <languageKey index="de"type="array">
  15.             <label index="eindeutigerName">Überschrift in Deutsch</label>
  16.         </languageKey>
  17.     </data>
  18. </T3locallang>

Diese Datei muss in UTF-8 abgespeichert werden, sonst gibt es entweder eine leere Frontend-Seite oder eine Fehlermeldung, je nach TYPO3-Version. Sie darf weder HTML noch Entities enthalten. In der Regel wirds das nicht brauchen (da die Datei ja UTF-8 ist), aber falls doch, gibt es folgende Möglichkeiten:

Entitiy definieren:

  1. <?xml version="1.0"encoding="utf-8"standalone="yes"?>
  2. <!DOCTYPE T3locallang [
  3.     <!ENTITY euro"&amp;euro;">
  4. ]>
  5. <T3locallang>
  6.     <meta type="array">
  7.         <type>module</type>
  8.         <description>Language labelsforplugin tx_tmeineExtension_pi1</description>
  9.     </meta>
  10.     <data type="array">
  11.         <languageKey index="default"type="array">
  12.                 <label index="eindeutigerName">Preis is&amp;euro;</label>
  13.         </languageKey>
  14.     </data>
  15. </T3locallang>

Sieht komisch aus, funktioniert aber. Die nächste Möglichkeit ist wahrscheinlich einfacher:

HTML in »locallang.xml«

  1. <?xml version="1.0"encoding="utf-8"standalone="yes"?>
  2. <T3locallang>
  3.     <meta type="array">
  4.         <type>module</type>
  5.         <description>Language labelsforplugin tx_meineExtension_pi1</description>
  6.     </meta>
  7.     <data type="array">
  8.         <languageKey index="default"type="array">
  9.             <label index="eindeutigerName"><![CDATA[<strong style='color:#00005a;'>und so weiter</strong>]]></label>
  10.         </languageKey>
  11.     </data>
  12. </T3locallang>

Jeder HTML-eintrag muss also mit

  1. <![CDATA[   ]]>

maskiert werden.

Ausgabe ins Frontend

Damit eine Ausgabe möglich wird, muss die »class.tx_meineExtension_pi1.php« erweitert werden:

  1. classtx_meineExtension_pi1extendstslib_pibase {
  2.   // snip
  3.   functionmain($content,$conf){
  4.   // snip
  5.   $this->pi_loadLL();
  6.   // snip
  7.  

 

Die Zeile $this->pi_loadLL(); lädt die notwendige Klasse.

Die eigentliche Ausgabe des Textes erfolgt so:

  1. $txt=$this->pi_getLL('eindeutigerName','default',FALSE);

Die Optionen bedeuten:

  • Name/Bezeichnung des Eintrags in der »locallang.xml«
  • Defaultwert – in der Regel ein englischer Begriff, als Platzhalter, falls der Eintrag in der locallang.xml fehlt
  • TRUE wenn der Wert durch htmlspecialchars() laufen soll

Daten aus den Datentabellen auslesen

Das erweist sich als das eigentliche Problem. Ich habe noch keine endgültige Lösung, aber die folgende funktioniert fürs erste.

1. Schritt: Daten aus der Tabelle holen. Dabei interessiert erstmal nur der WHERE-Teil des sql-Statements, der Rest dürfte klar sein:

  1. if ($GLOBALS['TSFE']->sys_language_content==0){
  2.   $whereClause="pid=xyz".
  3.   ' AND (sys_language_uid IN (-1,0) OR (sys_language_uid='.
  4.   $GLOBALS['TSFE']->sys_language_uid.
  5.   ' AND l18n_parent=0)) '.
  6.   $this->cObj->enableFields($tableName);
  7. }else{
  8.   $whereClause="pid=xyz".
  9.   ' AND (sys_language_uid IN (-1,0) OR (sys_language_uid='.
  10.   $GLOBALS['TSFE']->sys_language_uid.
  11.   ' AND l18n_parent=0)) ';
  12. }

Erläuterung: In der Defaultsprache wird »$this->cObj->enableFields()« aufgerufen, damit die versteckten Datensätze (und die gelöschten etc) nicht sichtbar werden. Bei den Alternativ-Sprachen entfällt das in diesem Schritt sondern folgt erst in einem weiteren. Die Zeile »’ AND (sys_language_uid ….« wählt alle Datensätze aus, die in »sys_language_uid« 0 (= Standardsprache) oder -1 (= alle Sprachen) stehen haben. Sie wählt aber auch alle Datensätze in der Alternativsprache aus, die keinen Eintrag in der Standardsprache haben.

Im nächsten Schritt werden die Sprach-Overlays geholt: die Felder des Datensatzes werden mit den Einträgen in der Alternativ-Sprache überschrieben:

  1. while ($row=$GLOBALS ['TYPO3_DB']->sql_fetch_assoc($newsResult) ){
  2.   if ($GLOBALS['TSFE']->sys_language_content){
  3.       $row=$GLOBALS['TSFE']->sys_page->getRecordOverlay($tableName,$row,$GLOBALS['TSFE']->sys_language_content,$GLOBALS['TSFE']->sys_language_mode=='strict'?'hideNonTranslated':'');
  4.   }
  5. }

Je nach Spracheinstellung werden dabei auch Einträge entfernt – also z. B. wenn sys_language_mode=strict eingestellt ist, werden alle nicht übesetzten Einträge entfernt.

Über GLOBALS[‘TSFE’]->fe_user kann man sich die gesamte Konfiguration der Frontenduser ausgeben lassen:

  1. $GLOBALS['TYPO3_DB']->debugOutput=true;
  2. echot3lib_div::debug($GLOBALS['TSFE']->fe_user);

Ergebnis:

  1. tslib_feuserauth Object
  2. (
  3.     [global_database]=>
  4.     [session_table]=>fe_sessions
  5.     [name]=>fe_typo_user
  6.     [get_name]=>ftu
  7.     [user_table]=>fe_users
  8.     [username_column]=>username
  9.     [userident_column]=>password
  10.     [userid_column]=>uid
  11.     [lastLogin_column]=>lastlogin
  12.     [enablecolumns]=> Array
  13.         (
  14.             [deleted]=>deleted
  15.             [disabled]=>disable
  16.             [starttime]=>starttime
  17.             [endtime]=>endtime
  18.         )
  19.  
  20.     [formfield_uname]=>user
  21.     [formfield_uident]=>pass
  22.     [formfield_chalvalue]=>challenge
  23.     [formfield_status]=>logintype
  24.     [security_level]=>normal
  25.     [auth_include]=>
  26.     [auth_timeout_field]=>6000
  27.     [lifetime]=>0
  28.     [gc_time]=>6000
  29.     [gc_probability]=>1
  30.     [writeStdLog]=>
  31.     [writeAttemptLog]=>
  32.     [sendNoCacheHeaders]=>0
  33.     [getFallBack]=>1
  34.     [hash_length]=>10
  35.     [getMethodEnabled]=>1
  36.     [lockIP]=>2
  37.     [lockHashKeyWords]=>useragent
  38.     [warningEmail]=>
  39.     [warningPeriod]=>3600
  40.     [warningMax]=>3
  41.     [checkPid]=>1
  42.     [checkPid_value]=>0
  43.     [id]=>ed2f34eb0c
  44.     [cookieId]=>ed2f34eb0c
  45.     [loginFailure]=>
  46.     [loginSessionStarted]=>
  47.     [get_URL_ID]=>
  48.     [newSessionID]=>
  49.     [forceSetCookie]=>
  50.     [dontSetCookie]=>
  51.     [challengeStoredInCookie]=>
  52.     [loginType]=>FE
  53.     [svConfig]=>
  54.     [writeDevLog]=>
  55.     [formfield_permanent]=>permalogin
  56.     [usergroup_column]=>usergroup
  57.     [usergroup_table]=>fe_groups
  58.     [groupData]=> Array
  59.         (
  60.             [title]=> Array
  61.                 (
  62.                 )
  63.  
  64.             [uid]=> Array
  65.                 (
  66.                 )
  67.  
  68.             [pid]=> Array
  69.                 (
  70.                 )
  71.  
  72.         )
  73.  
  74.     [TSdataArray]=> Array
  75.         (
  76.             [0]=>
  77.         )
  78.  
  79.     [userTS]=> Array
  80.         (
  81.         )
  82.  
  83.     [userTSUpdated]=>0
  84.     [showHiddenRecords]=>0
  85.     [sesData]=> Array
  86.         (
  87.             [th_mailformplus17]=>
  88.         )
  89.  
  90.     [sesData_change]=>0
  91.     [userData_change]=>0
  92.     [is_permanent]=>1
  93.     [user]=>
  94. )
  95. </pre>|</b><b>|Object:<pre>tslib_feuserauth Object
  96. (
  97.     [global_database]=>
  98.     [session_table]=>fe_sessions
  99.     [name]=>fe_typo_user
  100.     [get_name]=>ftu
  101.     [user_table]=>fe_users
  102.     [username_column]=>username
  103.     [userident_column]=>password
  104.     [userid_column]=>uid
  105.     [lastLogin_column]=>lastlogin
  106.     [enablecolumns]=> Array
  107.         (
  108.             [deleted]=>deleted
  109.             [disabled]=>disable
  110.             [starttime]=>starttime
  111.             [endtime]=>endtime
  112.         )
  113.  
  114.     [formfield_uname]=>user
  115.     [formfield_uident]=>pass
  116.     [formfield_chalvalue]=>challenge
  117.     [formfield_status]=>logintype
  118.     [security_level]=>normal
  119.     [auth_include]=>
  120.     [auth_timeout_field]=>6000
  121.     [lifetime]=>0
  122.     [gc_time]=>6000
  123.     [gc_probability]=>1
  124.     [writeStdLog]=>
  125.     [writeAttemptLog]=>
  126.     [sendNoCacheHeaders]=>0
  127.     [getFallBack]=>1
  128.     [hash_length]=>10
  129.     [getMethodEnabled]=>1
  130.     [lockIP]=>2
  131.     [lockHashKeyWords]=>useragent
  132.     [warningEmail]=>
  133.     [warningPeriod]=>3600
  134.     [warningMax]=>3
  135.     [checkPid]=>1
  136.     [checkPid_value]=>0
  137.     [id]=>ed2f34eb0c
  138.     [cookieId]=>ed2f34eb0c
  139.     [loginFailure]=>
  140.     [loginSessionStarted]=>
  141.     [get_URL_ID]=>
  142.     [newSessionID]=>
  143.     [forceSetCookie]=>
  144.     [dontSetCookie]=>
  145.     [challengeStoredInCookie]=>
  146.     [loginType]=>FE
  147.     [svConfig]=>
  148.     [writeDevLog]=>
  149.     [formfield_permanent]=>permalogin
  150.     [usergroup_column]=>usergroup
  151.     [usergroup_table]=>fe_groups
  152.     [groupData]=> Array
  153.         (
  154.             [title]=> Array
  155.                 (
  156.                 )
  157.  
  158.             [uid]=> Array
  159.                 (
  160.                 )
  161.  
  162.             [pid]=> Array
  163.                 (
  164.                 )
  165.  
  166.         )
  167.  
  168.     [TSdataArray]=> Array
  169.         (
  170.             [0]=>
  171.         )
  172.  
  173.     [userTS]=> Array
  174.         (
  175.         )
  176.  
  177.     [userTSUpdated]=>0
  178.     [showHiddenRecords]=>0
  179.     [sesData]=> Array
  180.         (
  181.             [th_mailformplus17]=>
  182.         )
  183.  
  184.     [sesData_change]=>0
  185.     [userData_change]=>0
  186.     [is_permanent]=>1
  187.     [user]=>
  188. )
  189. </pre>|</b><b>|Object:<pre>tslib_feuserauth Object
  190. (
  191.     [global_database]=>
  192.     [session_table]=>fe_sessions
  193.     [name]=>fe_typo_user
  194.     [get_name]=>ftu
  195.     [user_table]=>fe_users
  196.     [username_column]=>username
  197.     [userident_column]=>password
  198.     [userid_column]=>uid
  199.     [lastLogin_column]=>lastlogin
  200.     [enablecolumns]=> Array
  201.         (
  202.             [deleted]=>deleted
  203.             [disabled]=>disable
  204.             [starttime]=>starttime
  205.             [endtime]=>endtime
  206.         )
  207.  
  208.     [formfield_uname]=>user
  209.     [formfield_uident]=>pass
  210.     [formfield_chalvalue]=>challenge
  211.     [formfield_status]=>logintype
  212.     [security_level]=>normal
  213.     [auth_include]=>
  214.     [auth_timeout_field]=>6000
  215.     [lifetime]=>0
  216.     [gc_time]=>6000
  217.     [gc_probability]=>1
  218.     [writeStdLog]=>
  219.     [writeAttemptLog]=>
  220.     [sendNoCacheHeaders]=>0
  221.     [getFallBack]=>1
  222.     [hash_length]=>10
  223.     [getMethodEnabled]=>1
  224.     [lockIP]=>2
  225.     [lockHashKeyWords]=>useragent
  226.     [warningEmail]=>
  227.     [warningPeriod]=>3600
  228.     [warningMax]=>3
  229.     [checkPid]=>1
  230.     [checkPid_value]=>0
  231.     [id]=>ed2f34eb0c
  232.     [cookieId]=>ed2f34eb0c
  233.     [loginFailure]=>
  234.     [loginSessionStarted]=>
  235.     [get_URL_ID]=>
  236.     [newSessionID]=>
  237.     [forceSetCookie]=>
  238.     [dontSetCookie]=>
  239.     [challengeStoredInCookie]=>
  240.     [loginType]=>FE
  241.     [svConfig]=>
  242.     [writeDevLog]=>
  243.     [formfield_permanent]=>permalogin
  244.     [usergroup_column]=>usergroup
  245.     [usergroup_table]=>fe_groups
  246.     [groupData]=> Array
  247.         (
  248.             [title]=> Array
  249.                 (
  250.                 )
  251.  
  252.             [uid]=> Array
  253.                 (
  254.                 )
  255.  
  256.             [pid]=> Array
  257.                 (
  258.                 )
  259.  
  260.         )
  261.  
  262.     [TSdataArray]=> Array
  263.         (
  264.             [0]=>
  265.         )
  266.  
  267.     [userTS]=> Array
  268.         (
  269.         )
  270.  
  271.     [userTSUpdated]=>0
  272.     [showHiddenRecords]=>0
  273.     [sesData]=> Array
  274.         (
  275.             [th_mailformplus17]=>
  276.         )
  277.  
  278.     [sesData_change]=>0
  279.     [userData_change]=>0
  280.     [is_permanent]=>1
  281.     [user]=>
  282. <p>)</p>

Dabei werden die Daten des eingeloggten Benutzers und seiner Benutzergruppe ausgegeben. Damit kann einfach überprüft werden, ob es sich um einen gültigen Benutzer handelt und ob er der richtigen Gruppe angehört.

In eigenen Extensions gibt es eine einfache Möglichkeit zu verhindern, dass beim Einbinden von Dateien für den Download – zum Beispiel aus einem Unterordner von fileadmin – diese Dateien nochmals in den /uploads/-Ordner kopiert werden. Dazu muss in der tca.php der internal_type von file auf file_refence umgestellt werden:

  1. 'file'=> array (       
  2.     'exclude'=>0,    
  3.     'label'=>'LLL:EXT:MyExtension/locallang_db.xml:tx_myExtension.file',       
  4.     'config'=> array (
  5.         'type'=>'group',
  6.         ## hier umstellen von file auf file_refence: ######
  7.         'internal_type'=>'file_reference',
  8.         'allowed'=>'',   
  9.         'disallowed'=>'php,php3',  
  10.         'max_size'=>$GLOBALS['TYPO3_CONF_VARS']['BE']['maxFileSize'],  
  11.         'uploadfolder'=>'uploads/myExtension',
  12.         'size'=>1,   
  13.         'minitems'=>0,
  14.         'maxitems'=>1,
  15.     )
  16. ),

Dadurch wird nicht mehr der reine Dateiname in der Datenbank gespeichert sondern der komplette Pfad auf die Datei. Natürlich muss das bei der weiteren Verarbeitung der Datei auch entsprechend berücksichtigt werden.

Weiterführende Links

 

Datenbankabfragen sollten in Extensions nie über die normalen SQL-Befehle erfolgen. Gründe:

  • die globale Datenbank-Abstraktionsschicht DBAL wird eingbezogen; heisst, die Extension läuft automatisch auch mit anderen Datenbanken als MySQL
  • die Verbindung zur Datenbank muss nicht eigens erstellt und beendet werden
  • die INSERT-Queries führen automatisch ein FullQuote durch (die Mindest-Absicherung gegen SQL-Injection)
  • die verfügbaren Befehle sind zum Teil einfach bequem …

Funktionen aufrufen

Die Klassen müssen nicht initialisiert werden, es reicht, sie über die GLOBALS aufzurufen:

  1. $GLOBALS['TYPO3_DB']->exec_SELECTquery( .... )

Absicherung gegen SQL-Injection

Alle Eingaben, die vom Besucher der Website kommen (also über GET oder POST) müssen vorher abgesichert werden. Das betrifft vor allem die Befehle zum Einfügen in die Datenbank, ausserdem aber auch die Übernahme von Benutzerdaten in die where-Abschnitte der SQL-Abfragen. Auch hier sollten die Typo3-Funktionen benutzt werden, damit die Quotierung für jede Datenbank passend erfolgt. Folgende Funktionen stehen zur Verfügung:

  1. # Quotieren von Zeichenketten
  2. $GLOBALS['TYPO3_DB']->fullQuoteStr($str,$table);

Parameter

  • string: Zeichenkette, die quotiert werden soll
  • string: Tabellenname (daraus entnimmt DBAL die korrekte Quotierungstechnik)

  1. # Quotieren von eindimensionalen Arrays:
  2. $GLOBALS['TYPO3_DB']->fullQuoteArray($arr,$table,$noQuote=FALSE);

Parameter

  • array: eindimensionales oder assoziatives Array mit den Daten
  • string: Tabellenname (daraus entnimmt DBAL die korrekte Quotierungstechnik)
  • string/array: Liste oder Array von Schlüsseln, die nicht quotiert werden sollen. Nur bei assoziativen Datenarrays anwenden!

  1. # kommaseparierte Listen zu Integerlisten umwandeln (über intval() )
  2. $GLOBALS['TYPO3_DB']->cleanIntList($list)

Parameter

  • string: kommaseparierte Liste mit Werten, die Integer sein sollen

  1. # Arraywerte zu Integer umwandeln (über intval() )
  2. $GLOBALS['TYPO3_DB']->cleanIntArray($arr)

Parameter

  • array: Array mit Werten, die Integer sein sollen

Datenbankabfragen

  1. # SELECT-Abfrage
  2. $GLOBALS['TYPO3_DB']->exec_SELECTquery(
  3.       $select_fields,
  4.       $from_table,
  5.       $where_clause,
  6.       $groupBy='',
  7.       $orderBy='',
  8.       $limit=''
  9. )

Parameter

  • string: Liste der Felder oder * für alle Felder
  • string: Tabelle(n). Es gelten die üblichen SQL-Regeln für Aliase
  • string: WHERE-Klausel. Wie in SQL üblich. ACHTUNG: man muss alle GET / POST-Werte hier selbst qotieren! Verwenden Sie $this->fullQuoteStr() – siehe oben. Hier nicht Befehle wie GROUP oder LIMIT verwenden.
  • string: Optionale GROUP-Anweisung
  • string: Optionale ORDER BY-Anweisung
  • string: Optionale LIMIT-Anweisung.

  1. # INSERT-Abfrage
  2. $GLOBALS['TYPO3_DB']->exec_INSERTquery    (
  3.       $table,
  4.       $fields_values,
  5.       $no_quote_fields=FALSE
  6. )

Parameter

  • string: Tabelle
  • array: Feldwerte als array mit key=>value Paaren. Die Werte werden intern quotiert. Typischerweise verwenden Sie ein Array “$insertFields” mit ‘fieldname’=>‘value’ und übergeben es als Argument.
  • string/array: Liste oder Array von Schlüsseln, die nicht quotiert werden sollen; siehe ober bei fullQuoteArray()

  1. # UPDATE-Abfrage
  2. $GLOBALS['TYPO3_DB']->exec_UPDATEquery    (
  3.       $table,
  4.       $where,
  5.       $fields_values,
  6.       $no_quote_fields=FALSE
  7. )

Parameter

  • string: Tabelle
  • string: WHERE-Anweisung, typischerweise “uid=xx”. Achtung: Sie müssen alle GET- oder POST-Parameter hier selbst quoten – siehe oben.
  • array: Feldwerte als array mit key=>value Paaren. Die Werte werden intern quotiert. Typischerweise verwenden Sie ein Array “$insertFields” mit ‘fieldname’=>‘value’ und übergeben es als Argument.
  • string/array: Liste oder Array von Schlüsseln, die nicht quotiert werden sollen; siehe ober bei fullQuoteArray()

  1. # DELETE-Abfrage
  2. $GLOBALS['TYPO3_DB']->exec_DELETEquery($table,$where)

Parameter

  • string Tabelle
  • string WHERE-Anweisung, typischerweise “uid=xx”. Achtung: Sie müssen alle GET- oder POST-Parameter hier selbst quoten – siehe oben.

Spezielle Datenbank-Anweisungen

Die folgende Funktion eignet sich hervorragend zum Auswerten einer n-n Datenverbindung, wie sie zum Beispiel bei der Verwendung von Kategorien anfällt. Bedingung ist allerdings, dass die Verbindung mit einer m_m-Tabelle erfolgt und nicht über ein einzelnes Feld mit kommaseparierter ID-Liste.

  1. # SELECT relational
  2. $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query(
  3.       $select,
  4.       $local_table,
  5.       $mm_table,
  6.       $foreign_table,
  7.       $whereClause='',
  8.       $groupBy='',
  9.       $orderBy='',
  10.       $limit=''
  11. )

Parameter

  • string: Liste der Felder oder * für alle Felder
  • string: Name der lokalen Tabelle
  • string: Name der Relationalen m_m-Tabelle
  • string: Name der fremden, verknüpften Tabelle
  • string: WHERE-Klausel. Wie in SQL üblich. ACHTUNG: man muss alle GET / POST-Werte hier selbst qotieren! Verwenden Sie $this->fullQuoteStr() – siehe oben. Hier nicht Befehle wie GROUP oder LIMIT verwenden. Es muss ein ‘ AND ‘ eingefügt werden.
  • string: Optionale GROUP-Anweisung
  • string: Optionale ORDER BY-Anweisung
  • string: Optionale LIMIT-Anweisung.

Achtung: Viele Feldnamen in Typo3 sind identisch (uid, pid, hidden etc.). Das kann bei der Auswertung zu Problemen führen, da die Felder nicht eindeutig sind! Es ist deshalb sinnvoll, imm select- und für die Tabellennamen mit Aliasen zu arbeiten:

  1. $GLOBALS['TYPO3_DB']->exec_SELECT_mm_query(
  2.       'a.uid as a_uid, b.uid as b_uid',
  3.       'tabelle_1 a',
  4.       'tabelle_m_m',
  5.       'tabelle_2 b',
  6.       $whereClause='',
  7.       $groupBy='',
  8.       $orderBy='',
  9.       $limit=''
  10. )

Ein ganz besondere Funktion ist listQuery(): Sie ermöglicht eine einfache Behandlung von relationalen Verknüpfungen, die nicht über eine dritte m_m-Tabelle laufen sondern über Felder mit kommaseparierten Listen.

  1. $GLOBALS['TYPO3_DB']->listQuery  (
  2.       $field,
  3.       $value,
  4.       $table
  5. )

Das Problem derartiger Verknüpfungen ist, dass die Abfrage über LIKE laufen muss, und dabei zusätzlich unterschieden werden muss, auf welcher Seite des gewünschten Wertes das Komma folgt. Kern der Funktion ist diese Zeile:

  1. $where='('.$field.' LIKE \'%,'.$command.',%\' OR '.$field.' LIKE \''.$command.',%\' OR '.$field.' LIKE \'%,'.$command.'\' OR '.$field.'=\''.$command.'\')';

Parameter

  • string: Feldname, der die kommaseparierte Liste enthält
  • string: Wert, der gefunden werden soll
  • string: Tabelle, in der gesucht wird (für die korrekte Behandlung durch DBAL)

In die gleiche Richtung geht die folgende Funktion, die Srings für die LIKE-Abfrage korrekt für DBAL umsetzt:

  1. $GLOBALS['TYPO3_DB']->escapeStrForLike($str,$table)

WICHTIG: Es gibt eine hervorragende Abkürzung, um die wichtigen einschränkenden Felder in der WHERE-Abfrage automatisch einzuschließen: hidden, deleted, von/bis, Frontenduser etc. Die WHERE-Abrage muss einfach damit erweitert werden:

  1. $this->cObj->enableFields('datenbank')

Also zum Beispiel in der exec_SelectQuery:

  1. # SELECT-Abfrage
  2. $GLOBALS['TYPO3_DB']->exec_SELECTquery(
  3.       $select_fields,
  4.       $from_table,
  5.       $where_clause.$this->cObj->enableFields('datenbank'),
  6.       $groupBy='',
  7.       $orderBy='',
  8.       $limit=''
  9. )

Noch drei weitere Funktionen werden öfter gebraucht:

  1. # Liefert eine Integer zurück mit der Anzahl der betroffenen Datenzeilen, z. B. bei INSERT oder UPDATE
  2. $GLOBALS['TYPO3_DB']->sql_affected_rows( )

  1. # Liefert eine Integer, die uid des zuletzt eingefügten Datensatzes (über INSERT)
  2. $GLOBALS['TYPO3_DB']->sql_insert_id  ( )

  1. # Liefert eine Integer, mit der Anzahl der gefundenen Datensätze
  2. $GLOBALS['TYPO3_DB']->t3lib_DB.sql_num_rows($res)

Parameter

  • pointer: Der Resultpointer einer vorher erfolgten SELECT-Abfrage.

Weiterführende Links

 

Achtung: Keine Gewähr: bei mir hat das folgende geklappt, aber es hängt stark von der Konfiguration von PhpMyAdmin ab! Das muss nämlich bereits korrekt auf utf-8 konfiguriert sein (was allerdings bei neuen Verisionen der Fall sein sollte). Siehe unten die beiden Links für eine Version, die sicherer funktioniert, aber leider Shell-Zugriff benötigt.

Ziel ist die Umstellung einer vorhandenen Installation in ISO-8859-1 nach utf-8. Dazu sind eine Reihe von Schritten nötig:

  • mit PhpMyAdmin die Datenbank exportieren. PhpMyAdmin erstellt dabei standardmäßig eine Datei in utf-8; heisst, die Daten sind bereits konvertiert.
  • neue DB anlegen und dabei darauf achten, dass die neue DB eine Collation von utf8_general_ci bekommt. Notfalls nachträglich in PhpMyAdmin in der Karteikarte »Operationen« umstellen.
  • Die exportiere SQL-Datei in einem Editor öffnen und dort die Einträge “DEFAULT CHARSET=latin1” entfernen.Achtung: bei mir gab es ein Problem mit der Tabelle »tx_realurl_pathcache« – beim Import erhielt ich eine Fehlermeldung, dass der Schlüssel länger wäre als 1000 Zeichen, was nicht erlaubt ist. Das liegt an der Umstellung auf utf-8: durch die benötigten 2 – 3 Byte pro Zeichen wird der Schlüssel zu lang. Ich habe deshalb bei dieser Tabelle “DEFAULT CHARSET=latin1” gelassen.
  • Danach die Datei mit PhpMyAdmin importieren. Wenn alles richtig eingestellt ist, werden die Daten jetzt als utf-8 eingelesen. Das kann kontrolliert werden mit einer kleinen PHP-Datei:

  1. $db_link=mysql_connect(db_url,db_user,db_passwort);
  2. $names=mysql_query('set names utf8');
  3. $db_sel=mysql_select_db(db_name) or die("Auswahl der Datenbank fehlgeschlagen");
  4. $sql="SELECT * FROM pages";
  5. echo'<table border="1">';
  6. while ($zeile=mysql_fetch_array($db_erg,MYSQL_ASSOC))
  7. {
  8.   echo"<tr>";
  9.   echo"<td>".$zeile['uid']."</td>";
  10.   echo"<td>".$zeile['title']."</td>";
  11.   echo"</tr>";
  12. }
  13. echo"</table>";

Den Output überprüfen, indem die Kodierung im Browser entsprechend umgestellt wird (oder denOutput mit einem HTML-Header erweitern und ein charset mitgeben). Entscheidend ist diese Zeile:

  1. $names=mysql_query('set names utf8');

sie stellt nämlich die Collation der Verbindung auf utf-8 um, die sonst standardmäßig auf latin stehen würde. Jetzt müssten alle Seitennamen korrekt in utf-8 im Browser erscheinen und PhpMyAdmin müsste sie ebenfalls korrekt anzeigen. Ohne Umstellen der Verbindung würde PhpMyAdmin zwar korrekt utf-8 in die Datenbank schreiben, wir würden aber mit PHP immer noch latin auslesen.

Der Witz ist, dass jetzt wirklich diese Testdatei und PhpMyAdmin die korrekten Zeichen anzeigen müssen!

Allerdings klappt es in TYPO3 noch nicht. Nächster Schritt:

  • Im Installtool muss bei [forceCharset] “utf-8” und bei [setDBInit] “set names utf8” eingetragen werden. Der erste Eintrag sorgt dafür, dass das Backend und die Frontendausgabe immer auf utf-8 eingestellt sind und die zweite Angabe stellt die Kollation der Datenbankverbindung auf utf-8. Insbesondere der zweite Eintrag ist nötig, sonst erhält TYPO3 durch die falsche Standardkollation der Verbindung (latin) doch keine utf-8-Daten.

Weiterführende Links

  • UTF-8-Umstellung (Großen Dank an Oliver, der Tipp mit [setDBInit] war der entscheidende Hinweis nach einigen Stunden Suche).
  • MySQL: Zeichensatz-Grundlagen – Der ausführlichste Artikel zum Problem der Zeichensätze in MySQL. Köhntopp rät allerdings von einer Umstellung der gesamten Datenbank auf UTF-8 ab und begründet das auch überzeugend mit Speicher- und Performance-Vorteilen. Allerdings erfordert sein Ansatz sehr genaues Arbeiten – Definition von CHARSET und COLLATION auf Spaltenebene.

 

 

Kategorien: API/Sonstiges  Kommentare 0
Tags: mysql, php, datenbank