Advitum.de auf Google+

CakePHP Tutorial: Epsilon-Greedy-Tests – ein Plugin erstellen

CakePHP Tutorial: Epsilon-Greedy-Tests – ein Plugin erstellen
VN:F [1.9.22_1171]
Bewertung: 5.0/5 (2 Stimmen abgegeben)
Von am
Kategorien: CakePHP, PHP, Programmieren, Tutorials

Letzte Woche habe ich auf Pixeltuner.de die Epsilon-Greedy-Methode zur Optimierung von Landingpages vorgestellt. Epsilon-Greedy-Tests sind im Grunde modifizierte A/B-Tests. In diesem Artikel möchte ich Dir zeigen, wie Du ein CakePHP-Plugin schreiben kannst. Als Beispiel erstellen wir ein CakePHP-Tutorial, mit dem wir Epsilon-Greedy-Tests durchführen können.

Obwohl ich zwar auf dem Ergebnis der letzten Artikel dieser Serie aufbaue, brauchst Du diese nicht unbedingt gelesen haben, denn das Plugin wird sich automatisch in diese und jede andere CakePHP-App einfügen. Viel wichtiger ist, dass du meinen Artikel über Epsilon-Greedy-Tests gelesen hast, damit du weißt, was ein Epsilon-Greedy-Test ist und wie er funktioniert.

CakePHP Tutorial: Epsilon-Greedy-Tests - ein Plugin erstellen, 5.0 out of 5 based on 2 ratings

Artikelserie: CakePHP-Tutorial: Web-Anwendung mit MVC

Dieser Artikel ist Teil einer mehrteiligen Artikelserie. Lies dir auch die restlichen Teile durch!

  1. CakePHP Tutorial: Grundfunktionen und Hallo Welt
  2. CakePHP Tutorial: Views, Layouts und Helpers
  3. CakePHP Tutorial - Model und Controller - ein Gästebuch programmieren
  4. CakePHP Tutorial: Admin-Routen und Login - erweitertes Gästebuch
  5. CakePHP Tutorial: Epsilon-Greedy-Tests – ein Plugin erstellen

Abonniere den RSS-Feed von Advitum, um keinen Artikel zu verpassen, oder folge mir über meine Facebook-Seite oder meinen Twitter-Kanal.

Schritt 1: Datenbank-Tabelle für die Test anlegen

Als erstes legen wir in unserer Datenbank eine neue Tabelle epsilongreedy_variants an:

Spalte Typ
id bigint(20) unsigned Auto-Inkrement
test varchar(100)
key varchar(100)
views bigint(20) unsigned NULL [1]
actions bigint(20) unsigned NULL [1]

Schritt 2: Plugin anlegen

Als nächstes legen wir im Ordner app/Plugins einen neuen Ordner EpsilonGreedy an. In diesem Ordner befindet sich später unser gesamtes Plugin. In diesem Ordner müssen wir nun noch die Ordner Controller, Model und View an. In diesen können wir später, wie in der regulären App, Controller, Componenten und alle anderen CakePHP-Klassen anlegen.

Im Ordner EpsilonGreedy/Model legen wir nun die Datei EpsilonGreedyAppModel.php an. In dieser können wir für alle Models des Plugins Einstellungen vornehmen, was wir jetzt auch tun.

<?php

	class EpsilonGreedyAppModel extends AppModel
	{
		public $tablePrefix;

		public function __construct($id = false, $table = null, $ds = null) {
			parent::__construct($id, $table, $ds);
			$this->tablePrefix .= 'epsilongreedy_';
		}
	}

?>

Hier legen wir fest, dass alle Models ihren Tabellen den Prefix »epsilongreedy_« hinzufügen. So vermeiden wir, dass wir versehentlich mit einer Tabelle der App kollidieren.

Nun legen wir noch schnell das Model für die Tabelle an, die wir eben angelegt haben.

<?php
	
	class Variant extends EpsilonGreedyAppModel
	{
		
	}
	
?>

Schritt 3: EpsilonGreedyComponent

Als nächstes legen wir die Component an, die sich um die Verwaltung der Epsilon-Greedy-Tests kümmert. Dazu legen wir zuerst den Ordner EpsilonGreedy/Controller/Component an. Hier legen wir nun die Datei EpsilonGreedyComponent.php an.

<?php
	
	class EpsilonGreedyComponent extends Component
	{
		public $tests = array();
		public $explore = .1;

		public function beforeRender($controller) {
			$controller->helpers['EpsilonGreedy.EpsilonGreedy'] = array('explore' => $this->explore);

			$this->Variant = ClassRegistry::init('EpsilonGreedy.Variant');

			foreach($this->tests as $test => $variants) {
				foreach($variants as $key) {
					$variant = $this->Variant->find('first', array(
						'conditions' => array(
							'test' => $test,
							'key' => $key
						)
					));

					if($variant == null) {
						$this->Variant->create();
						$this->Variant->save(array('Variant' => array(
							'test' => $test,
							'key' => $key
						)), false);
					}
				}
			}

			if(isset($controller->request->query['eg'])) {
				$data = unserialize(base64_decode($controller->request->query['eg']));
				$this->success($data['test'], $data['option']);

				$controller->redirect(array());
			}
		}

		public function success($test, $option) {
			$this->Variant->updateAll(
				array(
					'actions' => 'actions+1'
				),
				array(
					'test' => $test,
					'key' => $option
				)
			);
		}
	}
	
?>

Die Component speichert die Varianten in der Datenbank (Zeilen 13 – 30) und sorgt dafür, dass die Erfolge der Tests gezählt werden (Zeilen 32 – 50). Außerdem läd die Component den EpsilonGreedyHelper (Zeile 9), welchen wir als nächstes anlegen.

Schritt 4: EpsilonGreedyHelper

<?php
	
	class EpsilonGreedyHelper extends AppHelper
	{
		public function getOption($test) {
			$this->Variant = ClassRegistry::init('EpsilonGreedy.Variant');

			$clickThroughRates = array();

			$variants = $this->Variant->find('all', array(
				'conditions' => array(
					'test' => $test
				)
			));

			foreach($variants as $variant) {
				if($variant['Variant']['views'] == 0) {
					$clickThroughRates[$variant['Variant']['key']] = 0;
				} else {
					$clickThroughRates[$variant['Variant']['key']] = $variant['Variant']['actions'] / $variant['Variant']['views'];
				}
			}

			if(rand(0, 100) / 100 <= $this->settings['explore']) {
				$choice = array_rand($clickThroughRates);
			} else {
				arsort($clickThroughRates);
				$best = reset($clickThroughRates);
				$winners = array();
				foreach($clickThroughRates as $key => $value) {
					if($value >= $best) {
						$winners[$key] = $value;
					}
				}

				$choice = array_rand($winners);
			}

			$this->Variant->updateAll(
				array(
					'views' => 'views+1'
				),
				array(
					'test' => $test,
					'key' => $choice
				)
			);

			return $choice;
		}

		public function linkParam($test, $choice) {
			return array('eg' => base64_encode(serialize(array('test' => $test, 'option' => $choice))));
		}
	}
	
?>

Der EpsilonGreedyHelper ist für das Frontend der Tests verantwortlich. Hier finden sich die Methoden zur Auswahl einer Variante (Zeilen 5 – 50) und generiert die Link-Parameter, welche zum Zählen der Erfolge benötigt werden (Zeilen 52 – 54).

Schritt 5: Anwendung des Plugins

Das Plugin funktiniert jetzt schon, wir müssen es nur noch anwenden. Dazu laden wir es zunächst über die Datei Config/bootstrap.php:

CakePlugin::load('EpsilonGreedy');

Nun müssen wir die Component nur noch in den AppController laden:

<?php
App::uses('Controller', 'Controller');

class AppController extends Controller {
	public $helpers = array('Nav');
	public $components = array(
		'Session',
		'Auth' => array(
			'loginRedirect' => array('controller' => 'entries', 'action' => 'index', 'admin' => true),
			'logoutRedirect' => array('controller' => 'pages', 'action' => 'display', 'home', 'admin' => false),
			'loginAction' => array('controller' => 'users', 'action' => 'login', 'admin' => false),
			'authError' => 'Melde dich an, um diesen Bereich zu sehen!'
		),
		'EpsilonGreedy.EpsilonGreedy' => array(
			'tests' => array(
				'landingpage' => array(
					'a', 'b', 'c'
				)
			)
		)
	);
	
	public function beforeFilter() {
		if(!empty($this->params['prefix']) && $this->params['prefix'] == 'admin'){
			$this->Auth->deny();
         } else {
			$this->Auth->allow();
		}
	}
}

Über das Array tests können wir die Epsilon-Greedy-Test einrichten. Hier pflegen wir in diesem Fall einen Test mit dem Titel »landingpage« ein. Die drei Varianten heißen hier a, b und c. Hier können beliebig viele Test und Varianten eingepflegt werden.

Als letztes müssen wir nur noch den Epsilon-Greedy-Test durchführen. Das können wir sehr einfach in jedem beliebigen View durchführen:

<?php
	
	$test = 'landingpage';
	$choice = $this->EpsilonGreedy->getOption($test);

?>

<h1>Landingpage</h1>
<?php echo $this->Html->link('Variante ' . $choice, array('controller' => 'pages', 'action' => 'display', 'home', '?' => $this->EpsilonGreedy->linkParam($test, $choice))); ?>

Über die Methode getOption können wir eine Variante auswählen. Dann fügen wir den Link mit den entsprechenden Parametern ein. Hier sollten nun natürlich verschiedene Varianten des gleichen Contents angezeigt werden, um zu prüfen, welche Variante am effektivsten ist.

Und schon haben wir ein Plugin, um Epsilon-Greedy-Test durchzuführen. Ich hoffe, dir hat das Tutorial gefallen und alles hat funktioniert. Also schreib mir einen Kommentar, ich freue mich auf Dein Feedback!

CakePHP Tutorial: Epsilon-Greedy-Tests - ein Plugin erstellen, 5.0 out of 5 based on 2 ratings

Nächster Artikel der Serie

Dies ist der (vorerst) letzte Artikel in dieser Serie. Bald folgen aber bestimmt neue Artikel!

Abonniere den RSS-Feed von Advitum, um keinen Artikel zu verpassen, oder folge mir über meine Facebook-Seite oder meinen Twitter-Kanal.

Jetzt seid ihr dran!

Teilt eure Meinung mit uns in den Kommentaren, gebt eine Bewertung für diesen Artikel ab und teilt ihn in Social Networks!

Über

Ich bin ein junger Webdesigner und Programmierer aus Siegen und blogge auf Advitum.de über meine Erfahrungen im Web. Meine Themenschwerpunkte liegen im Bereich der Web-Entwicklung mit PHP, JavaScript, HTML und anderen Script-, Programmier- und Markup-Sprachen, der Nutzung von Content Management System wie Typo3, Wordpress etc. und der Effekt-Hascherei mit Photoshop. Seit 2008 blogge ich auf Advitum.de – mal mehr, mal weniger regelmäßig – über alles, was mich so interessiert. Wenn dir mein Blog gefällt, freue ich mich immer sehr über Feedback in Form von Kommentaren und E-Mails.

Kommentare zu diese Artikel

Schreibe jetzt einen Kommentar!

André schrieb am Antworten

Hallo,

Vielen Dank für das Tutorial!

Ich habe es direkt ausprobiert und bei mir erschien der Fehler:

Strict (2048): Declaration of EpsilonGreedyComponent::beforeRender() should be compatible with Component::beforeRender(Controller $controller) [APP\Plugin\EpsilonGreedy\Controller\Component\EpsilonGreedyComponent.php, line 44]

In dieser Datei habe ich die Zeile 8 wie folgt geändert.

public function beforeRender(Controller $controller) {

}

Der Fehler ist weg und in der Datenbank werden Einträge geändert. Das sollte so passen? Im Einsatz habe ich CakePHP 2.3.5 .

    Lars Ebert schrieb am Antworten

    Hallo André,

    vielen Dank für den Hinweis. Bei mir ist dieser Fehler nicht aufgetreten, da ich scheinbar keine Strict-Errors anzeigen lasse.

    Was passiert hier? In PHP5 wurde das Type-Hinting für Funktions-Prototypen umgesetzt. Das heißt, dass man beim Anlegen eines Funktions-Prototypen definieren kann, welchen Typ die Parameter haben müssen, die die Funktion später annimmt.

    CakePHP definiert also, dass die Methode beforeRender einer Klasse, die die Component-Klasse beerbt, auf jeden Fall als ersten Parameter ein Objekt des Typs Controller (oder einer Klasse, die aus dieser abgeleitet wurde) akzeptieren muss. Deshalb müssen wir an dieser Stelle den Parameter unserer beforeRender-Funktion auf diesen Variablen-Typ beschränken.

    Danke noch einmal für den Hinweis! Ich sollte vielleicht einmal meinen Dev-Server neu konfigurieren…

      André schrieb am Antworten

      Danke für die Erklärung!

mark schrieb am Antworten

Ein kleines bisschen sauberer wäre es gewesen, dem MVC Prinzip folgend, die Daten von der Component zum View durchzureichen und vom Helper lediglich ausgeben zu lassen (statt den Helper den DB Call machen zu lassen).
Aber ansonsten sehr schön durchs Tutorial geführt! :)

PS: Cake Coding standards wäre das i Tüpferl. Aber halb so wild.

    Lars Ebert schrieb am Antworten

    An dieser Stelle habe ich auch lange mit mir gehadert! Denn in Views, die zum PagesController gehören, müsste man dann für jede Seite, auf der man EG-Tests machen will, eigene Actions definieren und könnte die display-Action nicht nutzen. Deshalb hier der etwas »unsaubere« Ansatz, im View ein Model zu nutzen.

    Was sind denn für dich die Cake Coding Standards? Vielleicht kann ich ja auch noch was lernen.

Diese Artikel könnten dir auch gefallen