Doctrine bietet ja von Haus aus einige sehr interessante Erweiterungen die über Behaviors eingebunden werden können. So auch das “Searchable” Behavior, das einen guten Job macht und – wenn man sich an die Searchable Dokumentation hält – auch super funktioniert.
Zumindest so lange bis man sich dazu entschließt, auch abhängige Daten aus Relationen zum Suchindex eines Records hinzuzufügen. So möchte ich gerne, dass bei der Suche nach einem Tag, auch der Record der mit diesem Tag versehen ist, gefunden wird. Wie das ganze zu bewerkstelligen ist, zeige ich in den nächsten Absätzen.
Tag-Relation
Da ich noch Doctrine 1.1 verwende, und das Taggable-Behavior bei mir nicht wirklich gut funktiniert hat, habe ich mir einfach selbst eine kleine Tag-Struktur gebastelt, die aus einer Many-to-Many-Relation besteht:
Tag:
columns:
id:
primary: true
autoincrement: true
type: integer(10)
name:
type: string(255)
unique: true
ArticleTag:
columns:
article_id:
type: integer
primary: true
tag_id:
type: integer
primary: true
relations:
Article:
local: article_id
foreign: id
Tag:
local: tag_id
foreign: id
Article:
actAs:
Searchable:
fields: [headline, content, Tags]
columns:
id:
primary: true
autoincrement: true
type: integer(10)
headline:
type: string(255)
content:
type: string
relations:
Tags:
class: Tag
local: article_id
foreign: tag_id
refClass: ArticleTagOptimisten würden denken, dass mit der Angabe von “Tags” in der “Searchable”-Relation schon alles erledigt ist. Doch leider ist das erst die halbe Miete. Denn die Tags werden zwar indiziert, allerdings werden sie dabei mit dem String “array” in der Datenbank abgelegt. Verständlich, denn die Relation ist beim indizieren ein Array aus Tags. Aber wie lässt sich das umgehen?
Der Doctrine Search Analyzer
Bei der Erzeugung des Such-Index wird auf einen Search-Analyzer zurückgegriffen. Diese Klasse ist dafür zuständig die einzelnen Worte die in den Index gelangen sollen vorzubereiten. Der Standard-Analyzer entfernt Leer- und Sonderzeichen. Und genau diese Funktion kann uns dabei helfen die Tags korrekt in der Datenbank zu speichern. Dazu erzeugen wir eine neue Klasse im Models-Verzeichnis “ArticleAnalyzer”:
<?php
class ArticleAnalyzer implements Doctrine_Search_Analyzer_Interface
{
public function analyze($text, $encoding = null)
{
if(is_array($text)) {
$tag_string = '';
foreach($text as $tag) {
if(isset($tag['name'])) {
$tag_string .= $tag['name']. ' ';
}
}
$text = $tag_string;
}
$text = strip_tags($text);
$analyzer = new Doctrine_Search_Analyzer_Standard();
return $analyzer->analyze($text, $encoding);
}
}
?>
Die Klasse behandelt Arrays als Liste von Tags und leitet diese einzelnen Begriffe dann korrekt an den Standard-Analyzer weiter. Nun müssen wir dem Such-Plugin nur noch unseren eigenen Analyzer zuweisen, damit dieser auch seine Arbeit verrichten kann.
Einen eigenen Search Analyzer zuweisen
Die Zuweisung erfolgt ganz einfach über die Model-Klasse, in diesem Fall “Article”:
public function preSave(Doctrine_Event $event)
{
$search = Doctrine::getTable('Article')->getTemplate('Doctrine_Template_Searchable')->getPlugin();
$search->setOption('analyzer', new ArticleAnalyzer());
}
Ab sofort wird beim Speichern des Entities unser eigener “ArticleAnalyzer” verwendet, der auch die gewünschten Tags in der Datenbank ablegt.
Bonus: Tags bei jedem Speichern indizieren
Es existiert noch ein weiteres kleines Problem: Wenn beim Speichern des Article-Objekts lediglich Tags verändert wurden, so werden diese Änderungen nicht in den Index übernommen, da Doctrine merkt, dass nur das Tag-Objekt, und nicht das Article-Objekt von Änderungen betroffen ist und somit auch die Eventlistener nicht greifen.
Deshalb forcieren wir das Update des Suchindexes bei jedem save()-Aufruf:
public function postSave(Doctrine_Event $event)
{
$article = $event->getInvoker();
$search = Doctrine::getTable('Article')->getTemplate('Doctrine_Template_Searchable')->getPlugin();
$search->updateIndex($article->toArray());
}
Das wars auch schon
Mit diesen kleinen Kniffen werden also auch unsere Tags indiziert. Natürlich lässt sich diese Methode auch auf andere Relationen übertragen.
Ähnliche Artikel: