Archiv der Kategorie: Schlagwörter (Joomla)

Joomla Modul mit SQL-Formfield

Da ich keinen vorgefertigten Formularfeldtyp gefunden habe, der mir die Tags (Schlagwörter) ausgibt, hab ichs mal über den Typ SQL form field type probiert, mal sehen wie weit wir damit kommen. Aber erst noch mal ein paar Gedanken zu den Anforderungen:

Ich schraubs mal ein bisschen runter

Im vorigen Artikel hatte ich ja für den Anwender die Möglichkeit geschaffen, eine bestimmte Beitragskategorie auszuwählen. Das hat ganz hübsch funktioniert, aber eigentlich ist es für meinen Zweck nicht ganz das Richtige. Ich brauche ja auch noch mein zweites Auswahlkriterium, nämlich die Tags=Schlagwörter. Kurze Erinnerung, was ausgegeben werden soll:

X Rezepte insgesamt

davon Y mit dem Tag Z

Ich habe aber in meinem Joomla-Kochbuch die Einschränkung gemacht, dass Tags nur für die Kategeorie Rezepte verwendet werden, in den anderen Kategorien (Kochbücher etc.) hab ich sie schlicht und ergreifend nicht eingesetzt. Das heisst aber, unsere Tagauswahl macht nur Sinn, wenn die Beitragskategorie 8 für Rezept ist, und dann kann ich es auch gleich fest verdrahten. Sonst dürfte ich die Tag-Auswahl nur anzeigen, wenn der Anwender die Kategorie Rezepte gewählt hat, und das geht mir dann doch ein bisschen zu weit.

Der Formularfeldtyp SQL

Ist wahrscheinlich am leichtesten zu verstehen, wenn man es am konkreten Beispiel sieht. Die Felddefinition in meiner XML-Datei sieht so aus:

<field
            name="title"
            type="sql"
            default="10"
            label="Einen Tag auswählen"
            query="SELECT id AS value, title FROM #__tags where title not like 'ROOT'"
            />

Dabei ist der Alias „AS value“ wichtig, da über diesen der Rückgabewert des Feldes definiert wird. Den Select hab ich gleich ein bisschen angepasst, wir gehen in die tabelle #_tags und schliessen hier den Systemeintrag ROOT aus, weil der in der Auswahlliste gar nicht auftauchen darf.

Zum Aufbau der Query zitiere ich mal die Joomla-Doku:

  • query (mandatory if not using the sql_* attributes) is the SQL query which will provide the data for the drop-down list. The query must return two columns; one called ‚value‘ (unless overridden by the key_field attribute) which will hold the values of the list items; the other called the same as the value of the name attribute (unless overridden by the value_field attribute) containing the text to be shown in the drop-down list.

Also, mal ganz langsam.

  • die query liefert die Daten für unser Dropdown-Feld
  • die query muss zwei Spalten zurückgeben
  • die erste Spalte der Rückgabe muss value heissen, das passiert hier mit dem Alias, und liefert die Rückgabewerte für das Formularfeld (in unserem Fall die numerische ID des Tags)
  • die zweite Spalte muss genauso heissen wie das Feld in der XML-Datei, ich bin hier bei title geblieben. Die zweite Spalte liefert die Einträge für das Dropdown-Feld.

Alles klar? Jedenfalls funktionierts, und mein Dropdownfeld sieht schon mal ganz gut aus:

dropdown_tags

dropdown_tags

Den Wert, den der Benutzer ausgewählt hat, holen wir uns in der mod_helloworld.php wie gehabt über den Namen des Feldes:

$titel = $params->get('title');

Wir erweitern unser Standardobjekt für die Parameterübergabe an die Helper-Klasse entsprechend:

$data = new stdClass();
//Objekt füllen
$data->kat = $kat;
$data->variable = $variable;
$data->titel = $titel;

Und können jetzt in der helper.php damit weiterarbeiten, da haben wir jetzt die numerische ID des gewählten Tags auf der Variablen $params->titel.

Jetzt müssen wir nur noch zählen, wieviele Rezepte dem gewählten Tag zugeordnet sind, und dafür brauchen wir die Tabelle #__content_item_tag_map, da schauen wir einfach nach, wie oft die aktuelle Tag-ID auftaucht.

//Anzahl der Tags zur TagID holen
        $db = JFactory::getDbo();
        $db->setQuery("SELECT * FROM #__contentitem_tag_map WHERE tag_id = ".$params->titel.""); 
        $db->execute();
        $my_count = $db->getNumRows();
        echo $my_count;

Schlußendlich fehlt uns noch der Name des Tags für die Ausgabe, den holen wir uns mit der ID aus der Tabelle #__tags:

//Name des Tags zur Tag-ID holen
        $db = JFactory::getDBO();
        $db->setQuery("SELECT title FROM #__tags WHERE id=".$params->titel);
        $db->execute();
        $tagName = $db->loadResult();

Das wars, fehlt nur noch die Ausgabe:

echo $my_count." davon zum Thema ".$tagName;

 Bingo, das hat hingehauen! Meine Ausgabe sieht so aus:

tag_kat_ausgabe

tag_kat_ausgabe

Passt einwandfrei. Man könnte es jetzt noch perfektionieren und einen Join auf die #__content über die Kategorie-ID einbauen, damit man nur die Tags angezeigt bekommt, die auch zu Rezepten zugeordnet sind, aber ich wills mal nicht übertreiben. Die Vorgabe war ja, dass Tags nur in der Kategorie Rezepte verwendet werden, das muss reichen.

Fazit

Ich finde, die Formularfelddefinition ist über die XML-Datei übersichtlich und komfortabel gelöst, und die vordefinierten Formularfeldtypen nehmen einem in vielen Fällen einen Haufen Arbeit ab. Das macht es wieder wett, dass die Modulerstellung am Anfang ein bisschen schwer zu durchblicken ist, aber wenn man einmal ein Basic Modul geschrieben hat, geht auch das leicht von der Hand. Modulbasteln in Joomla macht Spaß!

Nachtrag: mehr als 2 Kategorien in der Export-Datei

Ich hab ja ganz lässig behauptet, wenn man mehr als zwei Kategorien pro Rezept  in die Export-Datei schreiben will, braucht man in Access eine Kreuztabelle. Jetzt bin ich gefragt worden, wie man das anstellt, und ich muss zugeben die ist sehr, sehr haarig zu konfigurieren. Ich hab da mal den „kleinen Dienstweg“ über ein Excel-Makro genommen, funktioniert auch und ist wesentlich leichter nachzuvollziehen.

Die Ausgangsbasis

Wir schubsen unsere Ausgangstabelle in Excel rein, die dürfte ja inzwischen bekannt sein. Jeder Datensatz taucht so oft auf, wie er Kategorien hat, und wir haben die Joomla-ID dazugemappt:

excel_startdaten

excel_startdaten

Post_id, post_title, term_id und name kommen aus WordPress, das was hier joomla_map.id heißt ist die Joomla-Tag-ID aus unserer mapping-Tabelle.

So soll es dann aussehen

Das Arbeitsblatt mit den Ausgangsdaten habe ich „start“ genannt, das Output-Arbeitsblatt heisst „ziel“ und sieht in leerem Zustand so aus:

excel_ziel_leer

excel_ziel_leer

Wir übernehmen nur die id und den titel, und verteilen die Tags auf die entsprechenden Felder tag1…tagn. Der Clou dabei ist, dass man unbegrenzt viele Tags pro Datensatz übernehmen und einsortieren kann. Den Content hab ich mir hier gespart, kann man sich später über die id wieder dazumappen.

So wirds gemacht

Der VBA-Code dazu sieht so aus:

Sheets("start").Select
        
    i = 2
    j = 2
    k = 0
    akt_id = Cells(2, 1).Value
        
    While akt_id <> ""
        
                Sheets("start").Select
                akt_id = Cells(i, 1).Value
                akt_titel = Cells(i, 2).Value
                akt_kat = Cells(i, 4).Value
                akt_tag = Cells(i, 5).Value
                    
                    
                Sheets("ziel").Select
                Cells(j, 1).Value = akt_id
                Cells(j, 2).Value = akt_titel
                Cells(j, 3 + k).Value = akt_tag
                
                
                i = i + 1
                If Worksheets("start").Cells(i, 1) <> akt_id Then
                    j = j + 1
                    k = 0
                Else
                k = k + 1
                End If
        Wend

Was hab ich gemacht? Ich steppe mit dem i durch die start-Tabelle und schreibe so lange in die selbe Zeile j der ziel-Tabelle, wie ich in start auf die selbe id treffe. Das k zählt die Spalte für das tag1..n hoch. Treffe ich auf eine neue id in der Zieltabelle, wird j um 1 erhöht und k wieder auf 0 gesetzt, und das ganze so lange bis ich auf das erste leere Feld id in der start-Tabelle treffe.

Das Ergebnis sieht dann so aus:

excel_zieldaten

excel_zieldaten

In der Praxis wird man sicher die Anzahl der zu importierenden tags auf ein handliches Mass  beschränken, das muss sich jeder selber überlegen, was da bei seinen Bestandsdaten Sinn macht. Aber so bekommt man alle Kategorien aus WordPress auf Joomla-Tags gemappt, mit dieser Export-Tabelle kann man weiterarbeiten.

Rezepte mit 2 Tags importieren, so gehts

Ich hab mir mal eine kleine Export-Tabelle mit 11 Datensätzen gebaut, die sieht so aus:

11_export

11_export

id, titel und content kommen wie gehabt aus WordPress, kat_1 und kat_2 sind die passenden Schlagwort-IDs aus Joomla, die wir im letzten Beitrag via Access zugeordnet haben. Jetzt wird noch das Import-Skript umgebaut, da kommt die Logik für die Tag-Zuordnung innerhalb der Foreach-Schleife nach dem Erzeugen des neuen Artikelobjekts mit rein. Das Ganze sieht dann so aus:

//**************Tags zuordnen
           $roh1 = $zeile->kat_1;
           $roh2 = $zeile->kat_2; 
           
            $tag1 =strval($roh1);
            $tag2 =strval($roh2);
            $neue_id= $article->id;
            
            // Das ist einen Versuch wert!
                $basePath = JPATH_ADMINISTRATOR.'/components/com_content';
                require_once $basePath.'/models/article.php';
                $articlemodel = new ContentModelArticle(array('table_path' => $basePath . '/tables'));

                $params = array(
                    'id' => $neue_id,                // Article being tagged 
                    'tags' => array($tag1, $tag2)   // Tag IDs from #__tags to tag article with
                );
                
                if($articlemodel->save($params)){
                    echo 'Success!';
                }
                    
                    
                    
                    
                    
                    //**************Ende Tags zuordnen

Der Import der Rezepte bleibt genau wie gehabt, nur findet jetzt noch die Tag-Zuordnung statt. Damit erhalten wir nach dem Import eine wohlgefüllte Tagliste:

tagliste_screenshot

tagliste_screenshot

Was, wenn ich mehr als 2 Kategorien übernehmen will?

Dann muss das Tagmapping in Access aufgebohrt werden, wo ich jetzt mit erster Wert/letzter Wert operiert habe, muss eine Kreuztabelle rein, damit sollten auch beliebig viele Kategorien pro Rezept abgebildet werden können. Die Export-Tabelle wird dann halt ziemlich breit, und das Script braucht eine Logik, mit der alle Tag-Felder (kat_1.. kat_n) berücksichtigt werden. Aber das führt mir jetzt entschieden zu weit, da darf jeder selber experimentieren. Ich mach hier einen Break, und eine Denkpause für den nächsten Beitrag.

Tagmapping: da brauchts ein bisschen Vorarbeit

Die grundlegende Mechanik zum programmatischen Einfügen von Tags

Dank eines guten Tipps aus dem deutschen Joomla-Forum habe ich folgendes Codesnippet zum Laufen gekriegt. Es ordnet dem Artikel mit der ID 80 zwei Tags mit den IDs 7 und 9 zu. Der Knackpunkt war, dass man die IDs der zuzuordnenden Tags als Strings übergeben muss – da musste auch erstmal draufkommen!

    $roh1 = 7;
    $roh2 = 13;
    $tag1 =strval($roh1);
    $tag2 =strval($roh2);
    
        $basePath = JPATH_ADMINISTRATOR.'/components/com_content';
        require_once $basePath.'/models/article.php';
        $articlemodel = new ContentModelArticle(array('table_path' => $basePath . '/tables'));

        $params = array(
            'id' => 80,                // Article being tagged 
            'tags' => array($tag1, $tag2)   // Tag IDs from #__tags to tag article with
        );
        
        if($articlemodel->save($params)){
            echo 'Success!';
        }

Jedenfalls, das ist schonmal eine gute Ausgangsbasis. Soweit ich das bislang beim Testen gesehen habe, werden die Tags immer neu zugeordnet, evtl. vorher zum Artikel schon angelegte Tags werden rausgeschmissen. Macht aber nix, wir arbeiten ja auf einer niegelnagelneuen Joomla-Installation, da sind jetzt erstmal noch keine Tags zugeordnet. Bevor wir diese Mechanik aber in unseren Artikelimport aus WordPress einbauen können, ist noch etwas Freiturnen auf der Datenbank angesagt. Ich geh da mal wieder den Weg über Access, weil das doch etwas handlicher ist als MySQL.

Die Ausgangsbasis

Ich hab nochmal eine Einschränkung beschlossen, zu jedem Beitrag aus WordPress werden maximal zwei Kategorien exportiert, sonst ist mir das jetzt zu aufwendig.

Erste Voraussetzung ist schonmal, dass wir alle Kategorien aus WordPress abgefasst und erfolgreich als Schlagwörter nach Joomla importiert haben. Davon ziehe ich mir die #__tags-Tabelle nach Access ab und bastle mir eine Abfrage auf meine Tabelle mit den WordPress-Kategorien, der Join geht hier über den Namen der Kategorie in WordPress auf den Titel des Tags in Joomla. Das funktioniert, weil die Schreibweisen identisch sind. Das Tagmapping sieht dann so aus:

tagmap_screenshot

tagmap_screenshot

Die term_id und der name kommen aus WordPress, title und id aus Joomla, das ist die Basis für unser Mapping.

Zuordnung der Kategorien zu den WordPress-Posts

Man braucht vier Tabellen, um die Zuordnung der Kategorien zu den einzelnen Beiträgen in WordPress abzubilden, das sieht so aus:

wordpress_kat_post

wordpress_kat_post

Meine Tabelle published_posts ist ein schlanker Auszug aus der wp_posts und enthält nur ID, Titel und Content der veröffentlichten Rezepte. So kriege ich erstmal eine Liste aller Rezepte mit den zugeordneten Kategorien, wobei jedes Rezept so oft auftaucht wie es Kategorien hat:

alleposts_allekat

alleposts_allekat

Um das ganze jetzt auf zwei Kategorien pro Rezept einzuschränken, setze ich hierauf noch einmal eine Abfrage, in der jeweils nur der erste und der letzte Wert der Kategorien zu einem Rezept angezeigt wird :

ersterwert_letzterwert

ersterwert_letzterwert

Den PostContent hab ich mal weggelassen, sonst wirds so unübersichtlich. Jetzt kann man noch den ersten und letzten Wert der term_id einblenden:

ersterletzterwert_termid

ersterletzterwert_termid

Hierzu Joine ich mir dann noch meine Mapping-Tabelle von oben und ziehe mir daraus die Joomla-Tag-IDs:

tagmapping_ausfuehrlich

tagmapping_ausfuehrlich

Das sieht soweit ganz gut aus, jetzt können wir die überflüssigen Felder ausblenden, so dass nur der post_title, der post_content und die beiden Joomla-Tag-IDs übrigbleiben. So, das wars. Damit erstelle ich mir meine neue Import-Tabelle. Ach so, den post_content hab ich unterwegs verloren, den holen wir uns jetzt wieder dazu, aber dann ist jetzt wirklich Finish.

mapping_endprodukt

mapping_endprodukt

Mit Access macht sowas richtig Spaß! Für die Import-Mechanik nach Joomla gibts jetzt aber einen neuen Beitrag, der hier ist lang genug.

Schlagworte/Tags nach Joomla importieren: ohne Gewährleistung

Mit diesem Codebeispiel von der Stackexchange ging es jetzt doch relativ flott, ich habe mir eine Mechanik gebastelt, mit der ich tatsächlich Schlagwörter nach Joomla importieren kann. Ich zeig hier erst mal nur den funktionalen Code für ein Schlagwort, die Schleife über eine Liste (im Zweifelsfall eine Import-Tabelle) kann sich jeder selber dazu denken. Die ganze Sache sieht so aus:

$data = "Joschis Cocktailbar";
 $date = new JDate();
 include_once JPATH_BASE . '/administrator/components/com_tags/tables/tag.php';
 $table = new TagsTableTag(JFactory::getDbo());

 $table->title = $data;
 $table->note = '';
 $table->description = '';
 $table->published = 1;
 $table->checked_out = 0;
 $table->checked_out_time = '0000-00-00 00:00:00';
 $table->created_user_id = 839;
 $table->created_time = $date->toSql();
 $table->modified_user_id = 0;
 $table->modified_time = '0000-00-00 00:00:00';
 $table->hits = 0;
 $table->language = '*';
 //$table->parent_id = 1; (hat keine Auswirkung)
 //$table->level = 1; (hat keine Auswirkung)
 $table->path = JFilterOutput::stringURLSafe($data);
 
 $table->check();

 $table->store();

Warum sich die parent_id und der level nicht setzen lassen ist mit ehrlich gesagt völlig schleierhaft, aber es scheint weiter keine Auswirkungen zu haben, der Tag „Joschis Cocktailbar“ wird mit diesem Code erzeugt und scheint auch regulär zu funktionieren. Jetzt noch die Daten aus einer Importtabelle laden und mit einem Foreach durchsteppen, das ist der nächste Schritt, das mach ich später noch.

Was, wenn ein Schlagwort schon vorhanden ist?

Soweit ich das nachvollziehen konnte, wird es dann nicht neu angelegt, und auch nicht überschrieben, sondern der Datensatz wird dann nicht neu erzeugt.

Und woher nehm ich die Kategorienliste aus WordPress?

Das habe ich in diesem Beitrag zu den WordPress-Kategorien ausführlich beschrieben, das sollte jeder nachvollziehen können. Wir nehmen da einen kleinen Umweg über mein Leib-und Magendatenbankerl Access, das ist etwas handlicher als MySQL.

Warum ohne Gewährleistung?

Weil die programmatisch neu erzeugten Schlagwort-Datensätze nicht ganz logisch identisch mit den manuell angelegten sind, so bleiben z.B. die parent_id und der level beide = 0. Was das im Ernstfall für Auswirkungen hat kann ich nicht sagen, dafür ist mir die Logik der #__tags-Tabelle zu undurchsichtig. Die schliesst nämlich die Möglichkeit zum Schachteln von Tags in einer Baumstruktur ein, das hab ich bisher unterschlagen dass das in Joomla geht. Ich verwende es aber auch nicht, deswegen sei mir die Auslassung erlaubt. Wer sich dran versuchen möchte, geschachtelte WordPress-Kategorien nach geschachtelten Joomla-Tags abzubilden, bittesehr, aber ich mach hier mal einen Punkt. Die Schlagwörter sollten sich so importieren lassen, und das ohne Handarbeit des Praktikanten. Ist doch mal ein ganz guter Anfang. Jetzt müssten wir sie nur noch unseren zu importierenden Rezepte zuordnen können, aber dazu gibts eine Denkpause und einen neuen Beitrag.

Kleiner Nachtrag

Ich hab den Import jetzt mal durchgezogen, hat klaglos funktioniert, alle 28 Datensätze sind einwandfrei in Joomla als neue Tags importiert worden. Jetzt gehts dann aber ans Eingemachte, das mit dem Mapping wird ne harte Nuss!

WordPress-Kategorien auf Joomla-Tags abbilden? Vorüberlegungen

Etwas vereinfachte Voraussetzungen

OK, meine Ausgangsbasis ist relativ simpel, ich habe (bis auf eine Ausnahme) im Original-Inselfisch-Kochbuch keine geschachtelten Kategorien verwendet, das spielt sich alles auf einer Ebene ab. Deswegen kann ich es mir erlauben, die Kategorien in Joomla als Schlagwörter (Tags) abzubilden. Ich habe mir auch die Einschränkung erlaubt, dass ich Schlagwörter in Joomla nur für die Rezepte verwende, für meine einzige andere „echte“ Joomla-Kategorie, die Kochbücher, werden keine Tags verwendet, und auch sonst nirgendwo. Mit dieser Mechanik kann ich bisher manuell meine Schlagwörter der Reihe nach erfassen und jedem Rezept zuordnen. Jetzt wärs natürlich schick, wenn man beim Datenimport irgendwie die Kategorien aus WordPress mitgeben und diese als Schlagwörter in Joomla automatisch zuordnen könnte. Aber ich fürchte, das wird ein langer Weg… na ja, auch der fängt mit dem ersten Schritt an.

Kann man Schlagwörter überhaupt importieren?

Geht bestimmt, die Frage ist nur: wie aufwendig? Und: wann macht man das? Ich denke, wir sollten hier von einer jungfräulichen Joomla-Installation ausgehen, in die noch keine Rezeptdaten (ich bleibe beim Inselfisch-Kochbuch) importiert worden sind, und in der auch noch keine Schlagwörter per Hand angelegt worden sind. Dann könnte man es mal versuchen, ich hab auf Stack Exchange einen Artikel gefunden: Adding tags to Joomla programmatically. Der sollte eigentlich genau das machen, was wir brauchen, aber ehrlich gesagt, das muss ich mir nochmal in aller Ruhe anschauen.

Und dann hätte ich noch Plan B: es sind nur ca. 30 Kategorien. Die können wir schlimmstenfalls auch zu Fuß einhacken, das macht der Praktikant. Aber jetzt gesetzt mal den Fall, wir schaffen es die Tags zu importieren, was dann?

Zuordnung der Joomla-Tags zu den WordPress-Kategorien

Da wird in irgendeiner Form ein Mapping stattfinden müssen. Das könnte ein bisserl kompliziert werden, weil ich in WordPress theoretisch beliebig viele Kategorien zu einem Beitrag zuordnen kann. Ich mach hier mal einen rigorosen Cut und sage: drei Kategorien pro Beitrag, das muss reichen.

Zuordnung der gemappten Schlagwörter zu den importierten Beiträgen

Sagen wir mal, ich schaffe es die WordPress-Kategorien den Joomla-Tag-ids zuzuordnen und beim Import mitzugeben. Weitergehen könnte es dann so in der Art wie in diesem Codeschnipsel:

$basePath = JPATH_ADMINISTRATOR.'/components/com_content';
require_once $basePath.'/models/article.php';
$articlemodel = new ContentModelArticle(array('table_path' => $basePath . '/tables'));

$params = array(
    'id' => 123,                // Article being tagged
    'tags' => array(7,8,9,14)   // Tag IDs from #__tags to tag article with
);
if($articlemodel->save($params)){
    echo 'Success!';
}

Wie gesagt, das Mapping auf die jetzt in Joomla schon vorhandenen Schlagwörter muss irgendwie hinhauen. Das ist jetzt erstmal ein Haufen Holz, ich geh mir jetzt wieder mal mein Denkmützerl aufsetzen und schaue, wie weit ich komme. Wenn ich was rausgefunden habe, gibts einen neuen Beitrag.