Jaki jest właściwy sposób na wywołanie AJAX w komponencie?

40

Tworzę niestandardowy komponent dla Joomla! 3.x i chcesz wykonać wywołanie AJAX w celu pobrania niektórych danych. Jaki jest właściwy sposób to zrobić?

Dmitry Rekun
źródło
Ważna rada nigdy nie przerywa przepływu Joomla. Np. Kilka komponentów isten ajax do zdarzeniaAfterRoute i wykonaj zadanie i sam je zabij. Powoduje to błędy, które są trudne do debugowania.
Shyam,
Masz na myśli - nie zamykaj aplikacji? Czy możesz opracować więcej?
Dmitry Rekun
Tak, jeśli joomla zamknie aplikację, będzie najlepiej. Dzięki temu rozszerzalność twojego rozszerzenia zostanie zachowana.
Shyam,
Nadal nie rozumiem całkowicie. Mówię o $ app-> close () w kontrolerze. Masz na myśli to samo? :)
Dmitry Rekun
Tak, mówiąc o tym samym. Dlaczego powinniśmy zamknąć aplikację w kontrolerze, podczas gdy to samo zrobi sama joomla.
Shyam,

Odpowiedzi:

47

PROSZĘ PAMIĘTAĆ, ŻE ODPOWIEDŹ ma już kilka lat i nie została zaktualizowana. Edytuj / komentuj, jeśli uważasz, że coś nie jest już dokładne.

Abstrakcyjny

Prawie nie ma naprawdę oficjalnego sposobu radzenia sobie z tym, to zależy od złożoności i od tego, jak bardzo chcesz polegać na wzorcu MVC.

Poniżej znajduje się kilka możliwych rozwiązań, które powinny działać w Joomla 2.5 i 3.x. Kod nie jest prezentowany dla zadania kopiowania-wklejania, ale jako ogólny pomysł.

Przed Joomla! 3,2 jedyną rzeczą, którą należy użyć poniższych przykładów jest component. Po Joomla 3.2 (dla mniej złożonych zadań) możesz obsługiwać żądania z modułów i wtyczek.


Ogólna odpowiedź HTML (zgodnie ze starszym MVC)

Twój adres URL zadania musi wyglądać następująco:

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

AbcNastępnie tworzysz kontroler, który będzie korzystał z widoku, powiedzmy , który będzie zawierał plik view.raw.html (identyczny z normalnym plikiem widoku).

Poniżej masz kod do generowania surowej odpowiedzi HTML:

/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";

Uwaga: jest to rozwiązanie, którego użyłbym, gdybym musiał zwrócić HTML (jest czystszy i postępuje zgodnie z logiką Joomla). Aby zwrócić proste dane JSON, zobacz poniżej, jak umieścić wszystko w kontrolerze.

Podwykonawcy

Jeśli przesyłasz żądanie Ajax do podwykonawcy , na przykład:

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

Nazwa twojego podwykonawcy (dla surowego widoku) musi być abc.raw.php.

Oznacza to również, że będziesz / mógł mieć 2 subskrybentów o nazwie Abc.

Jeśli zwrócisz JSON, warto użyć format=jsoni abc.json.php. W Joomla 2.5. Miałem pewne problemy z uruchomieniem tej opcji (jakoś wynik był uszkodzony), więc użyłem raw.


Prawidłowa odpowiedź JSON (w związku z nowym / starszym MVC)

Jeśli chcesz wygenerować prawidłową odpowiedź JSON , sprawdź stronę z dokumentami Generowanie danych wyjściowych JSON

// 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);

Zazwyczaj umieszczasz ten kod w kontrolerze (wywołujesz model, który zwróci kodowane dane - bardzo częsty scenariusz). Jeśli chcesz pójść dalej, możesz również utworzyć widok JSON (view.json.php), podobny do surowego przykładu.


Bezpieczeństwo

Teraz, gdy żądanie Ajax działa, nie zamykaj jeszcze strony. Czytaj poniżej.

Nie zapomnij sprawdzić fałszywych zapytań. JSession::checkToken()przydaj się tutaj. Przeczytaj dokumentację na temat dodawania funkcji CSRF do fałszowania formularzy


Witryny wielojęzyczne

Może się zdarzyć, że jeśli nie wyślesz nazwy języka we wniosku, Joomla nie przetłumaczy żądanych ciągów językowych.

Zastanów się, czy w jakiś sposób nie dołączyć do swojego żądania lang param (jak &lang=de).


Joomla! Interfejs Ajax

Nowości w Joomla 3.2! - umożliwiło wykonywanie żądań obsługi bez budowania komponentu

Joomla! Interfejs Ajax - Joomla zapewnia teraz lekki sposób obsługi żądania Ajax we wtyczce lub module. Możesz użyć Joomla! Interfejs Ajax, jeśli nie masz jeszcze komponentu lub jeśli chcesz wysyłać żądania z modułu, który już masz.

Valentin Despa
źródło
9
Najlepsza jakość odpowiedzi, jaką do tej pory widziałem na joomla.stackexchange.com - ładnie zrobione i sposób na podniesienie poprzeczki. Świetna robota!
NivF007
Zgadzam się, ale co z tym JRequest? Jest przestarzałe, czy powinno to być po prostu, $this->inputponieważ używam v3.x?
Dmitry Rekun
1
Odpowiedziałem na twoje obawy dotyczące JRequest. Dzięki
Valentin Despa,
3
Ładna odpowiedź, chciałem tylko wspomnieć, że istnieje klasa Joomla od wersji 3.1, która obsługuje dane wyjściowe JSON: API , użycie
fruppel
@ fl0r yeap, Valentin wspomniał o tym w Valid JSON Responserozdziale.
Dmitry Rekun
20

To późna odpowiedź na to pytanie, na które bardzo dobrze odpowiedziano, ale chciałem dodać to bezpośrednie rozwiązanie dla tych, którzy potrzebują prostego sposobu na uzyskanie dostępu do danych swoich komponentów za pomocą wywołania AJAX.

Ze wszystkimi wersjami Joomla, możliwościami stron trzecich i hackami, które znalazłem w ciągu kilku dni googlingu, było to najprostsze podejście, jakie mogłem wymyślić - i opinie są zdecydowanie doceniane.

  1. Dodano funkcję executedo mojego istniejącego głównego kontrolera
  2. Utworzyłem subkontrolera z funkcją publiczną dla zadań, które chciałem wywołać za pomocą AJAX
  3. Użyłem wbudowanej klasy Joomla JResponseJson do obsługi danych wyjściowych ( to naprawdę miłe! )

Adres URL do wywołania / wykonania zadania:

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

Zmodyfikowany główny kontroler \ 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);
        }
    }
}

Nowy subkontroler \ com_example \ kontrolers \ 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();
    }
}

Renderowane wyjście JSON

{
    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"
    }
}
PKB
źródło
11

Odpowiedź Valentin jest dobra, ale jest nieco zbyt złożona, jeśli wszystko, co musisz zrobić, to dodać 1 lub 2 wywołania ajax do już zbudowanego komponentu. Zupełnie możliwe jest uniknięcie tworzenia osobnych plików controller.raw.phplub view.raw.phpplików.

Aby wykonać to wywołanie ajax

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

W jobsubkontrolerze

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
źródło
7

Odpowiedź Valentina jest dobra.

Wolę kontroler json, który obsługuje kodowanie i obsługę błędów. Utworzyłem klasę podstawową json:

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();
  }

}

Ten kontroler zostaje rozszerzony o klasę kontrolera, która działa, mniej więcej tak:

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();
  }
}

i wywołujesz takie żądanie:

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

Skrót tokenów jest generowany przez JSession :: getFormToken (). Kompletne pełne połączenie może wyglądać następująco:

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

Drugi parametr jest ustawiony na „false”, dzięki czemu możemy go używać w wywołaniach javascript bez przepisywania xml.

Harald Leithner
źródło
1
Fajnie, ale dlaczego nie skorzystać z JResponseJsonklasy, aby sobie z tym poradzić?
Dmitry Rekun
JResponseJson został wprowadzony w Joomla 3
Anibal
Nie było Joomla SE, w którym mógłbym zapytać;)
Harald Leithner
4

Jeśli masz 100% pewności, że żadna wtyczka Thrid-Party nie dodaje żadnych danych wyjściowych JavaScript, czysty kod_json działa.

Ale ... na przykład JomSocial dodaje „” do całej witryny.

Więc ... przydatna sztuczka, zawiń json_encode tagami i przetwarzaj go po stronie Javascript.

echo '@START@' . json_encode(...) . '@END@';
Anibal
źródło
3

Możesz uzyskać dostęp do kontrolera bezpośrednio, używając nazwy kontrolera w zadaniu:

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

wywoła: controller.raw.php (return is raw)

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

wezwie: controller.php (powrót jest html, jeśli nie używać die;)

Dennis Heiden
źródło