Zum Inhalt springen

Welche Funktion in welche Klasse?

Erstellt am 19. Mai 2011 · 15 Antworten · letzte Antwort am 24. Mai 2011

Tags: Frage

xandio ·

Die Autoren des Buches "Zukunftssichere TYPO3-Extensions mit Extbase und Fluid" beschreiben die Aufgabenverteilung wie folgt:

"Extbase (und FLOW3) verfolgt den Ansatz eines schlanken Controllers (slim controller). Der Controller ist ausschließlich für die Steuerung des Ablaufs zuständig. Weitere Logik (insbesondere Geschäfts- bzw. Domänenlogik) wird in die Klassen im Unterordner Domain ausgelagert."

Eydamos ·

Das hast du größtenteils schon richtig verstanden.

Der Controller ist die Schnittstelle zwischen der Datenverarbeitung (Model) und der Ausgabe (View).

Das Repository greift aber nicht auf Daten des Models zu. Das Repository holt selbst direkt die Daten aus der Datenbank. Anschließend legt es, basierend auf der Struktur die das Model vorgibt, Objekte an.
Diese Objekte übergibt der Controller dann an die View wo fluid die Daten basierend auf dem Funktionen des Models die Daten ausgeben kann.

Unbekannter Benutzer ·

@xandio:
Danke für das Zitat. Das Buch liegt 20 cm links von mir und ganz ehrlich: für jemanden, der mit extbase und fluid bzw. mit Domain Driven Design noch nicht viel am Hut hatte ist das Buch, gelinde gesagt, nicht zu empfehlen. Es wird viel erklärt und vieles nur halb. Ich stand schon einige Male da und dachte: "Und wo gehört das jetzt hin?!". Der rote Faden fehlt einfach.

@Eydamos:
Ok das macht die Sache doch schon klarer. Danke 🙂

xandio ·

Mir gefällt das Buch und ich betrachte es als wichtige Informationsquelle für diese (noch) schlecht dokumentierte Technologie. Einige Lösungen habe ich ausschließlich in diesem Buch gefunden.

Mit dem Zitat wollte ich lediglich ausdrücken, dass man nicht sämtliche Geschäftslogik in den Controller packen und das Model lediglich auf Getter- und Setter-Methoden reduzieren sollte. Ist aber natürlich Ansichtssache. :-)

Unbekannter Benutzer ·

Hi,

ich hab nun mein Fallbeispiel von oben erfolgreich umgesetzt, bin mir aber nicht ganz sicher ob das ne gute Lösung ist. Könntet ihr mir bite sagen ob man das besser machen kann?

Nochmal kurz das Fallbeispiel: ich möchte herausfinden, welche Datensätze in den letzten vier Wochen angelegt wurden und diese dann als "NEU" kennzeichnen.

Also, in meinem Model (Object.php) hab ich eine Variable $new definiert (mit getNew() und setNew() Funktion). Diese Variable wird nur zur Laufzeit genutzt (hat also kein Datenbankfeld).

Das ObjectRepository habe ich um zwei Funktionen erweitert, die mir die neuen und alten Datensätze getrennt liefern. Hier passiert die Überprüfung des "Alters" der Datensätze:

<?php
class Tx_MyExt_Domain_Repository_ObjectRepository extends Tx_Extbase_Persistence_Repository {
    
    /**
     * Timeframe within a object will be marked as NEW (4 Weeks = 2419200 Seconds)
     * 
     * @var int
     */
    private $timeframe = 2419200;
    
    /**
     * Finds all objects that were created less than 4 weeks ago from now
     * 
     * @return Tx_MyExt_Domain_Model_Object
     */
    public function findNewObjects() {
        $query = $this->createQuery();
        $query->matching($query->greaterThanOrEqual('crdate', time() - $this->timeframe));
        $query->setOrderings(array('crdate' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING));
        return $query->execute();
    }
    
    /**
     * Finds all objects that were created more than 4 weeks ago from now
     * 
     * @return Tx_MyExt_Domain_Model_Object
     */
    public function findOldObjects() {
        $query = $this->createQuery();
        $query->matching($query->lessThan('crdate', time() - $this->timeframe));
        $query->setOrderings(array('crdate' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING));
        return $query->execute();
    }
    
}
?>

Im ObjectController durchlaufe ich dann beide Arrays und setze $new auf "1" bzw. auf "0":

<?php
/**
 * List all objects
 * 
 * @return string The rendered HTML string
 */
public function listAction() {
    // $allObjects = $this->objectRepository->findAll();
    $allObjects = array();
    
    $newObjects = $this->objectRepository->findNewObjects();
    foreach($newObjects as $object) {
        $object->setNew(1);
    }
    
    $oldObjects = $this->objectRepository->findOldObjects();
    foreach($oldObjects as $object) {
        $object->setNew(0);
    }
    
    $allObjects = array_merge($newObjects, $oldObjects);
    
    $this->view->assign('objects', $allObjects);
    
}
?>

Im View (list.html) prüfe ich den Wert der Variable $new und gebe, falls der Wert auf 1 steht, den Text "NEU" aus:

<f:if condition="{object.new}">
    <span>NEU</span>
</f:if>

Geht das nicht einfacher? Ich bin diesen Weg gegangen, weil ich nicht weiß wie ich schon im Repository $new auf "1" bzw. auf "0" setzen kann. Wenn ich das wüsste könnte ich im Repository alles auf eine Datenbankabfrage reduzieren und der Controller müsste nichts weiter tun als das zurückgegebene Array an den View zu übergeben.

Wie kann ich das bewerkstelligen?

a.​berl ·

Ich würde das anders lösen:

<?php
/**
 * List all objects
 * 
 * @return string The rendered HTML string
 */
public function listAction() {
    $allObjects = $this->objectRepository->findAll();
    $this->view->assign('objects', $allObjects);

    $this->view->assign('time', time() - 2419200);
}
?>

und dann die "Logik" dafür in die View bauen:

<f:if condition="{object.crdate} > {time}">
    <span>NEU</span>
</f:if>

Das entspricht zwar nicht ganz einem sauberen MVC Ansatz, macht den Gesamtcode aber übersichtlicher und das ist IMO wichtiger als das Festklammern an Patterns und Paradigmen.

PS: Sollte object.crdate einen Datums-String zurückgeben (bin mir nicht ganz sicher), dann hilft es dafür einfach einen neuen Getter ins Model zu schreiben, dass das crdate als Timestamp zurück gibt.

Unbekannter Benutzer ·

@a.berl:

vielen Dank für deinen Vorschlag. Ich brauche die Unterscheidung zwischen alten und neuen Datensätzen jedoch nicht nur im View sondern muss da an anderer Stelle noch drauf zugreifen können.

Außerdem interessiert es mich, wie man das nach MVC Standard sauber lösen könnte 🙂

Edit:
Das Datenbankfeld "crdate" ist ja das Standard-Datenbankfeld von TYPO3. Wie kann ich denn im Controller darauf zugreifen, ohne es im Model als extra Variable definiert zu haben?

Edit 2:
Ok ich hab spaßeshalber einfach mal folgendes im Model hinzugefügt:

/**
 * @var int
 */
protected $crdate;

public function getCrdate() {
    return $this->crdate;
}

Wenn ich im Controller mir das Object mittels t3lib_div::debug() ausgeben lasse, steht nun folgendes drin:

[crdate] => DateTime Object
    (
    )

Wie kann ich denn auf so ein DateTime Object zugreifen und es z.B: als Timestamp ausgeben?

a.​berl ·
Typo3Cuckoo schrieb

@a.berl:

vielen Dank für deinen Vorschlag. Ich brauche die Unterscheidung zwischen alten und neuen Datensätzen jedoch nicht nur im View sondern muss da an anderer Stelle noch drauf zugreifen können.

Außerdem interessiert es mich, wie man das nach MVC Standard sauber lösen könnte 🙂

In dem Fall würde ich das Model einfach erweitern. Im Grunde ist das ja einfach eine Eigenschaft deines Modells - ist es Neu oder nicht? - die nur über den Zustand eines anderen Feldes definiert ist.

/**
 * @var int
 */
protected $crdate;

public function getNew() {
    return $this->crdate > (time() - 2419200);
}

Einen Setter brauchst du in dem Fall nicht, da es nur eine zu Lesende Eigenschaft ist und auch nicht in der Datenbank persistiert wird. Über diesen Getter kannst du aber von überall auf diese Eigenschaft deines Models zugreifen.

Edit:
Das Datenbankfeld "crdate" ist ja das Standard-Datenbankfeld von TYPO3. Wie kann ich denn im Controller darauf zugreifen, ohne es im Model als extra Variable definiert zu haben?

Edit 2:
Ok ich hab spaßeshalber einfach mal folgendes im Model hinzugefügt:

/**
 * @var int
 */
protected $crdate;

public function getCrdate() {
    return $this->crdate;
}

Wenn ich im Controller mir das Object mittels t3lib_div::debug() ausgeben lasse, steht nun folgendes drin:

[crdate] => DateTime Object
    (
    )

Wie kann ich denn auf so ein DateTime Object zugreifen und es z.B: als Timestamp ausgeben?

Standardmäßig ist das Feld nicht vom Model aus zugänglich, da es eigentlich ein Typo3-internes Feld ist, deswegen musst du dem Model noch den Member $crdate geben.
Ist das crdate Feld in der TCA für dein Model enthalten? Ansonsten funktioniert das mapping vom Datenbankfeld auf das Model nicht und der Wert bleibt leer.

	'crdate' => array(
		'exclude' => 0,
		'label'   => 'LLL:EXT:lang/locallang_general.xml:LGL.crdate',
		'config'  => array(
			'type' => 'input',
			'size' => 12,
			'max' => 20,
			'eval' => 'datetime',
			'checkbox' => '1',
			'default' => '0'
		)
	),
Unbekannter Benutzer ·

Ok, also ich hab das Model einfach erweitert und das funktioniert soweit auch ganz gut (siehe Edit2 😉).

crdate habe ich in der ext_tables.php wie folgt drinstehen:

$TCA['tx_myext_domain_model_object'] = array (
    
	'ctrl' => array (
		'crdate' => 'crdate',
                ...
        );

Ich bekomme das Datum in View auch ausgeben mit:

<f:format.date format="d.m.Y">{object.crdate}</f:format.date>

Die Daten sind also vorhanden, es funktioniert also auch ohne dein Vorschlag:

'crdate' => array(
		'exclude' => 0,
		'label'   => 'LLL:EXT:lang/locallang_general.xml:LGL.crdate',
		'config'  => array(
			'type' => 'input',
			'size' => 12,
			'max' => 20,
			'eval' => 'datetime',
			'checkbox' => '1',
			'default' => '0'
		)
	),

Würde mir das nicht außerdem die Möglichkeit bieten, crdate direkt im BE bearbeiten zu können? Das sollte ja eigentlich nicht möglich sein,

Meine Frage bzgl. des DateTime Objects war eher so gemeint, dass ich in PHP damit nicht weiter arbeiten kann. Wie kann ich in PHP aus dem DateTime Object einen Timestamp generieren, so dass ich in PHP z.B. einen Vergleich zwischen zwei DateTime Objects machen kann?

a.​berl ·
Typo3Cuckoo schrieb

Würde mir das nicht außerdem die Möglichkeit bieten, crdate direkt im BE bearbeiten zu können? Das sollte ja eigentlich nicht möglich sein,

Stimmt, das sollte dann natürlich ein exclude-Feld sein, war ungünstig zusammengepastet 🙂 Aber du hast auch Recht, mit 'crdate' => 'crdate' im 'ctrl' funktioniert es wohl auch so.

Meine Frage bzgl. des DateTime Objects war eher so gemeint, dass ich in PHP damit nicht weiter arbeiten kann. Wie kann ich in PHP aus dem DateTime Object einen Timestamp generieren, so dass ich in PHP z.B. einen Vergleich zwischen zwei DateTime Objects machen kann?

getTimestamp ist hier dein Freund 😉

Unbekannter Benutzer ·

getTimestamp() funktioniert leider nicht 🙁

Wenn ich das mache, bekomm ich gar keine Ausgabe mehr (Browserfenster bleibt weiß):

$date = $object->getCrdate();
t3lib_div::debug($date->getTimestamp(), '$object->getTimestamp()');

Und wenn ich das so versuche

$date = new DateTime($object->getDateStart());
t3lib_div::debug($date->getTimestamp(), '$object->getTimestamp()');

erhalte ich folgende Fehlermeldung:

Exception: DateTime::__construct() expects parameter 1 to be string, object given

Was mache ich falsch?

a.​berl ·

Schau doch bitte mal was get_class/is_int jeweils bei getCrdate() und getDateStart() zurückliefert. Ich vermute ersteres ist bereits ein Timestamp und letzteres ein DateTime Objekt.

Unbekannter Benutzer ·

Gesagt, getan 😉

Im Model ist $crdate und $dateStart wie folgt deklariert:

<?php
/**
 * @var DateTime
 */
protected $dateStart;

/**
 * @var int
 */
protected $crdate;
?>

Ausgabe von get_class() und is_int() mit:

<?php
// $crdate
t3lib_div::debug(get_class($object->getCrdate()), 'get_class($object->getCrdate())');
t3lib_div::debug(is_int($object->getCrdate()), 'is_int($object->getCrdate())');

// $dateStart
t3lib_div::debug(get_class($object->getDateStart()), 'get_class($object->getDateStart())');
t3lib_div::debug(is_int($object->getDateStart()), 'is_int($object->getDateStart())');
?>

ergibt:

get_class($object->getCrdate())
|DateTime|

is_int($object->getCrdate())
| debug |

get_class($object->getDateStart())
|DateTime|

is_int($object->getDateStart())
| debug |

Rückgabe "debug" heißt in dem Fall "FALSE", $crdate und $dateStart sind also keine Integer sondern DateTime Objekte.

Wie sieht es mit der Deklarierung im Model aus? Muss die Variable vom Typ int oder vom Typ DateTime sein? Die Datenbankfelder habe ich beide als int deklariert, weil das Datum in der Datenbank als Timestamp gespeichert wird.

Edit:

Ausgabe von get_class_methods($object->getCrdate()):

get_class_methods($object->getCrdate())
 0 	__construct
 1 	format
 2 	modify
 3 	getTimezone
 4 	setTimezone
 5 	getOffset
 6 	setTime
 7 	setDate
 8 	setISODate

DateTime::getTimestamp() gibts also gar nicht?! Kann man denn mit der DateTime::format() einen Timestamp ausgeben?

Edit 2:

DateTime::getTimestamp() gibts ab PHP 5.3,x und bei mir läuft 5.2.9. Das is wohl des Rätsels Lösung! Gibts dafür vielleicht ne Alternative in PHP 5.2.9?

Edit 3:

Ok ich habs raus! Schön wenn man seine eigenen Fragen selbst beantworten kann haha 😃
Folgendes gibt mir ein DateTimeObject als Timestamp aus:

$date = $object->getCrdate();
$timestamp = strtotime($date->format('Y-m-d H:i:s'));