Publish-Methode des Controllers aus CLI-Script oder Helper aufrufen

  • Guten Tag,ich finde nun nach langer Zeit etwas Freizeit, um mich mit der Gestaltung meiner Komponente weiter auseinander zu setzen.Ich habe hier folgendes Ziel: Ein Datensatz ("Item") soll durch ein CLI-Script eine Status-Änderung (von 0 auf 1) durchlaufen. Im Controller des Items finden einige Prüfungen statt und gleichzeitig werden noch andere Prozesse angesteuert. Dies möchte ich natürlich gerne "recyceln". Daher wäre es schön, die Publishing-Methode inklusive des Tasks aus einer Methode in dem CLI-Script (oder einem Helper, der durch das CLI-Script aufgerufen wird) anzusteuern.Mein Ziel wäre also etwas, das so aussehen könnte:

    Code
    $itemcontroller->publish($task);

    Der Controller selbst beinhaltet Code, der so beschrieben werden könnte (ich kürze ihn zur besseren Lesbarkeit):

    Code
    /*** publish function.* * @access public* @return void*/public function publish(){$app = JFactory::getApplication();$jinput = JFactory::getApplication()->input;$ids = $jinput->get('cid', '', 'array');$task = $this->getTask();$date = JFactory::getDate(); $modelitem = $this->getModel('Item'); foreach($ids as $id){switch($task){case 'publish' : // do somethingbreak; case 'unpublish': // do somethingbreak; case 'archive' : // do somethingbreak; case 'trash' : // do somethingbreak; }$publish = $modelitem->publish($id, $state); $this->setRedirect('index.php?option=com_bestia&view=items', false);return true;}

    Ich freue mich hier über jeden Input :)

  • Hallo Re:Later


    Vielen Dank für Deine Antwort - mein CLI Script befindet sich bereits im Framework und ist schon in der Lage, Models und Helper zu laden und zu verwenden. Ich würde halt nur gerne auch einen Eintrag über die CLI "publishen" - und zwar unter Aufruf der Methode im Controller, weil ja dort noch "Nebenschauplätze" integriert sind.


    P.S. Ich habe folgendes gefunden: http://stackoverflow.com/quest…ontroller-in-joomla-3-1-1


    Die Frage ist: Ist das der "elegante Weg"? Oder würdet ihr das anders machen?

    Einmal editiert, zuletzt von ehrenwert ()

  • Besten Dank, Christine!


    Das habe ich tatsächlich auch schon gelesen - allerdings freunde ich mich mit der Lösung nicht richtig an: Im Controller wird ein Model geladen und dort wird die Publish-Methode implementiert?


    Für mich klingt das unsauber. Allerdings lasse ich mich hier natürlich auch gerne belehren.

  • Leider kann ich meinen eigenen Beitrag nicht mehr editieren - daher nun ein neuer Beitrag: Eine Möglichkeit wäre, das wurde aus der Stackoverflow-Antwort ja deutlich, die eigentliche Methode auszulagern (ich würde hierfür dann einen Helper nutzen). Ich hätte mir das wie unten dargestellt vorgestellt. Das ist jetzt etwas schnell und ohne künstlerischen Anspruch erstellt (und an die Pfeile müssten noch "ID" und "TASK" angefügt werden). Meine Frage: Wäre das eine "saubere" Lösung im Sinne des Joomla! MVC?

  • Zitat

    Im Controller wird ein Model geladen und dort wird die Publish-Methode implementiert?
    Für mich klingt das unsauber.


    Hab jetzt die Links alle nicht gelesen, aber eigentlich ist das die grundlegende Denke in einer Joomla-Legacy-MVC-Architektur oder wie heißt. Der Controller erhält den Befehl/Task, "von außen sozusagen", übergibt an Model, das die Logik ausführt, wofür es natürlich auch geladen sein muss. An Controller gehen Erfolgs- und Error-Meldungen zurück, der damit über weiteres Vorgehen entscheidet.
    Der View, der Model ebenfalls für "Geschäftslogik" nutzt/nutzen kann, ist bei dir ja erst mal außen vor.


    Das angenehme dabei in Joomla ist halt, dass vieles davon die Parent-Klassen schon im Hintergrund erledigen. Spricht aber nichts dagegen im Controller der Komponente A jedwedes andere Model der Komponente B zu laden und zu nutzen.
    ---------------
    Nebenbei ist ja letztlich das der Kritikpunkt am Legacy-MVC, dass es gelegentlich viel zu viele Methoden in den Parentklassen lädt, die oft gar nicht genutzt werden.

  • Hm, dann wäre es also sinnvoll, den Weg über den Controller zu gehen? Das wäre natürlich auch für mich ideal, weil ich dann wenig umstellen müsste.


    Die Frage ist nur: Wie steuere ich das aus einem Helper heraus an? Ich rufe ja keine URL auf, wo ich dann über "task=irgendetwas" etwas ansteuern würde.


    Der Aufruf geschieht ja über das CLI-Script, und das muss irgendwie den Controller des Items aufrufen und die Publish-Methode aufrufen.

  • So ungefähr:


    Die URL (bzw. REQUEST-Daten) ist ja nur ein Umweg. URL kommt per Browser an. Joomla-Router ermittelt daraus die Komponente und erstellt eine Instanz des zugehörigen HAUPT-Controllers im jeweiligen Erweiterungs-Verzeichnis.


    Das ist "normales PHP". HAUPT-Controller-Klasse laden und Instanz per "$controller = new Klassenname" bereitstellen.


    Abschließend Methode execute() dieses Komponenten-Controllers aufrufen. Und gesammelte REQUEST-Daten u.a. der Controller-Instanz übermitteln


    $controller = new $class($this->input, $this->app);
    ...
    $controller->execute();


    So weit der Router...


    Task (ja oft nicht mehr als eine PHP-Methode) und View in der URL entscheidet dann der Komponenten-Controller, was er damit tut.


    View und Task holt er sich aus den app->input-Daten, vom Router aufbereitet, schon nicht mehr aus der URL direkt.


    // Instanz eines eigenen Komponenten-Untercontrollers, der wiederum eine Parentklasse extended.
    $controller = JControllerLegacy::getInstance('Content');


    // Und ruft irgendeine Methode des Untercontrollers auf. Üblicherweise die execute(), was aber nicht zwingend ist.
    $controller->execute($input->get('task'));



    .....
    $controller->getModel();
    $controller->getView();
    $controller->publish();
    usw.


    Was eben Controller und Parent-Klassen zur Verfügung stellen.


    Kurz: Komponenten-Controller laden/Instanz erstellen. Methoden direkt nutzen.


    Nichts verbietet dir, ein Model direkt zu laden und Methoden darin ohne Umweg Controller zu nutzen. Sind ja alles nur PHP-Klassen mit mehr oder weniger Methoden.


    Oder Helferklassen, die wiederum eine Controller-Klasse extenden.


    Oder...

  • So, ich habe nun den Spaß soweit bekommen, dass ich die Publishing-Methode aus dem Controller ausgelagert habe, bzw. die Publishing-Methode im Controller zwar nutze, diese jedoch in einem Helper verarbeite.


    Das CLI-Script soll dies ebenfalls tun.


    Soweit funktioniert auch alles - bis zu dem Punkt, bei dem $model->publish($id, $status) aufgerufen wird.


    Unter der CLI erhalte ich hier "false" als Rückgabe (Status wird auch nicht geändert), wenn ich das gleiche über das Web-Frontend aufrufe, erhalte ich ein "true" (Status wird geändert).


    Irgendwelche Ideen?

  • Hilft nur schrittweise durch die Klassen mit Debug-Zeile mit print_r und exit bis du den Grund fürs false hast.
    exit ist halt oft nötig, da z.B. bei einem redirect die print_r-Ausgabe verloren geht.


    Aber immerhin bekommst ja schon mal ein boolean false zurück, wenn ich dich richtig verstehe. Kein Fatal Error, Null o.ä.

  • Ich habe jetzt einmal die Publishing-Methode im Model überschrieben:


    Code
    public function publish(&$pks, $value = 1){    // Initialise variables.    $dispatcher = JDispatcher::getInstance();    $user = JFactory::getUser();    $table = $this->getTable();    $pks = (array) $pks;    // Include the content plugins for the change of state event.    JPluginHelper::importPlugin('content');    // Access checks.    foreach ($pks as $i => $pk)    {        $table->reset();        var_dump($this->canEditState($table)); die;        if ($table->load($pk))        {            if (!$this->canEditState($table))            {                                   // Prune items that you can't change.                unset($pks[$i]);                JError::raiseWarning(403, JText::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'));                return false;            }        }    }    // Attempt to change the state of the records.    if (!$table->publish($pks, $value, $user->get('id')))    {        $this->setError($table->getError());        return false;    }    $context = $this->option . '.' . $this->name;    // Trigger the onContentChangeState event.    $result = $dispatcher->trigger($this->event_change_state, array($context, $pks, $value));    if (in_array(false, $result, true))    {        $this->setError($table->getError());        return false;    }    // Clear the component's cache    $this->cleanCache();    return true;}


    Mir fällt hier auf, dass ich an der Stelle

    Code
    var_dump($this->canEditState($table)); die;

    NULL zurück bekomme. Er geht also davon aus, dass das CLI-Script hier gar keine Rechte zum Editieren hat, verstehe ich das richtig?


    Wie könnte ich das ändern?

  • Leider kann ich meinen Beitrag nicht mehr editieren.


    Ich habe testweise einmal folgendes auskommentiert:



    Nun funktioniert es - es ist aber natürlich eher unschön. Hat da vielleicht noch jemand Ideen, wie ich das CLI-Script so ausführen kann, dass this->canEditState nicht NULL zurück gibt? Wahrscheinlich muss das CLI-Script eine Admin-Usersession starten - doch trotz Google bin ich bisher nicht darauf gekommen, wie das funktionieren kann.

    • Hilfreich

    Weiß auch nicht, vielleicht wer anders, aber


    mein Gedanke wäre:
    Wenn ich beim Ausführen des CLI-Scripts automatisch eine Admin-Session starte, um Rechte zu bekommen, kommt das ja aufs selbe raus wie die Prüfung auf Rechte gleich komplett rauszunehmen oder die Rechteabfrage (canEditState) für CLI-Ausführung zu erweitern.


    Frage wäre dann bzgl. Sicherheit, wer kann das CLI-Script ausführen? Gibt man ihm vielleicht eigene "Login-Daten" mit, die zusätzlich geprüft werden, um über true oder false zu entscheiden? Darf sowieso nur ein root o.ä.?


    Nur der Beginn einer Stoffsammlung...

  • Ich habe das nun folgendermaßen gelöst: Ich habe tatsächlich die Prüfung dahingehend erweitert, dass geprüft wird, ob der Nutzer die Rechte hat (hat er dann ja nicht) oder ob das Script als CLI ausgeführt wird (wird es).


    Da die Installation auf einem eigenen vServer liegt ist es vermutlich auf diese Weise in Ordnung.


    Ich danke vielmals für die geduldige Hilfe :)