Was ist der richtige Weg, um einen AJAX-Aufruf in der Komponente zu tätigen?

40

Ich entwickle eine benutzerdefinierte Komponente für Joomla! 3.x und möchten einen AJAX-Aufruf ausführen, um einige Daten abzurufen. Was ist der richtige Weg, um es zu tun?

Dmitry Rekun
quelle
Wichtiger Hinweis: Unterbrechen Sie niemals den Fluss von Joomla. ZB isten einige Komponenten eine Ajax-Anfrage auf dem AfterRoute-Ereignis und erledigen die Aufgabe und beenden die Anfrage hier selbst. Dies führt zu Fehlern, die schwer zu debuggen sind.
Shyam
Meinen Sie damit - schließen Sie keine App? Können Sie mehr ausarbeiten?
Dmitry Rekun
Ja, wenn Joomla die App schließt, ist es am besten. So bleibt die Erweiterbarkeit Ihrer Erweiterung erhalten.
Shyam
Verstehe immer noch nicht ganz. Ich spreche von $ app-> close () im Controller. Meinst du das selbe :)
Dmitry Rekun
Ja, ich spreche den gleichen Punkt. Warum sollten wir die App im Controller schließen, während das selbe von Joomla selbst gemacht wird?
Shyam

Antworten:

47

BITTE BEACHTEN SIE, DASS DIESE ANTWORT bereits einige Jahre alt ist und nicht aktualisiert wurde. Fühlen Sie sich frei zu bearbeiten / kommentieren, wenn Sie denken, dass etwas nicht mehr genau ist.

Abstrakt

Es gibt praktisch keine offizielle Möglichkeit, damit umzugehen. Dies hängt stark von der Komplexität ab und davon, wie sehr Sie sich bei der Ausführung der Arbeit auf das MVC-Muster verlassen möchten.

Nachfolgend sind einige mögliche Lösungen aufgeführt, die in Joomla 2.5 und 3.x funktionieren sollten. Der Code wird nicht zum Kopieren und Einfügen, sondern als allgemeine Idee angezeigt.

Vor Joomla! 3.2 Das einzige, was Sie zur Verwendung der folgenden Beispiele benötigen, ist a component. Nach Joomla 3.2 (für weniger komplexe Aufgaben) können Sie Anfragen von Modulen und Plugins bearbeiten.


Generische HTML-Antwort (nach altem MVC)

Ihre URL für die Aufgabe muss folgendermaßen aussehen:

index.php?option=com_similar&task=abc&format=raw

Sie erstellen dann den Controller, der die Ansicht verwendet, beispielsweise Abcdie Datei view.raw.html (identisch mit einer normalen Ansichtsdatei).

Im Folgenden finden Sie den Code zum Generieren einer rohen HTML-Antwort:

/controller.php

public function abc() 
{
    // Set view

    // Joomla 2.5
    JRequest::setVar('view', 'Abc'); 

    // (use JInput in 3.x)
    $this->input->set('view', 'Abc');

    parent::display();
}

/views/abc/view.raw.php

<?php
defined('_JEXEC') or die;

jimport('joomla.application.component.view');

class SimilarViewAbc extends JViewLegacy
{
    function display($tpl = null)
    {
        parent::display($tpl);
    }
}

/views/abc/tmpl/default.php

<?php

echo "Hello World from /views/abc/tmpl/default.php";

Hinweis: Dies ist die Lösung, die ich verwenden würde, wenn ich HTML zurückgeben müsste (sie ist sauberer und folgt der Joomla-Logik). Informationen zum Zurückgeben einfacher JSON-Daten finden Sie unten, wie Sie alles in den Controller einfügen.

Subcontroller

Wenn Sie Ihre Ajax-Anfrage an einen Subcontroller richten , wie:

index.php?option=com_similar&controller=abc&format=raw

Dann muss der Name Ihres Subcontrollers (für die Rohansicht) lauten abc.raw.php.

Dies bedeutet auch, dass Sie 2 Subcontroller mit dem Namen Abc haben werden / können.

Wenn Sie JSON zurückgeben, ist es möglicherweise sinnvoll, format=jsonund zu verwenden abc.json.php. In Joomla 2.5. Ich hatte einige Probleme damit, diese Option zum Laufen zu bringen (irgendwie war die Ausgabe beschädigt), also habe ich raw verwendet.


Gültige JSON-Antwort (nach neuem / älterem MVC)

Wenn Sie eine gültige JSON-Antwort generieren müssen , lesen Sie die Dokumentationsseite JSON-Ausgabe generieren

// We assume that the whatver you do was a success.
$response = array("success" => true);
// You can also return something like:
$response = array("success" => false, "error"=> "Could not find ...");

// Get the document object.
$document = JFactory::getDocument();

// Set the MIME type for JSON output.
$document->setMimeEncoding('application/json');

// Change the suggested filename.
JResponse::setHeader('Content-Disposition','attachment;filename="result.json"');

echo json_encode($response);

Sie würden diesen Code im Allgemeinen in den Controller einfügen (Sie rufen ein Modell auf, das die von Ihnen codierten Daten zurückgibt - ein sehr häufiges Szenario). Wenn Sie noch weiter gehen möchten, können Sie auch eine JSON-Ansicht (view.json.php) erstellen, ähnlich wie im Rohbeispiel.


Sicherheit

Schließen Sie die Seite noch nicht, da die Ajax-Anfrage funktioniert. Lesen Sie unten.

Vergessen Sie nicht, nach Fälschungen zu suchen. JSession::checkToken()hier nützlich sein. Lesen Sie die Dokumentation zum Hinzufügen von CSRF-Antispoofing zu Formularen


Mehrsprachige Websites

Es kann vorkommen, dass Joomla die gewünschten Sprachzeichenfolgen nicht übersetzt, wenn Sie den Namen der Sprache in der Anfrage nicht senden.

Erwägen Sie, den Parameter lang an Ihre Anfrage anzuhängen (wie &lang=de).


Joomla! Ajax-Schnittstelle

Neu in Joomla 3.2! - Ermöglicht die Bearbeitung von Anfragen, ohne dass eine Komponente erstellt werden muss

Joomla! Ajax-Schnittstelle - Joomla bietet jetzt eine einfache Möglichkeit, Ajax-Anfragen in einem Plugin oder Modul zu bearbeiten. Vielleicht möchten Sie das Joomla! Ajax-Schnittstelle, wenn Sie noch keine Komponente haben oder Anforderungen von einem Modul stellen müssen, das Sie bereits haben.

Valentin Despa
quelle
9
Beste Antwortqualität, die ich bisher auf joomla.stackexchange.com gesehen habe - gut gemacht und die Messlatte höher gelegt. Exzellente Arbeit!
NivF007
Ich stimme zu, aber was ist JRequest? Es ist veraltet, sollte es einfach sein, $this->inputda ich v3.x verwende?
Dmitry Rekun
1
Ich habe Ihre Besorgnis bezüglich ... angesprochen JRequest. Vielen Dank
Valentin Despa
3
Schöne Antwort, wollte nur erwähnen, dass es seit 3.1 eine Joomla-Klasse gibt, die die JSON-Ausgabe verarbeitet: API , Usage
fruppel
@fl0r yeap, Valentin hat es im Valid JSON ResponseAbschnitt erwähnt.
Dmitry Rekun
20

Dies ist eine späte Antwort auf diese sehr gut beantwortete Frage, aber ich wollte diese auf den Punkt gebrachte Lösung für diejenigen hinzufügen, die nur eine einfache Möglichkeit benötigen, mit einem AJAX-Aufruf an die Daten ihrer Komponenten zu gelangen.

Mit all den Joomla-Versionen, Möglichkeiten von Drittanbietern und Hacks, die ich in mehreren Tagen des Googelns gefunden habe, war dies der einfachste Ansatz, den ich mir einfallen lassen konnte - und Feedback wird auf jeden Fall geschätzt.

  1. Funktion executezu meiner vorhandenen Hauptsteuerung hinzugefügt
  2. Erstellt einen Subcontroller mit einer öffentlichen Funktion für die Aufgabe (n), die ich mit AJAX aufrufen wollte
  3. Verwendete die eingebaute Joomla JResponseJson-Klasse, um die Ausgabe zu handhaben ( es ist wirklich schön! )

URL zum Aufrufen / Ausführen der Aufgabe:

www.mysite.com/index.php?option=com_example&task=ForAjax.mytaskname

Modifizierter Hauptcontroller \ com_example \ controller.php

class ExampleController extends JControllerLegacy {
    public function display($cachable = false, $urlparams = false) {
        $app = JFactory::getApplication();
        $view = $app->input->getCmd('view', 'default');
        $app->input->set('view', $view);
        parent::display($cachable, $urlparams);
        return $this;
    }

    public function execute()
    {
        // Not technically needed, but a DAMN good idea.  See http://docs.joomla.org/How_to_add_CSRF_anti-spoofing_to_forms
        // JSession::checkToken();
        $task = JFactory::getApplication()->input->get('task');
        try
        {
            parent::execute($task);
        }
        catch(Exception $e)
        {
            echo new JResponseJson($e);
        }
    }
}

Neuer Subcontroller \ com_example \ controller \ forajax.php

require_once JPATH_COMPONENT.'/controller.php';
class ExampleControllerForAjax extends ExampleController
{
    public function MyTaskName()
    {
        $app = JFactory::getApplication();

        $data['myRequest'] =$_REQUEST;
        $data['myFile'] =__FILE__;
        $data['myLine'] ='Line '.__LINE__;

        $app->enqueueMessage('This part was reached at line ' . __LINE__);
        $app->enqueueMessage('Then this part was reached at line ' . __LINE__);
        $app->enqueueMessage('Here was a small warning at line ' . __LINE__, 'warning');
        $app->enqueueMessage('Here was a big warning at line ' . __LINE__, 'error');

        $task_failed = false;
        echo new JResponseJson($data, 'My main response message',$task_failed);

        $app->close();
    }
}

Gerenderte JSON-Ausgabe

{
    success: true,
    message: "My main response message",
    messages: {
        message: [
            "This part was reached at line 26",
            "Then this part was reached at line 27"
        ],
        warning: [
            "Here was a small warning at line 28"
        ],
        error: [
            "Here was a big warning at line 29"
        ]
    },
    data: {
        myRequest: {
            option: "com_example",
            task: "mytaskname",
            Itemid: null
        },
        myFile: "C:\mysite\components\com_example\controllers\forajax.php",
        myLine: "Line 24"
    }
}
BIP
quelle
11

Die Antwort von Valentin ist gut, aber etwas zu komplex, wenn Sie nur 1 oder 2 Ajax-Aufrufe zu einer bereits erstellten Komponente hinzufügen müssen. Es ist durchaus möglich, weg mit nicht getrennt zu machen controller.raw.phpoder view.raw.phpDateien.

Um diesen Ajax-Anruf zu tätigen

index.php?format=raw&option=com_example&controller=job&task=keep_alive&tokenhash=1

Im jobSubcontroller

public function keep_alive() {
    $this->ajax_check();

    //Do your processing and echo out whatever you want to return to the AJAX call
    header('HTTP/1.1 202 Accepted', true, 202);
    echo 'OK';

    JFactory::getApplication()->close();
}

// Verifies jtoken and does a basic check that this is actually an AJAX call
private function ajax_check() {
    if(!JSession::checkToken('GET') || !isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') {
        header('HTTP/1.1 403 Forbidden', true, 403);
        JFactory::getApplication()->close();
    }
}
Spunkie
quelle
7

Valentins Antwort ist gut.

Ich bevorzuge einen JSON-Controller, der die Codierung und Fehlerbehandlung übernimmt. Ich habe eine JSON-Basisklasse erstellt:

class itrControllerJson extends JControllerLegacy {

  /** @var array the response to the client */
  protected $response = array();

  public function addResponse($type, $message, $status=200) {

    array_push($this->response, array(
      'status' => $status,
      'type' => $type,
      'data' => $message
    ));

  }

  /**
   * Outputs the response
   * @return JControllerLegacy|void
   */
  public function display() {

    $response = array(
      'status' => 200,
      'type' => 'multiple',
      'count' => count($this->response),
      'messages' => $this->response
    );

    echo json_encode($response);
    jexit();
  }

}

Dieser Controller wird um die Controller-Klasse erweitert, die die Arbeit erledigt:

require_once __DIR__.'json.php';

class componentControllerAddress extends itrControllerJson {
  public function get() {

    try {
      if (!JSession::checkToken()) {
        throw new Exception(JText::_('JINVALID_TOKEN'), 500);
      }
      $app = JFactory::getApplication();

      $id = $app->input->get('id', null, 'uint');
      if (is_null($id)) {
        throw new Exception('Invalid Parameter', 500);
      }

      $db = JFactory::getDbo();
      $query = $db->getQuery(true);
      $query->select('*');
      $query->from('#__table');
      $query->where('id = '.$db->quote($id));
      $db->setQuery($query);
      $response = $db->loadObject();

      $this->addResponse('message', $response, 200);

    } catch (Exception $e) {
      $this->addResponse('error', $e->getMessage(), 500);
    }

    $this->display();
  }
}

und du rufst die Anfrage so an:

index.php?option=com_component&task=address.get&format=json&id=1234&tokenhash=1

Der Token-Hash wird von JSession :: getFormToken () generiert. Der vollständige Aufruf könnte also so aussehen:

$link = JRoute::_('index.php?option=com_component&task=address.get&format=json&id=1234&'.JSession::getFormToken().'=1', false);

Der zweite Parameter ist auf "false" gesetzt, damit wir dies in Javascript-Aufrufen ohne xml-Neuschreibung verwenden können.

Harald Leithner
quelle
1
Schön, aber warum nicht mit JResponseJsonKlasse umgehen?
Dmitry Rekun
JResponseJson wurde in Joomla 3
Anibal am
Es gab keine Joomla SE, wo ich fragen konnte;)
Harald Leithner
4

Wenn Sie zu 100% sicher sind, dass es kein Plugin für Drittanbieter gibt, das Javascript-Ausgaben hinzufügt, funktioniert ein reiner json_encode.

Aber ... zum Beispiel fügt JomSocial "" der gesamten Site hinzu.

Also ... ein praktischer Trick, wickeln Sie json_encode in Tags ein und verarbeiten Sie ihn auf der Javascript-Seite.

echo '@START@' . json_encode(...) . '@END@';
Anibal
quelle
3

Sie können direkt auf einen Controller zugreifen, indem Sie den Controllernamen in der Task verwenden:

index.php?option=com_similar&task=controller.abc&format=raw

ruft auf: controller.raw.php (return ist raw)

index.php?option=com_similar&task=controller.abc

ruft auf: controller.php (return ist html wenn du es nicht verwendest die;)

Dennis Heiden
quelle