Ergebnis 1 bis 6 von 6

Thema: Model: Results um eigene Funktionen erweitern

  1. #1
    Contao-Nutzer Avatar von chatjack
    Registriert seit
    02.09.2012.
    Ort
    Essen
    Beiträge
    162

    Standard Model: Results um eigene Funktionen erweitern

    Hallo,

    ich bin zurzeit dabei in einer Erweiterung konsequent Models für alle angelecten DCAs einzusetzen und bin immer noch absolut verblüfft, wie unnötig umständlich ich bisher mit Datenbankabfragen umgegangen bin.

    Inzwischen habe ich etwas Blut geleckt und frage mich, in wieweit sich die Models noch optimieren lassen. Konkret geht es hier um die Erweiterung von Results um eigene Funktionen, und wie ich das am elegantesten realisiert bekomme.

    Ich habe an einer Stelle beispielsweise im DCA per MultiColumnWizard die Möglichkeit hinzugefügt, Jahre auszuwählen.

    year_select.png

    Das Ganze wird in der Datenbank serialisiert gespeichert. Da ich es an verschiedenen Stellen als sauberes Array benötige, habe ich das Model hier um eine entsprechende Funktion erweitert.

    PHP-Code:
    public static function years($id)
    {
        
    $election self::findById($id);
        
    $years unserialize($election->years);
        return 
    $years array_map(function($year){return $year["years"];},$years) : null;

    Auf diese Weise kann ich mir für eine Wahl die Jahre einfach folgendermaßen ausgeben lassen.

    PHP-Code:
    ElectionModel::years(1); 
    Soweit so gut. Nun möchte ich das Ganze aber sehr gerne auch auf Results übertragen. Beispielsweise in diesem Fall.

    PHP-Code:
    $election ElectionModel::findByPid(0);
    while ( 
    $election->next() ) {
      
    $years $election->years;

    Hier bekomme ich die Jahre wieder als serialisierten String zurück. Um das Ganze wieder durch die Funktion laufen zu lassen, könnte ich natürlich wie folgt vorgehen.

    PHP-Code:
    $election ElectionModel::findByPid(0);
    while ( 
    $election->next() ) {
      
    $yearsArr ElectionModel::years($election->id);

    Das finde ich aber etwas unschön. Lieber würde ich es so lösen.

    PHP-Code:
    $election ElectionModel::findByPid(0);
    while ( 
    $election->next() ) {
      
    $yearsArr $election->yearsArr;

    $election->yearsArr würde in diesem Falle eine Funktion anstoßen die prüft, ob pid gleich 0 ist und das Jahr direkt aus der Spalte "years" verarbeiten und erst im Falle, dass pid nicht gleich 0 ist wieder auf die Funktion "years" zurück greifen.

    Ich kann im Model direkt ja einfach eigene Variablen hinzufügen.

    PHP-Code:
    public function __get($value) {
      if ( 
    $value == "yearsArr" ) {
        return 
    meine_funktion();
      }
      return 
    parent::__get($value);

    Das wirkt sich aber scheinbar nicht auf die Results aus. Hat jemand eine Idee, wie ich das bewerkstelligen kann, dass es sich im Idealfall auf ElectionModel::findById(), ElectionModel::findByPid(), ElectionModel::findAll() u.s.w. auswirkt?

    Vermutlich ganz einfach und ich steh nur auf dem Schlauch. Aber ich wollte lieber einmal nachfragen, bevor ich hier etwas ähnlich unnötig umständliches baue wie die bisherigen Datenbankabfragen auf die DCA-Tabellen.

    Vielen Dank und beste Grüße,
    Dennis
    Geändert von chatjack (21.04.2017 um 12:26 Uhr)

  2. #2
    Contao-Nutzer Avatar von miniA4kuser
    Registriert seit
    14.11.2013.
    Beiträge
    130

    Standard

    Du kannst doch bei der Zuweisung in ein Array umwandeln:
    PHP-Code:
    $years deserialize($election->yearstrue); 
    Oder auch gleich ein Feld innerhalb des Arrays, sofern die Felder bekannt bzw. konstant sind:
    PHP-Code:
    $years deserialize($election->yearstrue)[0]; 
    Oder eine eigene Model Methode die dir deine Felder als Array zurückgibt:
    PHP-Code:
    public static function findByIdDeserialized($strId null)
    {
        if (
    null === $strId)
        {
            return 
    null;
        }

        
    $t = static::$strTable;
        
    $arrColumns = array("$t.id=?");
        
    $arrValues = array($strId);

        
    $objRows = static::findBy($arrColumns$arrValues);

        if (
    null === $objRows)
        {
            return 
    null;
        }

        foreach (
    $objRows as &$objRow)
        {
            
    $objRow->years deserialize($objRow->yearstrue);
        }
        return 
    $objRows;

    Dabei musst du nur beachten nicht unbedarft die save()-Methode aufzurufen, ohne zuvor die entsprechenden Felder wieder zu serialisieren
    Tippfehler unterliegen nicht dem Copyright und können frei weitergegeben werden.

  3. #3
    Contao-Nutzer Avatar von chatjack
    Registriert seit
    02.09.2012.
    Ort
    Essen
    Beiträge
    162

    Standard

    Das wäre dann, wenn ich das richtig versanden habe im Endeffekt diese Lösung.

    PHP-Code:
    $election ElectionModel::findByPid(0); 
    while ( 
    $election->next() ) { 
      
    $yearsArr ElectionModel::years($election->id);

    Ich habe das Gefühl, mir immer noch zu viele Insellösungen zu bauen anstatt sauber auf Kernfeatures des Systems zu setzen.

    Beim Graben durch GitHub bin ich jetzt auf eine interessante Funktion gestoßen, die aber scheinbar noch nirgendwo zum Einsatz kommt.

    https://github.com/contao/core/blob/...ntao/Model.php

    PHP-Code:
    /**
     * Modify the database result before the model is created
     *
     * @param \Database\Result $objResult The database result object
     *
     * @return \Database\Result The database result object
     */
    protected static function postFind(\Database\Result $objResult)
    {
        return 
    $objResult;

    Mit dem Überschreiben der Funktion könnte man sich das Result-Objekt also scheinbar einfach anpassen und den zusätzlichen Wert direkt hinein schreiben. Leider ist mir der korrekte Umgang mit dem Result-Objekt noch nicht ganz klar. Hier ein kleiner Testlauf.

    PHP-Code:
    protected static function postFind($results)
    {
        
    $results->first(); // Wähle zum Testen den ersten Eintrag des Results
        
    $results->yearsArr "Klappt!"// Schreibe in die Variable "yearsArr"
        
    var_dump($results->yearsArr); // => "Klappt!"

        
    $results->first(); // Wähle noch einmal den ersten Eintrag des Results
        
    var_dump($results->yearsArr); // => NULL
        
        
    return $results;

    Aus irgendeinem Grund wird der Wert nicht korrekt im Objekt gehalten. Oder interpretiere ich hier den Sinn der postFind einfach völlig falsch?

    Viele Grüße,
    Dennis
    Geändert von chatjack (21.04.2017 um 15:26 Uhr)

  4. #4
    Contao-Nutzer Avatar von chatjack
    Registriert seit
    02.09.2012.
    Ort
    Essen
    Beiträge
    162

    Standard

    Ich habe es dann jetzt erst einmal über eine separate Funktion im Model gelöst.

    Über postFind lassen sich die Results allem Anschein nach nur dann vernünftig anpassen, solange es sich nicht um Collections handelt. Das widerspricht natürlich dem Konzept der Rundumlösung, die sowohl mit findById (Result) als auch mit findByPid (ResultCollection) funktionieren soll.

    Falls dennoch mal jemand mit postFind gearbeitet hat, wäre ich zum besseren Verständnis für ein Beispiel aus dem Produktiveinsatz sehr dankbar.

  5. #5
    Contao-Nutzer Avatar von miniA4kuser
    Registriert seit
    14.11.2013.
    Beiträge
    130

    Standard

    Ein Model repräsentiert ja einen Datensatz aus der Datenbank. Wenn dort also in einem Feld ein serialisierter Wert gespeichert wird, so sollte dann beim iterieren des Results auch ein serialisierter Wert ausgegeben werden. So mein Verständnis zu den Models, so wie sie auch im Core benutzt werden.

    Für spezielle Ausgaben oder Datensatzzähllungen werden im Core bei den einzelnen Models ja zusatzliche Abfragefunktionen definiert, daher mein Ansatz Beitrag #2


    Zum Überschreiben der postFind()-Funktion macht mir im ersten Moment mal Bauchschmerzen, dass das ja dann bei jedem Model angewendet wird, also müsste man dann ja vorher prüfen ob das Feld existiert etc pp...

    Zitat Zitat von chatjack Beitrag anzeigen
    Ich habe es dann jetzt erst einmal über eine separate Funktion im Model gelöst.
    Und wie genau jetzt? Kannst das mal posten?
    Tippfehler unterliegen nicht dem Copyright und können frei weitergegeben werden.

  6. #6
    Contao-Nutzer Avatar von chatjack
    Registriert seit
    02.09.2012.
    Ort
    Essen
    Beiträge
    162

    Standard

    Ich habe das Model jetzt um eine möglichst flexible Funktion zur Rückgabe der Jahre erweitert.

    PHP-Code:
        // FIND YEARS
        
    public static function years($value)
        {
            
    // IS IT AN OBJECT?
            
    if ( gettype($value) == "object" ) {
                
    $years $value->pid self::root($value->id)->years $value->years;
            
    // IS IT AN ID?
            
    } else if ( gettype($value) != "array" && preg_match('/^\d+$/',$value) ) {
                
    $years self::root($value)->years;
            
    // IT SEEMS TO BE A SERIALIZED STRING OR AN ARRAY
            
    } else {
                
    $years $value;
            }
            
    // TRY TO DESERIALIZE IF ITS NOT AN ARRAY
            
    $yearsArr = ( gettype($years) == "array" ) ? $years unserialize($years);
            
    // CLEAN THE ARRAY
            
    return $yearsArr preg_grep("/^\d{4}$/",array_unique(array_map(function($year){return $year["years"];},$yearsArr))) : null;
        } 
    Diese nimmt jetzt sowohl eine ID des Datensatzes, das Objekt des Datensatzes, einen serialisierten String oder ein Array (z.B. im Falle der Übergabe im "save_callback" innerhalb eines Feldes) entgegen. Auf diese Weise kann ich die Funktion an verschiedensten Stellen einsetzen. Da die Datensätze in einer Baumstruktur angeordnet sind, und immer nur der oberste Datensatz die Jahre enthalten kann, wird über self::root der oberste Datensatz zurück gegeben.

    Ansprechen kann ich das Ganze jetzt über ...

    PHP-Code:
      ElectionModel::years(1);
      
    // ... oder ...
      
    ElectionModel::years($dcaObj);
      
    // ... oder ...
      
    ElectionModel::years('a:2:{i:0;a:1:{s:5:"years";s:4:"2017";}i:1;a:1:{s:5:"years";s:4:"2012";}}');
      
    // ... oder ...
      
    ElectionModel::years(Array(Array("years"=>"2017"),Array("years"=>"2012"))); 
    Geändert von chatjack (24.04.2017 um 15:57 Uhr)

Aktive Benutzer

Aktive Benutzer

Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1)

Lesezeichen

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •