{"id":1146,"date":"2018-05-02T17:42:20","date_gmt":"2018-05-02T15:42:20","guid":{"rendered":"http:\/\/evileu.de\/zum-schwarzen-pinguin\/?p=1146"},"modified":"2018-05-03T12:05:16","modified_gmt":"2018-05-03T10:05:16","slug":"wordpress-ajax-autocomplete-fuer-minimalisten","status":"publish","type":"post","link":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/2018\/05\/02\/wordpress-ajax-autocomplete-fuer-minimalisten\/","title":{"rendered":"WordPress AJAX Autocomplete f\u00fcr Minimalisten"},"content":{"rendered":"<p>Ich bin ja bekanntermassen ziemlich hartn\u00e4ckig, und obwohl ich die L\u00f6sung f\u00fcr das Autocomplete-Feld von David Nash toll finde, hats mir doch keine Ruhe gelassen. Das muss doch noch ein bisschen einfacher gehen, dachte ich mir und hab den Codex durchforstet. Zum Thema <a href=\"https:\/\/codex.wordpress.org\/AJAX_in_Plugins\">Using Ajax in Plugins<\/a> bin ich f\u00fcndig geworden, da stand doch ein (Codex-un\u00fcblich nachvollziehbares) Beispiel drin, \u00fcber das habe ich mich hergemacht. Das Javascript f\u00fcr den Ajax-Call wird hier nicht als externes Script enqueued, sondern in die Plugin-Datei integriert. Ist aber nicht schlimm, das Script ist n\u00e4mlich sehr \u00fcberschaubar &#8211; aber jetzt mal der Reihe nach.<\/p>\n<h2>Die Vorgabe<\/h2>\n<p>Ich m\u00f6chte gern ein Textfeld, in das ich Suchbegriffe eingeben kann, und das soll mir dann aus der Datenbank alle Post Titles ausgeben, in denen der Suchbegriff vorkommt. Das soll etwa so aussehen:<\/p>\n<div id=\"attachment_1147\" style=\"width: 534px\" class=\"wp-caption alignnone\"><img aria-describedby=\"caption-attachment-1147\" decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-1147\" src=\"http:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-content\/uploads\/2018\/05\/schoko.jpg\" alt=\"schoko\" width=\"524\" height=\"246\" srcset=\"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-content\/uploads\/2018\/05\/schoko.jpg 524w, https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-content\/uploads\/2018\/05\/schoko-300x141.jpg 300w\" sizes=\"(max-width: 524px) 100vw, 524px\" \/><p id=\"caption-attachment-1147\" class=\"wp-caption-text\">schoko<\/p><\/div>\n<h2>Wir basteln uns ein Plugin<\/h2>\n<p>Nat\u00fcrlich brauchen wir dazu wieder ein Formular, und ich werde auch wieder die HTML5-Datalist verwenden. Wir packen das Ganze in ein Plugin, damit wir vom Theme unabh\u00e4ngig sind, und tun Formular und Javascript in einen Shortcode, damit wir das ganze in einem beliebigen Beitrag oder auf einer beliebigen Seite auch ausgeben k\u00f6nnen.<\/p>\n<p>Zuerst mal nur das Plugin ohne JScript und Ajax-Funktion:<\/p>\n<pre>&lt;?php\r\n\/*\r\nPlugin Name: WordPressAjax Testplugin\r\nPlugin URI: http:\/\/localhost\/wp_ajax\/wp-content\/plugins\/wp-ajax\r\nDescription: Zum Testen von Ajax mit WordPress\r\nVersion: 1.0\r\nAuthor: Evi Leu\r\nAuthor URI: http:\/\/www.evileu.de\r\n*\/\r\n\r\n\r\n\/*************************************************************\/\r\n\/\/*********Formular als Shortcode einbinden\r\n\u00a0function asuche(){\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0echo \"&lt;input type = 'text' id = 'textfeld' autocomplete = 'off'\r\n <span style=\"color: #ff0000;\">onkeyup = 'myFunction(this.value);<\/span>' <span style=\"color: #008000;\">list = 'suchliste'<\/span>&gt;\";\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0echo \"&lt;datalist id='<span style=\"color: #008000;\">suchliste<\/span>'&gt;\";\r\n\u00a0\u00a0 \u00a0echo \"&lt;div id = 'liste'&gt;\";\r\n\u00a0\u00a0 \u00a0echo \"&lt;\/div&gt;\";\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0echo \"&lt;\/datalist&gt;\";\r\n\u00a0\u00a0 \u00a0\r\n\u00a0}\r\n\u00a0add_shortcode('asuche', 'asuche');<\/pre>\n<p>Ich hab hier schon mal den Funktionsaufruf bei onkeyup mit drin, der kriegt als Parameter den Inhalt des Textfeldes mit, Script kommt gleich. Die Verbindung vom Textfeld zur Datalist geht \u00fcber die id suchliste. Autocomplete = &#8218;off&#8216; damit der Browser nicht reinpfuscht. In der Datalist hab ich eine weitere Div mit der id liste angelegt, in die schreibt nachher mein Ajax-Call seine R\u00fcckgabewerte.<\/p>\n<h2>Das integrierte Javascript<\/h2>\n<p>Wie gesagt, das Scripterl ist so kurz, dass man es nicht in eine externe Datei auslagern muss, wir nehmen es in die Funktion asuche mit rein, das sieht dann so aus:<\/p>\n<pre>function asuche(){ ?&gt;\r\n\u00a0\u00a0 \u00a0 \r\n\u00a0\u00a0 \u00a0<span style=\"color: #008000;\">&lt;script&gt;<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0function myFunction(wert){<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0<\/span><span style=\"color: #008000;\"> \u00a0\u00a0\u00a0 \u00a0<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0<span style=\"color: #ff0000;\">var aktwert = wert;<\/span><\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0var data = {<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0'action': 'my_action',<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0'whatever': <span style=\"color: #ff0000;\">aktwert<\/span><\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0};<\/span>\r\n\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ wir sind aber nicht auf den Admin-Pages, deswegen der volle Pfad<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0<span style=\"color: #0000ff;\">jQuery.post('http:\/\/localhost\/wp_ajax\/wp-admin\/admin-ajax.php', data, function(response) {<\/span><\/span>\r\n<span style=\"color: #0000ff;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/\u00a0\u00a0 \u00a0jQuery.post(ajaxurl, data, function(response) {<\/span>\r\n<span style=\"color: #0000ff;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0<\/span>\r\n<span style=\"color: #0000ff;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0document.getElementById(\"liste\").innerHTML = response;<\/span>\r\n<span style=\"color: #0000ff;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0<\/span>\r\n<span style=\"color: #0000ff;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0});<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0} \/\/*********************End myFunction<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0<\/span>\r\n<span style=\"color: #008000;\">\u00a0\u00a0 \u00a0&lt;\/script&gt;<\/span>\r\n\u00a0\u00a0 \u00a0&lt;?php\r\n...<\/pre>\n<p>Was passiert hier? Zuerstmal wird der als Parameter \u00fcbergebene Wert des Textfeldes auf die Variable aktwert gelegt, diese kommt in die data-Parameter des Ajaxcalls mit rein. Der erste Parameter mit dem &#8218;action&#8216; definiert, welche Funktion nachher aufgerufen werden soll, die kommt gleich im Anschluss. Mit dem JQuery.post(&#8230;) wird die Anfrage an den admin-ajax.php weitergeleitet und die response definiert. Die macht nichts anderes als die Div mit der id liste mit dem R\u00fcckgabewert der aufgerufenen Funktion zu belegen, konkret f\u00fcllt sie die Options-Liste unserer Datalist.<\/p>\n<p>Was weniger sch\u00f6n ist: die Url zur admin-ajax.php steht hier noch als langer Rattenschwanz mit drin, das k\u00f6nnte man sicher sch\u00f6ner l\u00f6sen. Wenn man zum Beispiel das JQuery-Script auslagern und per enqueue laden w\u00fcrde, k\u00f6nnte man mit wp_localize_script den Pfad als Variable mitgeben &#8211; aber das sind jetzt schon Feinheiten.<\/p>\n<h2>Damit der Funktionsaufruf klappt: Add Action<\/h2>\n<p>Ich habs zweimal drin, sowohl f\u00fcr die Admin-Ansicht als auch f\u00fcr nicht eingeloggte User:<\/p>\n<pre>add_action( 'wp_ajax_my_action', 'my_action' );\r\nadd_action( 'wp_ajax_nopriv_my_action', 'my_action' );<\/pre>\n<p>Jetzt fehlt uns noch die tats\u00e4chliche Funktion, die den \u00dcbergebenen Parameter aktwert entgegennimmt und und die passenden\u00a0 Werte aus der Datenbank abruft.<\/p>\n<h2>Die PHP-Funktion<\/h2>\n<p>Da wir uns im Kontext von WordPress bewegen, brauchen wir keinen externen Datenbankzugriff mit mysqli oder PDO, sondern k\u00f6nnen wie gewohnt mit dem $wpdb-Objekt arbeiten. Die Funktion sieht so aus:<\/p>\n<pre>function my_action() {\r\n\u00a0\u00a0 \u00a0global $wpdb; \r\n\r\n\u00a0\u00a0 \u00a0$whatever = $_POST['whatever'];\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\/**********************************************\/\r\n\u00a0\u00a0 \u00a0$alleposts = $wpdb-&gt;get_results( \"SELECT * from \".$wpdb-&gt;prefix.\"posts \r\nwhere post_title like '%\".$whatever.\"%' \r\n\u00a0\u00a0 \u00a0and post_type like 'post' and post_status like 'publish'\");\r\n\u00a0\u00a0 \u00a0foreach($alleposts as $einpost){\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0echo \"&lt;option&gt;\".$einpost-&gt;post_title.\"&lt;\/option&gt;\";\r\n}\r\n\u00a0\u00a0 \u00a0\/**********************************************\/\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0wp_die(); \/\/ this is required to terminate immediately and return a proper response\r\n} \/\/**********End function my_action<\/pre>\n<p>Unsere \u00fcbergebene Variable whatever wird nicht anders verarbeitet, als ob sie aus einem Formular direkt k\u00e4me, daf\u00fcr sorgt der Call mit dem <span style=\"color: #008000;\"><span style=\"color: #0000ff;\">jQuery.post(&#8230;)<span style=\"color: #000000;\">. Der Rest ist wirklich simpel, ich suche mir aus der Tabelle wp_posts alle Eintr\u00e4ge, die unseren Suchbegriff matchen, gebe die gefundenen Post Titel mit der Foreach-Schleife aus und klebe noch die Option-Tags f\u00fcr unsere Datalist dran. Fertig ist unser Autocomplete-Feld!<\/span> <\/span><\/span><\/p>\n<div id=\"attachment_1151\" style=\"width: 540px\" class=\"wp-caption alignnone\"><img aria-describedby=\"caption-attachment-1151\" decoding=\"async\" loading=\"lazy\" class=\"size-full wp-image-1151\" src=\"http:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-content\/uploads\/2018\/05\/suchwort_back.jpg\" alt=\"suchwort_back\" width=\"530\" height=\"324\" srcset=\"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-content\/uploads\/2018\/05\/suchwort_back.jpg 530w, https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-content\/uploads\/2018\/05\/suchwort_back-300x183.jpg 300w\" sizes=\"(max-width: 530px) 100vw, 530px\" \/><p id=\"caption-attachment-1151\" class=\"wp-caption-text\">suchwort_back<\/p><\/div>\n<h2>Nachtrag: JS ausgelagert und Pfad \u00fcbergeben<\/h2>\n<p>Wie ich oben schon angemerkt habe, ist es nicht so ideal dass im Javascript der volle Pfad zur admin-ajax.php steht. Das geht auch besser. Das Script kommt aus der Plugin-Datei raus, ich nenne es mal wp_ajax.js, und es sieht so aus:<\/p>\n<pre>function myFunction(wert){\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0var aktwert = wert;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0var data = {\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0'action': 'my_action',\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0'whatever': aktwert\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0};\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ der volle Pfad zur admin-ajax.php liegt auf my_ajaxurl\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0jQuery.post(<span style=\"color: #ff0000;\">my_ajaxurl<\/span>, data, function(response) {\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0document.getElementById(\"liste\").innerHTML = response;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0});\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0} \/\/*********************End myFunction<\/pre>\n<p>Die Variable my_ajaxurl kriegt ihren Wert \u00fcber wp_localize_script, das geht in einem Aufwasch mit dem Einbinden der externen Javascript-Datei, dazu stellt man folgenden Code an den Anfang des Plugins:<\/p>\n<pre>\/*********Externes Script einbinden und Pfad zur admin-ajax.php \u00fcbergeben*\/\r\nfunction mysite_js() {\r\n\u00a0\u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0\u00a0 wp_enqueue_script('mysite-js', plugins_url().'\/wp_ajax\/<span style=\"color: #008000;\">wp_ajax.js<\/span>', array('jquery'), false, false);\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0\u00a0 wp_localize_script( 'mysite-js', '<span style=\"color: #ff0000;\">my_ajaxurl<\/span>', <span style=\"color: #008000;\">admin_url( 'admin-ajax.php'<\/span> ) );\r\n\u00a0\r\n\u00a0\u00a0 }\r\nadd_action('wp_enqueue_scripts', 'mysite_js');<\/pre>\n<p>Dann kann das JS aus der Plugindatei raus, das sorgt f\u00fcr mehr \u00dcbersicht und ist wesentlich eleganter gel\u00f6st.<\/p>\n<h2>Was ich jetzt noch gern h\u00e4tte<\/h2>\n<p>Wenn man einen Rezepttitel angew\u00e4hlt und auf Return gedr\u00fcckt hat, m\u00f6chte ich gerne das gew\u00e4hlte Rezept anzeigen lassen. Aber dazu muss ich mir noch ein, zwei Gedanken machen, meine bisherige L\u00f6sung mit der angeh\u00e4ngten Rezept-ID ist n\u00e4mlich eine ziemliche Kr\u00fccke, das m\u00fcsste eleganter gehen. Ich geh mal forschen&#8230;. und daf\u00fcr gibts dann einen neuen Beitrag.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ich bin ja bekanntermassen ziemlich hartn\u00e4ckig, und obwohl ich die L\u00f6sung f\u00fcr das Autocomplete-Feld von David Nash toll finde, hats mir doch keine Ruhe gelassen. Das muss doch noch ein bisschen einfacher gehen, dachte ich mir und hab den Codex durchforstet. Zum Thema Using Ajax in Plugins bin ich f\u00fcndig geworden, da stand doch ein [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[23,46,11,4,8,2],"tags":[],"_links":{"self":[{"href":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-json\/wp\/v2\/posts\/1146"}],"collection":[{"href":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-json\/wp\/v2\/comments?post=1146"}],"version-history":[{"count":7,"href":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-json\/wp\/v2\/posts\/1146\/revisions"}],"predecessor-version":[{"id":1155,"href":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-json\/wp\/v2\/posts\/1146\/revisions\/1155"}],"wp:attachment":[{"href":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-json\/wp\/v2\/media?parent=1146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-json\/wp\/v2\/categories?post=1146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/evileu.de\/zum-schwarzen-pinguin\/wp-json\/wp\/v2\/tags?post=1146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}