Querverlinkung

You have a suggestion for a future version of phpMyFAQ? Then post it here!

Moderator: Thorsten

Post Reply
Mini
Posts: 63
Joined: Mon May 08, 2006 11:11 pm

Querverlinkung

Post by Mini »

Hallo miteinander,

nachdem ich eine Weile gebastelt habe und vor 30 Minuten in einem anderen Forum durch Zufall eine Lösung für meine bisherigen RegEx-Probleme gefunden habe, möchte ich nun meine dynamische Lösung für eine Querverlinkung der Artikel zueinander ("Relation") veröffentlichen, ggf. ist sie für andere User ebenfalls von Nutzen.

Bitte beachtet, dass ich ausschließlich eine deutsche FAQ-DB pflege, d.h. ich habe auf die Mehrsprachigkeit nicht geachtet!!! Da ich aber fast alles Funktionsorientiert umgesetzt habe, sollte das eigentlich kein großes Problem darstellen. Weiterhin habe ich kein Mod-Rewrite aktiviert, d.h. auch darauf habe ich keine Rücksicht genommen.

Zunächst wird eine zusätzliche Tabelle benötigt...

Code: Select all

CREATE TABLE `phpmyfaq_verbis_faqkeywords` (  
                               `id` int(11) NOT NULL auto_increment,       
                               `keyword` text,                             
                               PRIMARY KEY  (`id`)                         
                             ) ENGINE=InnoDB
Die functions.php habe ich um folgende Funktionen erweitert...

Code: Select all

/**
 * Gibt String als Keyword ohne Sonderzeichen und ausgeschlossene Phrasen/Worte zurück
 *
 * @param    string
 * @param    string
 * @param    unknown_type $removecursewords
 * @return   string
 * @access   public
 * @author   Mini
 */
function getClearString($str,$allowed_as_regexp = "- ",$remove_curse_words = false)
{
    // Sonderzeichen sind unerwünscht, also weg damit!
    $clearstring = preg_replace("~[^\w".$allowed_as_regexp."]~", " ", $str." ");
	// Aufeinanderfolgende Leerzeichen löschen
    $clearstring = preg_replace("/ {2,}/"," ",$clearstring);
	// Mehrzahlwörter auf Einzahl trimmen, da im Text sowohl Einzahl als auch Mehrzahl verwendet werden kann
    $clearstring = str_replace("ers ", "er ", $clearstring);
	$clearstring = str_replace("ungen ", "ung ", $clearstring);
	$clearstring = str_replace("daten ", "datenn ", $clearstring);
	$clearstring = str_replace("ten ", "t ", $clearstring);
	$clearstring = str_replace("datenn ", "daten ", $clearstring);
	$clearstring = str_replace("ern ", "er ", $clearstring);
	$clearstring = str_replace("llen ", "lle ", $clearstring);
	$clearstring = str_replace("len ", "l ", $clearstring);
	$clearstring = str_replace("lfen ", "lfe ", $clearstring);
	$clearstring = str_replace("nden ", "nd ", $clearstring);
	// Unerwünschte Wörter löschen?
	if ($remove_curse_words == true) {
		$clearstring = RemoveCurseWords($clearstring);
	}
	return trim($clearstring);
}

/**
 * Löscht unerwünschte Wörter
 *
 * @param 	 string
 * @return   string
 * @access   public
 * @author   Mini
 *  */
function RemoveCurseWords($original) {

	$patterns = array(" in ", " an ", " im ", " mit "," von "," und "," bis "," aus "," nach "," oder "," ohne "," bei "," einer "," eine "," nicht "," vom "," feld "," der ", " die ", " das ", " des ", " alter ", " NT ", " aufrufbar ", " verbindliche ", " regelung ", " kunde ", " kunden " );

	$finalremove=$original;
	$piece_front="";
	$piece_back="";
	$piece_replace=" ";

	for ($x=0; $x < count($patterns); $x++) {

		$safety=0;

		while(strstr(strtolower($finalremove),strtolower($patterns[$x]))) {
			$safety=$safety+1;
			if ($safety >= 100000) { break; }

			$occ=strpos(strtolower($finalremove),strtolower($patterns[$x]));
			$piece_front=substr($finalremove,0,$occ);
			$piece_back=substr($finalremove,($occ+strlen($patterns[$x])));
			$finalremove=$piece_front . $piece_replace . $piece_back;
		} 

	} 
	
	return $finalremove;

}

/**
 * Speichert die Schlüsselwörter für die Relation-Lösung
 *
 * @param 	 string
 * @access   public
 * @author   Mini
 *  */
function saveRelationKeywords($relation_keywords) {
    global $db;
	$relation_clean = getClearString($relation_keywords," ",true);
    $relation = explode(" ",$relation_clean);
    foreach ($relation as $relation_word){
    	if (strlen($relation_word) > 2) {
    		$result_relation = $db->query('SELECT keyword FROM '.SQLPREFIX.'faqkeywords WHERE keyword=\''.$relation_word.'\'');
			if ($db->num_rows($result_relation) == 0) {
    			$db->query('INSERT INTO '.SQLPREFIX.'faqkeywords VALUES (\'\',\''.$relation_word.'\')');
			}
    	}
    }
}


/**
 * Verlinkt einen Artikel dynamisch mit der Suche über die übergebenen Schlüsselwörter
 *
 * @param 	 string $strHighlight
 * @param 	 string $strSource
 * @param 	 integer $intCount
 * @return 	 string
 * @access   public
 * @author   Mini
 */
function setRelationLinks($strHighlight, $strSource, $intCount = 0) {
	global $in_content;
    $x = 0;
	preg_match_all('/(<a[^<>]*?>.*?<\/a>)|(<.*?>)/is', $strSource, $arrMatch);
    $strSource = preg_replace('/(<a[^<>]*?>.*?<\/a>)|(<.*?>)/is', '~+*# replaced html #*+~', $strSource);
    $x = $x + preg_match('/('.preg_quote($strHighlight).')/ims', $strSource);
    $strSource = preg_replace('/('.preg_quote($strHighlight).')/ims', '<a href="index.php?action=search&search='.$strHighlight.'" title="Insgesamt '.$intCount.' Artikel zu diesem Schlagwort ('.$strHighlight.') vorhanden. Jetzt danach suchen..." class="relation">$1</a>', $strSource);
    foreach($arrMatch[0] as $html) {
    	$strSource = preg_replace('/'.preg_quote('~+*# replaced html #*+~').'/', $html, $strSource, 1);
    }
    if ($x == 0) {
    	$in_content = false;
    } else {
    	$in_content = true;
    }
    return $strSource;
}
Irgendwie müssen die Keywors ja auch gefüttert werden...das habe ich im Admin-Bereich implementiert...

/admin/record.add.php:

Nach der Passage....

Code: Select all

            // Insert the new category relations
            foreach ($rubrik as $categories) {
                $db->query('INSERT INTO '.SQLPREFIX.'faqcategoryrelations VALUES ('.$categories.', \''.$_REQUEST["language"].'\', '.$nextID.', \''.$_REQUEST["language"].'\')');
            }
...das hier eingefügt...

Code: Select all

      // Relationlösung (Verlinkung der Suche nach Begriffen aus Titel und den Keywords aller Artikel)
	        saveRelationKeywords($_REQUEST["thema"]." ".$_REQUEST["keywords"]);
/admin/record.save.php

Nach der Passage....

Code: Select all

    if ($db->query($query)) {
...das hier eingefügt...

Code: Select all

      // Relationlösung (Verlinkung der Suche nach Begriffen aus Titel und den Keywords aller Artikel)
	        saveRelationKeywords($_REQUEST["thema"]." ".$_REQUEST["keywords"]);
Damit werden aber nur die Keywords in die Tabelle gespeichert. Ich habe die Keywords dann direkt im Artikel mit der Suche verlinkt...

In /template/artikel.tpl habe ich an passender Stelle mit aufgenommen:

Code: Select all

	<div class="relations"><h4>Verwandte Themen:</h4><p>
		{writeArticleRelations}
	</p></div>
In /template/style.css am Ende eingefügt:

Code: Select all

.main-content a.relation{
	font-weight: normal;
	font-family: Arial, Helvetica, sans-serif;
	border: 0;
	color: #325F87;
}
.main-content a.relation:hover{
	font-weight: normal;
	font-family: Arial, Helvetica, sans-serif;
}
div.relations{
	font-size: 80%;
	margin-left: 8px;
	line-height: normal;
	color: #6E6F5F;
}
h4.relations {
	font-size: 115%;
}
span.relations{
	color: #969696;
	font-size: 95%
}
.relations ul li{
	margin-top: 2px;
}
Und in die artikel.php noch die Verlinkung und die Artikelliste und zwar direkt vor dem highlight-Konstrukt:

Code: Select all

// Querverlinkung
$query_relation = sprintf("SELECT * FROM %sfaqkeywords GROUP BY keyword ORDER BY keyword", SQLPREFIX);
$result_relation = $db->query($query_relation);
$num = $db->num_rows($result_relation);

$relations = array();
$i = 0;
$in_content = false;
if ($num > 0) {
    while ($row = $db->fetch_assoc($result_relation)) {
    	$query_num_article = sprintf("SELECT * FROM %sfaqdata WHERE (thema LIKE '%s' OR keywords LIKE '%s' OR content LIKE '%s') AND active = 'yes' GROUP BY thema ORDER BY thema", SQLPREFIX, '%'.$row['keyword'].'%', '%'.$row['keyword'].'%', '%'.$row['keyword'].'%');
    	$result_num_article = $db->query($query_num_article);
    	if ($result_num_article > 0) {
	    	$i = $i + $result_num_article;
	    	$content = setRelationLinks($row['keyword'],$content,$db->num_rows($result_num_article));
	    	if ($in_content == true) {
		    	while ($row_article = $db->fetch_assoc($result_num_article)) {
		    		if ($id <> $row_article['id'] && !in_array($row_article['id'],$relations)) {
		    			$relations[$row_article['id']]['link'] = '<li><a href="'.$_SERVER["PHP_SELF"]."?".$sids."action=artikel&id=".$row_article['id']."&artlang=".$row_article['lang'].'">'.$row_article['thema'].'</a></li>';
		    			$relations[$row_article['id']]['keywords'][] = $row['keyword'];
		    		}
		    	}
	    	}
    	}
    }
}

$writeRelations = '<ul class="relation">';

foreach ($relations as $thema_id) {
	$writeRelations .= $thema_id['link'] . '<br />';
	$writeRelations .= '<span class="relations">('.implode(', ', $thema_id['keywords']).')</span>';
}

if ($i == 0) {
	$writeRelations .= '<li>Keine verwandten Themen gefunden!</li>';
}

$writeRelations .= '</ul>';
Was ich nicht gemacht habe, was man aber tun könnte:
- Mehrsprachigkeit
- Mod-Rewrite-Unterstützung
- Gewichtung der Keywords
- Keyword-Tabelle und über Admin-Bereich administrieren
- Pattern-Liste über den Admin-Bereich administrieren
- Liste der verwandten Themen per GET-Link verbergen/anzeigen
- Zur Performance-Verbesserung Array zusammenstellen und an die saveRelationLinks-Funktion übergeben
- Zur Performance-Verbesserung die Relation Keyword und Artikel zueinander in die DB speichern

Viel Spaß, vielleicht kanns ja der ein oder andere User gebrauchen!

Gruß

Mini
Thorsten
Posts: 15723
Joined: Tue Sep 25, 2001 11:14 am
Location: #phpmyfaq
Contact:

Post by Thorsten »

Hi,

wenn du nichts dagegen hast, würde ich das in die 2.0 mit einbauen.

bye
Thorsten
phpMyFAQ Maintainer and Lead Developer
amazon.de Wishlist
Mini
Posts: 63
Joined: Mon May 08, 2006 11:11 pm

Post by Mini »

wieso sollte ich was dagegen haben?

habs doch hier gepostet, damit alle profitieren können. mit den aufgeschriebenen todo müsste man sich nur gedanken machen, ob man die umsetzt.

ach ja, ein nachteil hat das ganze, wie du dir sicherlich denken kannst...wenn ein kürzeres wort vorhanden ist, wird das kürzere wort verlinkt (z.b. kunde und kundendaten --> kunde wird verlinkt).

ist ne frage des geschmacks. mir ist es so lieber, denn damit findet er auch teilwörter und mehrzahlwortformen. ggf. wäre soundex noch ne alternative!
Thorsten
Posts: 15723
Joined: Tue Sep 25, 2001 11:14 am
Location: #phpmyfaq
Contact:

Post by Thorsten »

Hi,

ich guck mir das einfach mal genau an und bau es in die 2.0 mit ein. Deine Credits sind dir Gewiss. :-)

bye
Thorsten
phpMyFAQ Maintainer and Lead Developer
amazon.de Wishlist
Thorsten
Posts: 15723
Joined: Tue Sep 25, 2001 11:14 am
Location: #phpmyfaq
Contact:

Post by Thorsten »

Hi,

die ersten Zeilen Code von dir sind im CVS eingecheckt. :-)

bye
Thorsten
phpMyFAQ Maintainer and Lead Developer
amazon.de Wishlist
Mini
Posts: 63
Joined: Mon May 08, 2006 11:11 pm

Post by Mini »

Hi,

ist ja Klasse. Habe eben auch den Blog-Eintrag gelesen. Danke dafür.

Als nächstes werde ich meine TinyMCE-Gesamtlösung veröffentlichen, wenns denn hinhaut, wie ich es mir vorstelle. Mal schauen was sonst noch so kommt. ;o)

Gruß

Mini
Post Reply