PHP Sammelthread: Zend Framework und alles was dazugehört

begrüße!

ich versuche gerade breadcrumbs in meinem projekt zu integrieren.. diese sollen an zentraler stelle anhand von controller und action mithilfe einer XML datei erstellt werden..

wie würdet ihr sowas integrieren? es ist logischerweise für jede seite nötig.. und ich brauche an der stelle, an der ich die breadcrumbs erstelle zugriff auf das view und das request objekt..

ich dachte zunächst an ein plugin.. aber irgendwie komme ich nicht drauf, wie ich an dieser stelle an den view komme.. ist nicht ganz einfach sich in die struktur des frameworks reinzuarbeiten ;)

und was ist der unterschied zwischen einer "resource" und einem "plugin"? in der bootstrap klasse habe ich eine methode "registerPluginResource()" aber keine methode registerPlugin().. zum registrieren des plugins habe ich mir in der bootstrap jetzt erstmal ne instanz des front controllers geholt und lade das plugin über diesen.. ist mit sicherheit keine schöne lösung, aber zum rumspielen reichts.. das sollte ja eigentlich auch über die configurationsdatei funktionieren.. aber mit der habe ich mich noch nich wirklich beschäftigt..


ich dachte mit dem buch von stefan eggert kann ich richtig durchstarten, aber das ist leider vor der zeit von Zend_Application entstanden und deswegen für meine momentan probleme relativ nutzlos..

mfg
whizzler
(ich hoffe meine fragen sind nicht allzu blöd ;) )
 
ich versuche gerade breadcrumbs in meinem projekt zu integrieren..
...
wie würdet ihr sowas integrieren? es ist logischerweise für jede seite nötig.. und ich brauche an der stelle, an der ich die breadcrumbs erstelle zugriff auf das view und das request objekt..
Dann ist ein Plugin doch ideal, dort hast direkt Zugriff auf den Request und nen View kann man sich erstellen.
ich dachte zunächst an ein plugin.. aber irgendwie komme ich nicht drauf, wie ich an dieser stelle an den view komme..
Einfach dem Plugin nen __constructor geben in dem Du nen neuen View erstellst.
Und was ist der unterschied zwischen einer "resource" und einem "plugin"? in der bootstrap klasse habe ich eine methode "registerPluginResource()" aber keine methode registerPlugin().. zum registrieren des plugins habe ich mir in der bootstrap jetzt erstmal ne instanz des front controllers geholt und lade das plugin über diesen...
Das ist richtig, Plugins musst Du beim FrontController registrieren, entweder wie du es gemacht hast mit einer Instanz, oder per config File.
So muss das in der Config aussehen
Code:
...
resources.frontController.plugins.auth = "Project_Controller_Plugin_Auth"
resources.frontController.plugins.navi = "Project_Controller_Plugin_Navigation"
...
Und der Unterschied von Resourcen und Plugins, wird mir Ice nun warscheinlich wiedersprechen, aber ich sehe es so das Resourcen benötigte Dinge laden und Plugins zum manipulieren oder erweitern der Funktionen da sind.
Ist kurz gesagt wirklich grob meine Meinung, ich werde morgen oder so nochmal genauer erklären was ich damit meine, wenns jemanden interessiert ;).
(ich hoffe meine fragen sind nicht allzu blöd ;) )
Nö, wieso dafür ist der Thread doch da.

Hier nochmal mein Navigations Plugin was einfach das Response Object erweitert bzw. manipuliert um die Navigation einzufügen.
PHP:
<?php
/**
 * {PROJECT} Application
 *
 * This file is part of {PROJECT}
 * Copyright (c) {COPYRIGHT_YEAR} by {COPYRIGHT_BY}
 * All rights reserved
 *
 * @category		{PROJECT}
 * @copyright		Copyright (c) {COPYRIGHT_YEAR}, {COPYRIGHT_BY} ({WEBSITE})
 * @author			Daniel Schumann
 * @license			{LICENSE}
 * @version			$Id: $
 */

/**
 * Project_Controller_Plugin_Layout
 *
 * @category		{PROJECT}
 * @package			Library
 * @subpackage		Plugins
 * @uses			Zend_Layout_Controller_Plugin_Layout
 * @copyright		Copyright (c) {COPYRIGHT_YEAR}, {COPYRIGHT_BY} ({WEBSITE})
 * @license			{LICENSE}
 */
class Project_Controller_Plugin_Navigation extends Zend_Controller_Plugin_Abstract
{
	/**
	 * View Object
	 * too modifies the Output later.
	 * 
	 * @var Zend_View
	 */
	protected $_view = NULL;

	/**
	 * Config object
	 * @var Zend_Config
	 */
	private $_config = NULL;

	/**
	 * Request Object
	 * @var Zend_Controller_Request_Abstract
	 */
	protected $_request = NULL;

	/**
	 * Store a new View Object in Class.
	 * 
	 * @return void
	 */
	public function __construct()
	{
		// get a new View
		if($this->_view === NULL) {
			$this->_view = new Zend_View;
		}
	}

	/**
	 * Handle the Proccess for Navigation Loading.
	 * 
	 * @see Controller/Plugin/Zend_Controller_Plugin_Abstract#postDispatch($request)
	 * @return void
	 */
	public function postDispatch(Zend_Controller_Request_Abstract $_request)
	{
		// save request for other methods
		$this->_request = $_request;
		
		if($this->_request->getModuleName() == Zend_Registry::get(Project_Registry_LocalConfig::REGISTRY_KEY)->system->adminmodul) {
			// gets the admin Menu
			$this->_getAdminMenu();
		} else {
			// gets the module Menu
			$this->_getModuleMenu();
		}

		// set config readonly
		if($this->_config !== NULL) {
			$this->_config->setReadOnly();
		}

		// store the Navigation in Registry
		$this->_addRegistry(new Zend_Navigation($this->_config));

		// store the Acl Object in Helper
		if(Zend_Registry::isRegistered(Project_Registry_Acl::REGISTRY_KEY)) {
			Zend_View_Helper_Navigation_HelperAbstract::setDefaultAcl(Zend_Registry::get(Project_Registry_Acl::REGISTRY_KEY));
		}

		// store a Role in Helper
		$role = Zend_Auth::getInstance()->hasIdentity() ? 
					Zend_Auth::getInstance()->getIdentity()->role : 
				'guest';
		Zend_View_Helper_Navigation_HelperAbstract::setDefaultRole($role);
		
		// store the Translator in Helper
		if(Zend_Registry::isRegistered(Project_Registry_Translate::REGISTRY_KEY)) {
			$this->_view->getHelper('navigation')->setTranslator(Zend_Registry::get(Project_Registry_Translate::REGISTRY_KEY));
		}

		// set the Zend_View Paths for these partial
		// @todo !monitoring after ever Zend Library Update!
		$this->_view->addScriptPath(array(
			implode(DS, array(
				APP_DIR,
				'views', 'scripts'
			)),
			implode(DS, array(
				Zend_Controller_Front::getInstance()->getModuleDirectory($_request->getModuleName()),
				'views', 'scripts')
			)
		));

		// insert Navigation in the current Response object
		$this->getResponse()->insert(Project_Layout_Navigation::REGISTRY_KEY, $this->_view->getHelper('navigation')->menu()->setPartial('partials/menu.phtml'));
	}

	/**
	 * Handle the Proccess too fetch all the Modules Menu,
	 * from Config File named "admin.xml"
	 * 
	 * @return void
	 */
	protected function _getAdminMenu()
	{
		$modules = $this->_getModules();
		foreach($modules AS $modulPath) {
			$this->_addConfig(implode(DS, array($modulPath, 'configs', 'admin.xml')));
		}
	}

	/**
	 * Handle the Proccess too fetch the Modules Menu,
	 * from Config File named "menu.xml"
	 * 
	 * @return void
	 */
	protected function _getModuleMenu()
	{
		// reset config if error handler is initiate
		$this->_config = NULL;

		$modules = $this->_getModules($this->_request->getModuleName());
		foreach($modules AS $modulPath) {
			$this->_addConfig(implode(DS, array($modulPath, 'configs', 'menu.xml')));
		}
	}

	/**
	 * Set Config Data with the Modification Option
	 * 
	 * @param string $_path
	 * @return void
	 */
	protected function _setConfig($_path)
	{
		$this->_config = new Zend_Config_Xml($_path, 'nav', TRUE);
	}

	/**
	 * Adds Config too the existing by using Zend_Config::merge()
	 * 
	 * @param string $_path
	 * @return void
	 */
	protected function _addConfig($_path)
	{
		if(!Zend_Loader::isReadable($_path)) {
			return;
		}

		if($this->_config === NULL) {
			$this->_setConfig($_path);
			return;
		}

		$this->_config->merge(new Zend_Config_Xml($_path, 'nav'));
	}

	/**
	 * Set Data too the existing Registry Key,
	 * by using Zend_Config->merge().
	 * 
	 * @param object $_data								Zend_Navigation instance
	 * @return Project_Controller_Plugin_Navigation		provides Fluent Interface
	 */
	protected function _setRegistry(Zend_Navigation $_data)
	{
		Zend_Registry::set(Project_Registry_Navigation::REGISTRY_KEY, $_data);
		return $this;
	}

	/**
	 * Adds Data too the existing Registry Key,
	 * by using Zend_Config->merge().
	 * 
	 * @param object $_data								Zend_Navigation instance
	 * @return Project_Controller_Plugin_Navigation		provides Fluent Interface
	 */
	protected function _addRegistry(Zend_Navigation $_data)
	{
		if(Zend_Registry::isRegistered(Project_Registry_Navigation::REGISTRY_KEY) == FALSE) {
			return $this->_setRegistry($_data);
		}

		Zend_Registry::set(Project_Registry_Navigation::REGISTRY_KEY,
			(object)array_merge(
				(array)Zend_Registry::get(Project_Registry_Navigation::REGISTRY_KEY), 
				(array)$_data
			)
		);
		return $this;
	}

	/**
	 * Fetch Module Paths
	 * 
	 * @param NULL|string|array $_module		if NULL fetch all Modules, or the defined Modules
	 * @return array
	 */
	private function _getModules($_module = NULL)
	{
		// check if is an array
		if($_module !== NULL && !is_array($_module)) {
			settype($_module, 'array');
		}
		// workaround too get the aktually modules path
		$path = Zend_Controller_Front::getInstance()->getModuleDirectory($this->_request->getModuleName());
		$path = explode(DS, $path);	// covert too array
		unset($path[count($path)-1]);	// delete module Directory
		$path = implode(DS, $path) . DS;	// convert too string

		$paths = array();

		// fetch all modules Directorys
		$iterator = new DirectoryIterator($path);
		while($iterator->valid()) {
			if(!$iterator->isDot()) {
				if($_module === NULL || in_array($iterator->getFilename(), $_module, TRUE)) {
					$paths[] = $path . $iterator->getFilename();
				}
			}
    		$iterator->next();
		}
		return (array)$paths;
	}
}
Interessant dürften für dich nur der Konstructor sein und der postDispatch Hook. Das Plugin muss ich nochmal umbauen aber diese zwei Funktionen kann man ohne weiteres so nutzen und das holen der Menü Config wirst Du eh anders lösen.

Im Layout machst Du dann einfach folgendes
PHP:
<?php echo $this->layout()->{Project_Layout_Navigation::REGISTRY_KEY}; ?>
und die Navi erscheint genau an dieser Stelle.
("Project_Layout_Navigation::REGISTRY_KEY" ist nur um es Konsitent zu halten, etwa in der Art des ErrorHandler Plugins mit den Status codes. Kann man aber auch durchaus einfach z.B. mit "breadcrum" ersetzen.)

Und der Vollstängikeit halber hier noch mein partial
PHP:
<?php // "partials/menu.phtml" 
if(!empty($this->container)): ?>
<ul class="navigation_normal">
<?php foreach($this->container as $page): ?>
  <li><?php echo $this->navigation()->menu()->htmlify($page); ?>
<?php if(is_array($page->pages) && !empty($page->pages)): ?> 
    <ul class="container_pages" id="<?php echo (empty($page->id) ? substr(md5($page->href), 0, 8) : $page->id); ?>">
<?php foreach($page->pages AS $subpage): ?>
      <li><?php echo $this->navigation()->menu()->htmlify($subpage); ?></li><?php echo PHP_EOL; ?>
<?php endforeach; ?>
    </ul>
<?php endif; ?>
  </li>
<?php endforeach; ?>
</ul>
<?php endif; ?>

So ich hoffe ich konnte zu so später Stunde nocheinmal helfen, fals noch Fragen sind werde ich die morgen beantworten.
 
Zuletzt bearbeitet:
Dann gehen doch aber die Änderungen die man am View-Objekt gemacht hat verloren, oder?8O

Btw: Warum verwendest du denn nicht einfach den Breadcrumb-View-Helper?:ugly:
Das ist richtig, wenn man den Inhalt des erstellten Views nicht in das Respons Object integriert. Das tue ich im Hook als letztes
PHP:
// insert Navigation in the current Response object
        $this->getResponse()->insert(Project_Layout_Navigation::REGISTRY_KEY, $this->_view->getHelper('navigation')->menu()->setPartial('partials/menu.phtml'));
Ich nutzt den Helfer nicht weil die Zuweisung der ACL und Role meiner Meinung nach nix im Layout oder View zu suchen hat, und ich mich nicht stäntig in der init Methode eines Controller wiederholen will, um es dort zuzuweisen.

Naja und das ich den Helfer nicht nutze ist auch nicht ganz korrekt, ich nutze Ihn halt im Plugin ;)
 
Und der Unterschied von Resourcen und Plugins, wird mir Ice nun warscheinlich wiedersprechen, aber ich sehe es so das Resourcen benötigte Dinge laden und Plugins zum manipulieren oder erweitern der Funktionen da sind.
Resourcen werden bisher nur von Zend_Application definiert, und da ist die Resource eben da um ein Moduk (Db, Controller, Mail, View ...) zu konfigurieren.
Ein Front-Controller-Plugin ist eben dafür um in den Routing-Prozess des Zf einzugreifen, und kann eben per Resource dem FC hinzugefügt werden.
(ka ob es deiner Aussage wiederspricht, ich habe keine richtige Definition herauslesen können)

Dann gehen doch aber die Änderungen die man am View-Objekt gemacht hat verloren, oder?8O
jo, also es gibt niemals nie einen Grund manuell neu ein View-Objekt zu erzeugen, es seidenn man nutzt auch den View-Teil für Mails ;)

Btw: Warum verwendest du denn nicht einfach den Breadcrumb-View-Helper?:ugly:
genau das dachte ich mir, das in das Layout rein und gut ist.
Das FC-Plugin kann ja bleiben, ist ja ok so, auch wenn es mir persönlich zuviele Seiteneffekte hat (Zend_Registry-Zugriffe, Manipulation des Response-Objektes), meiner Meinung nach greift das Plugin viel zu weit in das System ein.


Btw: Entschuldigung, dass ich mich momentan nur in Aktion trete, wenn ich speziell angesprochen werde, aber in meinem Studium ist momentan die Hölle los und da komme ich kaum zu programmieren. Und wenn doch findet dies leider nicht in PHP statt :(
 
Ich habe auch lange überlegt wie ich es machen soll, aber dann bin ich auf folgenden Thread gestoßen (Post 2) und das mit dem Respons Segment fand ich persönlich ne klasse Idee.
 
Also ich reiße mal an, wie ich es mir ungefähr vorstellen würde:
  • eine Application-Navigation-Resource (depends View, Layout, FrontController)
  • diese Resource setzt ein FC-Plugin (da strolch scheinbar einiges drinne macht)
  • das FC-Plugin erstellt dann die Navigation wie gewünscht (OHNE Seiteneffekte wie das Schreiben in den Controller-Response oder Abholen irgendwelcher Eigenschaften aus der Registry, diese sollten alle Eigenschaften der Resource sein)
  • das FC-Plugin fügt dann dem Layout unter einer gegebenen Var (Application-Resource-Configuration) das Navigations-Objekt hinzu
  • das Rendern ist dann ja Sache des Views, ein partial ist aber bestimmt keine schlechte Idee

Edit: Ich sehe noch das Strolch irgendwelche Vars abruft ob man eingeloggt ist, Admin und so. Um das möglichst variabel zu gestalten würde ich das wahrscheinlich mit ACLs lösen, müsste man eben Zend_Navigation Pages/Container schaffen, die gegen ACLs prüfen können, möglicherweise wieder indem man aus einer Resource das ACL-Objekt abholt.
 
Zuletzt bearbeitet:
Btw: Warum verwendest du denn nicht einfach den Breadcrumb-View-Helper?:ugly:

den verwende ich ;) ich wollte nur das auslesen der xml datei und das ermitteln des aktiven breadcrumbs an zentraler stelle haben.. im prinzip sind es ja keine 10 zeilen code.. aber ich wollte das nicht in den view schreiben.. in der bootstrap ging es nicht, weil man da noch keinen zugriff auf das request objekt hat (und ich muss für die breadcrumbs ja wissen, welcher controller und welche action ausgeführt werden sollen)
ich hätte es wohl auch in einem view helper umsetzen können.. aber jetzt bin ich erstmal glücklich ;)

jetzt im nachhinein denke ich mir, dass ich es wohl auch in einem eigenen view helper machen könnte.. das zf bietet einfach zu viele möglichkeiten ;)

@strolch:
danke für deine ausführliche antwort! das registrieren des plugins in der config werde ich auf jeden fall so machen..

das restliche ist wohl zuviel des guten für meine zwecke.. für die breadcrumbs brauche ich wohl nicht direkt am response objekt rumpfuschen..
als nächstes mache ich mich an die integration von acl.. mal sehen, ob ich da dann auf deinen vorschlag zurückkomme ;) aber für meine zwecke sollte es denke ich ausreichen, direkt in den action methoden zu überprüfen, ob der user für die aufgerufene seite die nötigen rechte hat..
 
Zuletzt bearbeitet:
oO Bezüglich Ice seiner Aussage zuer Navigation Resource, warum zum Teufel sagt mir keiner das es auch schon ein Navigation Resource Plugin existiert :p

Dann muss ich mein Konzept nochmal umschreiben, und dazu eine Frage.

Ich Plane das jedes Modul seine eigene Navi Konfig bekommen soll, es müsste doch auch machbar sein die Konfig Daten per Model in die Bootstrap zu bekommen oder? In meinen Augen ist dies sinnvoller als ein Gigantischer Codeblock in der Bootstrap. Was meint Ihr dazu?
 
Das Bootstrapping findet VOR dem Routing statt, also nicht, du kannst aber manuell ein Router-Objekt erstellen, und da deine URL eingeben und daraus dann die Daten auslesen ;)
 
@all oder speziell an Ice

Gibt es einen Weg in der Bootstrap an den Request zu gelangen bzw. an das gerade angefragte Modul?

Du könntest es aus Request-URI raussuchen, aber nein, der Bootstrap ist noch vorm Routing. Du müsstest ein Plugin mit Route_shoutdown machen;)

Edit: Verdammt und einmal dachte ich, ich wär erster :(
 
Und genau das hatte ich schon vermutet :(, irgendwie fällt mir kein sinnvoller weg ein eine Navigation in eine Bootstrap Resource auszulagern.

Ich bräuchte den Request um zu entscheiden ob der die admin Navi holen soll oder die public Navi, habt Ihr vielleicht noch Ideen wie man das lösen kann?
Evtl. gar beides in der Registry ablegen und per Plugin dann das richtige holen, ist das sinnvoll?

*edit

@ice Du meinst so ne
PHP:
$router = new Zend_Controller_Router_Rewrite();
$router->route(new Zend_Controller_Request_Http());
 
So in die Richtung.
Du musst in dem Request aber noch dir URL einstellen und dem Router natürlich noch die RewriteRules hinzufügen, dann sollte es aber funktionieren.

PHP:
$router = new Zend_Controller_Router_Rewrite();
// add routes
$request = $router->route(new Zend_Controller_Request_Http($_SERVER["HTTP_HOST"] .  $_SERVER["REQUEST_URI"]));
var_dump($request)
 
Jap danke Dir hab es inzwischen getestet, geht wesentlich kürzer:
PHP:
$router = new Zend_Controller_Router_Rewrite();
$module = $router->route(new Zend_Controller_Request_Http())->getModuleName();

Mit "$router->route();" bekommt man ein Request Objekt und die Requestet Uri holt "...Router_Rewrite" sich selber per Zend_Uri.

So bekomme ich mein aktuelles Modul, danke Dir nochmal.
 
Du musst in dem Request aber noch dir URL einstellen und dem Router natürlich noch die RewriteRules hinzufügen, dann sollte es aber funktionieren.

Ist es aus u.a. dem genannten Grund nicht gerade "unsauber", dass so zu lösen? Wenn man extra ein zweites Request Objekt erstellen muss.. das sollte dann ja auch genau so wie das "Haupt-Request" Objekt arbeiten.
Sollte man das dann nicht doch lieber per Plugin lösen?
Ist das bootstrappen nicht dafür da, die wirklichen Hauptbestandteile zu laden - View, Acl, Modulverwaltung, ... und so eine Navigation würde ja auf dem View aufbauen und eine Templateveränderung darstellen (so wie man z.B. nicht nur die Navigation je nach Seite lädt sondern vllt. auch bestimmte Inhaltsboxen oder so)... das würde ich dann glaube ich schon eher über ein PlugIn lösen.

Oder laber ich totalen Quatsch? So viel Erfahrung mit dem ZF habe ich nämlich noch nicht ;) Speziell wie man am besten welche Angelegenheiten löst fehlt mir noch einiges wissen.. ;)
 
natürlich ist es unsauber, aber wenn er unbedingt das Navigations-Objekt komplett richtig im Boostrapping aufbauen will, geht es eben nur so.
Manchmal muss man auch schöne Strukturen aufbrechen, wenn es nicht anders geht.