How To: Abstraktion am praktischen Beispiel Teil 1

mastercad
15.04.2015 23:34:46
Da ich in der Vergangenheit oft bemerke, das viele Probleme haben mit der Abstraktion von Code, möchte ich hier eine kleine Serie beginnen um zu zeigen wie ich vor gehe und woran man deutlich erkennt, dass es ein Anhaltspunkt für eine Abstraktion ist.

Zu erst einmal ein Beispiel aus meinem aktuellen Projekt:

PHP code

  1. public function indexAction() {
  2. $oNahrungsmittelXMasseinheitenDbTable = new Application_Model_DbTable_NahrungsmittelXMasseinheit();
  3. $iMasseinheitMapId = CAD_Tool_Extractor::extractOverPath($this, 'getRequest->getParams->id');
  4. $sContent = '';
  5.  
  6. $oNahrungsmittelXMasseinheitenRowSet = $oNahrungsmittelXMasseinheitenDbTable->findNahrungsmittelXMeasureEntry($iMasseinheitMapId);
  7.  
  8. foreach ($oNahrungsmittelXMasseinheitenRowSet as $oNahrungsmittelXMasseinheitenRow) {
  9. $sMasseinheitenDropDown = $this->createMeasureDropDown($oNahrungsmittelXMasseinheitenRow->{'nahrungsmittel_x_masseinheit_fk'});
  10. $sMasseinheitenMapDropDown = $this->createMeasureDropDown($oNahrungsmittelXMasseinheitenRow->{'nahrungsmittel_x_masseinheit_referenz_masseinheit_fk'});
  11.  
  12. $this->view->assign('sDurchschnittsmenge', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_x_masseinheit_durchschnittsmenge'));
  13. $this->view->assign('iNahrungsmittelFk', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_x_masseinheit_nahrungsmittel_fk'));
  14. $this->view->assign('sNahrungsmittelName', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_name'));
  15. $this->view->assign('sMasseinheitenDropDown', $sMasseinheitenDropDown);
  16. $this->view->assign('sMasseinheitenMapDropDown', $sMasseinheitenMapDropDown);
  17.  
  18. $sContent .= $this->view->render('loops/masseinheit-menge-map.phtml');
  19. }
  20.  
  21. $this->view->assign('sContent', $sContent);
  22. }
  23.  
  24. public function showAction() {
  25.  
  26. }
  27.  
  28. public function editAction() {
  29. $oNahrungsmittelXMasseinheitenDbTable = new Application_Model_DbTable_NahrungsmittelXMasseinheit();
  30. $iMasseinheitMapId = CAD_Tool_Extractor::extractOverPath($this, 'getRequest->getParams->id');
  31. $sContent = '';
  32.  
  33. if (null !== $iMasseinheitMapId) {
  34. $oNahrungsmittelXMasseinheitenRow = $oNahrungsmittelXMasseinheitenDbTable->findNahrungsmittelXMeasureEntry($iMasseinheitMapId);
  35. $sMasseinheitenDropDown = $this->createMeasureDropDown($oNahrungsmittelXMasseinheitenRow->{'nahrungsmittel_x_masseinheit_fk'});
  36. $sMasseinheitenMapDropDown = $this->createMeasureDropDown($oNahrungsmittelXMasseinheitenRow->{'nahrungsmittel_x_masseinheit_referenz_masseinheit_fk'});
  37.  
  38. $this->view->assign('sDurchschnittsmenge', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_x_masseinheit_durchschnittsmenge'));
  39. $this->view->assign('iNahrungsmittelFk', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_x_masseinheit_nahrungsmittel_fk'));
  40. $this->view->assign('sNahrungsmittelName', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_name'));
  41.  
  42. $this->view->assign('sMasseinheitenDropDown', $sMasseinheitenDropDown);
  43. $this->view->assign('sMasseinheitenMapDropDown', $sMasseinheitenMapDropDown);
  44.  
  45. $sContent .= $this->view->render('loops/masseinheit-menge-map.phtml');
  46. } else {
  47. $sMasseinheitenDropDown = $this->createMeasureDropDown();
  48. $sMasseinheitenMapDropDown = $this->createMeasureDropDown();
  49.  
  50. $this->view->assign('sMasseinheitenDropDown', $sMasseinheitenDropDown);
  51. $this->view->assign('sMasseinheitenMapDropDown', $sMasseinheitenMapDropDown);
  52.  
  53. $sContent .= $this->view->render('loops/
  54. masseinheit-menge-map.phtml');
  55. }
  56.  
  57. $this->view->assign('sContent', $sContent);
  58. }
  59.  
  60. protected function createMeasureDropDown($iMeasureId = null) {
  61. $sDropDownContent = '';
  62. $oMeasuresDbTable = new Application_Model_DbTable_Masseinheiten();
  63. $oMeasuresRowSet = $oMeasuresDbTable->findMeasures(true);
  64.  
  65. foreach ($oMeasuresRowSet as $oMeasureRow) {
  66. $this->view->assign('a_masseinheit', $oMeasureRow->toArray());
  67. $this->view->assign('masseinheit_fk', $iMeasureId);
  68.  
  69. $sDropDownContent .= $this->view->render('loops/masseinheit_option.phtml');
  70. }
  71.  
  72. return $sDropDownContent;
  73. }


man sollte sich hier nicht an der Benamung stören, die Datenbank ist bisher auf deutsch gepflegt worden und meine Funktionen und Parameter halte ich in englisch. Das provoziert einen schrecklichen Mischmasch, soll uns aber nicht von Hauptteil ablenken.

An diesem Code sieht man meiner Ansicht nach ziemlich deutlich, das hier abstrahiert werden kann.

Was haben wir?

  • eine index Action mit einer liste an Einträgen, die alle gleich aussehen
  • eine edit Action zum editieren eines Eintrages
  • eine show Action zum anzeigen eines Eintrags





Der eine Eintrag sieht sowohl in der Liste als auch in der Show und der Edit gleich aus, bis auf das darstellen der Input Felder.

Das erstellen des DropDown habe hier hier bereits extrahiert um nicht eine schleife zum durchlaufen der Einträge in jeder der Action zu haben und greife auf diese Funktion in den jeweiligen Actions zu.

Als nächstes fällt auf das der code für das vorbereiten der Parameter, das holen der einträge und das setzen immer wieder gleich ist. Was diese 3 Funktionen in der Summe vereint, ist die Row. Also nehmen wir diese 3 Formulierungen und machen daraus eine zentrale Funktion.

Ausgang - Erstellen der Liste in der Index Action :


PHP code

  1. foreach ($oNahrungsmittelXMasseinheitenRowSet as $oNahrungsmittelXMasseinheitenRow) {
  2. $sMasseinheitenDropDown = $this->createMeasureDropDown($oNahrungsmittelXMasseinheitenRow->{'nahrungsmittel_x_masseinheit_fk'});
  3. $sMasseinheitenMapDropDown = $this->createMeasureDropDown($oNahrungsmittelXMasseinheitenRow->{'nahrungsmittel_x_masseinheit_referenz_masseinheit_fk'});
  4.  
  5. $this->view->assign('sDurchschnittsmenge', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_x_masseinheit_durchschnittsmenge'));
  6. $this->view->assign('iNahrungsmittelFk', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_x_masseinheit_nahrungsmittel_fk'));
  7. $this->view->assign('sNahrungsmittelName', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_name'));
  8. $this->view->assign('sMasseinheitenDropDown', $sMasseinheitenDropDown);
  9. $this->view->assign('sMasseinheitenMapDropDown', $sMasseinheitenMapDropDown);
  10.  
  11. $sContent .= $this->view->render('loops/masseinheit-menge-map.phtml');
  12. }


Erstellen eines gefüllten Eintrages in der Edit Action:

PHP code

  1. $oNahrungsmittelXMasseinheitenRow = $oNahrungsmittelXMasseinheitenDbTable->findNahrungsmittelXMeasureEntry($iMasseinheitMapId);
  2. $sMasseinheitenDropDown = $this->createMeasureDropDown($oNahrungsmittelXMasseinheitenRow->{'nahrungsmittel_x_masseinheit_fk'});
  3. $sMasseinheitenMapDropDown = $this->createMeasureDropDown($oNahrungsmittelXMasseinheitenRow->{'nahrungsmittel_x_masseinheit_referenz_masseinheit_fk'});
  4.  
  5. $this->view->assign('sDurchschnittsmenge', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_x_masseinheit_durchschnittsmenge'));
  6. $this->view->assign('iNahrungsmittelFk', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_x_masseinheit_nahrungsmittel_fk'));
  7. $this->view->assign('
  8. sNahrungsmittelName', CAD_Tool_Extractor::extractOverPath($oNahrungsmittelXMasseinheitenRow, 'nahrungsmittel_name'));
  9.  
  10. $this->view->assign('sMasseinheitenDropDown', $sMasseinheitenDropDown);
  11. $this->view->assign('sMasseinheitenMapDropDown', $sMasseinheitenMapDropDown);
  12.  
  13. $sContent .= $this->view->render('loops/masseinheit-menge-map.phtml');


Erstellen eines leeren Eintrages in der Show Action:

PHP code

  1. $sMasseinheitenDropDown = $this->createMeasureDropDown();
  2. $sMasseinheitenMapDropDown = $this->createMeasureDropDown();
  3.  
  4. $this->view->assign('sMasseinheitenDropDown', $sMasseinheitenDropDown);
  5. $this->view->assign('sMasseinheitenMapDropDown', $sMasseinheitenMapDropDown);
  6.  
  7. $sContent = $this->view->render('loops/masseinheit-menge-map.phtml');


Wir brauchen also einmal die möglichkeit das Partial mit daten zu befüllen und einmal müssen wir nur ein leeres element erstellen:

PHP code

  1. protected function _createInputRow($oRow = null) {
  2. if (null !== $oRow) {
  3. $this->view->assign('sMasseinheitenDropDown', $this->_createMeasureDropDown($oRow->{'nahrungsmittel_x_masseinheit_fk'}));
  4. $this->view->assign('sMasseinheitenMapDropDown', $this->_createMeasureDropDown($oRow->{'nahrungsmittel_x_masseinheit_referenz_masseinheit_fk'}));
  5.  
  6. $this->view->assign('sDurchschnittsmenge', CAD_Tool_Extractor::extractOverPath($oRow, 'nahrungsmittel_x_masseinheit_durchschnittsmenge'));
  7. $this->view->assign('iNahrungsmittelFk', CAD_Tool_Extractor::extractOverPath($oRow, 'nahrungsmittel_x_masseinheit_nahrungsmittel_fk'));
  8. $this->view->assign('sNahrungsmittelName', CAD_Tool_Extractor::extractOverPath($oRow, 'nahrungsmittel_name'));
  9. } else {
  10. $this->view->assign('sMasseinheitenDropDown', $this->_createMeasureDropDown());
  11. $this->view->assign('sMasseinheitenMapDropDown', $this->_createMeasureDropDown());
  12.  
  13. $this->view->assign('sDurchschnittsmenge', '');
  14. $this->view->assign('iNahrungsmittelFk', '');
  15. $this->view->assign('sNahrungsmittelName', '');
  16. }
  17.  
  18. return $this->view->render('loops/masseinheit-menge-map.phtml');
  19. }


Damit haben wir erst einmal die möglichkeit eine Zeile der DB oder nichts zu übergeben und damit entsprechend zu reagieren. Des weiteren haben wir hier eine Unterscheidung zwischen einer Row gesetzt oder null und füllen dem entsprechend die Template Variablen mit den Settings oder leeren sie.

Wir erstellen so genau eine Zeile des Templates und geben diesen gerenderten Content zurück.

Aufgerufen wird diese Funktion wie folgt:

Aus der Index Action, die eine Liste der Daten generiert:

PHP code

  1. public function indexAction() {
  2. $oNahrungsmittelXMasseinheitenDbTable = new Application_Model_DbTable_NahrungsmittelXMasseinheit();
  3. $iMasseinheitMapId = CAD_Tool_Extractor::extractOverPath($this, 'getRequest->getParams->id');
  4. $sContent = '';
  5.  
  6. $oNahrungsmittelXMasseinheitenRowSet = $oNahrungsmittelXMasseinheitenDbTable->findNahrungsmittelXMeasureEntry($iMasseinheitMapId);
  7.  
  8. foreach ($oNahrungsmittelXMasseinheitenRowSet as $oNahrungsmittelXMasseinheitenRow) {
  9. $sContent .= $this->_createInputRow($oNahrungsmittelXMasseinheitenRow);
  10. }
  11.  
  12. $this->view->assign('sContent', $sContent);
  13. }


hier wird das RowSet durchlaufen und pro Zeile eine Input Zeile gerendert und an den Content gefügt.

Entsprechend ähnlich ist der Code jetzt in der Edit Action:

PHP code

  1. public function editAction() {
  2. $oNahrungsmittelXMasseinheitenDbTable = new Application_Model_DbTable_NahrungsmittelXMasseinheit();
  3. $iMasseinheitMapId = CAD_Tool_Extractor::extractOverPath($this, 'getRequest->getParams->id');
  4.  
  5. if (null !== $iMasseinheitMapId) {
  6. $oNahrungsmittelXMasseinheitenRow = $oNahrungsmittelXMasseinheitenDbTable-
  7. >findNahrungsmittelXMeasureEntry($iMasseinheitMapId);
  8. $this->view->assign('sContent', $this->_createInputRow($oNahrungsmittelXMasseinheitenRow));
  9. } else {
  10. $this->view->assign('sContent', $this->_createInputRow());
  11. }
  12. }


hier wird einmal die Zeile übergeben und damit der Content generiert, wird aber kein Element aus der Datenbank gelesen, wird hier eine leere Inputzeile gerendert und angezeigt.

Ich habe hier im Zuge des Abstraktion gleich noch die gerenderten Inhalten der _createInputField an die View übergeben, da hier ein extra persistieren in einer Variable nicht notwendig ist und nur wieder mehr Redundanz verursacht.

Wichtig ist bei einer Abstraktion aber immer das Augenmerk auf die Les und Wartbarkeit des Codes. Es ist in erster Linie wichtiger den Code wartbar und lesbar zu halten, als ihn maximal zu abstrahieren und so wenig wie möglich redundanz zu haben!

Ich hoffe ich konnte mit dem Beispiel dem einen oder anderen behilflich sein. Demnächst wird noch ein umfangreicheres Beispiel aus dem selben Projekt für den selben Zweck hier präsentiert, was aber umfangreicher ist. Dort wird aus verschiedenen Datenbanken mit der selben Struktur Content gezogen um damit Templates zu füllen.

Bis dahin