¿Cuál es la forma correcta de hacer una llamada AJAX en componente?

40

Estoy desarrollando un componente personalizado para Joomla! 3.x y desea hacer una llamada AJAX dentro de él para recuperar algunos datos. ¿Cuál es la forma correcta de hacerlo?

Dmitry Rekun
fuente
Un consejo importante es nunca romper el flujo de Joomla. Por ejemplo, pocos componentes atienden la solicitud ajax en el evento AfterRoute y realizan la tarea y eliminan la solicitud aquí mismo. Esto causa errores que son difíciles de depurar.
Shyam
¿Quieres decir que no cierras una aplicación? ¿Puedes elaborar más?
Dmitry Rekun
Sí, si Joomla cierra la aplicación, será lo mejor. Entonces se mantendrá la extensibilidad de su extensión.
Shyam
Aún no lo entiendo completamente. De lo que estoy hablando es de $ app-> close () en el controlador. ¿Quieres decir lo mismo? :)
Dmitry Rekun
Sí, hablando el mismo punto. Por qué deberíamos cerrar la aplicación en el controlador, mientras que Joomla lo hará.
Shyam

Respuestas:

47

TENGA EN CUENTA QUE ESTA RESPUESTA ya tiene algunos años y no se actualizó. Siéntase libre de editar / comentar si cree que algo ya no es exacto.

Abstracto

Casi no hay una forma realmente oficial de lidiar con esto, depende mucho de la complejidad y de cuánto desee confiar en el patrón MVC para hacer el trabajo.

A continuación se presentan algunas posibles soluciones que deberían funcionar en Joomla 2.5 y 3.x. El código no se presenta para un trabajo de copiar y pegar, sino más bien como una idea general.

Antes de Joomla! 3.2 lo único que necesita para usar los ejemplos a continuación es a component. Después de Joomla 3.2 (para tareas de menor complejidad) puede manejar la solicitud de módulos y complementos.


Respuesta HTML genérica (siguiendo MVC heredado)

Su URL para la tarea debe tener este aspecto:

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

AbcEntonces, cree el controlador que usará la vista, digamos , que contendrá el archivo view.raw.html (idéntico a un archivo de vista normal).

A continuación tiene el código para generar una respuesta HTML sin procesar:

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

Nota: Esta es la solución que usaría si tuviera que devolver HTML (es más limpio y sigue la lógica de Joomla). Para devolver datos JSON simples, consulte a continuación cómo colocar todo en el controlador.

Subcontroladores

Si realiza su solicitud de Ajax a un subcontrolador , como:

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

De lo que debe ser el nombre de su subcontrolador (para la vista sin formato) abc.raw.php.

Esto significa también que tendrá / puede tener 2 subcontroladores llamados Abc.

Si devuelve JSON, puede tener sentido usar format=jsony abc.json.php. En Joomla 2.5. Tuve algunos problemas para que esta opción funcionara (de alguna manera el resultado estaba dañado), así que utilicé raw.


Respuesta JSON válida (siguiente MVC nuevo / heredado)

Si necesita generar una respuesta JSON válida , consulte la página de documentos Generar salida 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);

En general, pondría este código en el controlador (llamará a un modelo que devolverá los datos que codifica, un escenario muy común). Si necesita llevarlo más lejos, también puede crear una vista JSON (view.json.php), similar al ejemplo en bruto.


Seguridad

Ahora que la solicitud de Ajax está funcionando, no cierre la página todavía. Lee abajo.

No olvide verificar las falsificaciones de solicitudes. JSession::checkToken()ven a la mano aquí. Lea la documentación sobre cómo agregar CSRF anti-spoofing a los formularios


Sitios multilingües

Puede suceder que si no envía el nombre del idioma en la solicitud, Joomla no traducirá las cadenas de idioma que desea.

Considere agregar de alguna manera el parámetro lang a su solicitud (como &lang=de).


Joomla! Interfaz Ajax

¡Nuevo en Joomla 3.2! - le permitió realizar solicitudes de manejo sin construir un componente

Joomla! Interfaz Ajax : Joomla ahora proporciona una forma ligera de manejar la solicitud de Ajax en un complemento o módulo. Es posible que desee utilizar Joomla! Interfaz Ajax si aún no tiene un componente o si necesita realizar solicitudes desde un módulo que ya tiene.

Valentin Despa
fuente
9
La mejor respuesta de calidad que he visto en joomla.stackexchange.com hasta ahora: muy bien hecho y forma de elevar el listón. ¡Excelente trabajo!
NivF007
De acuerdo, pero ¿qué pasa JRequest? Está en desuso, ¿debería ser simplemente $this->inputporque uso v3.x?
Dmitry Rekun
1
Abordé su preocupación con respecto JRequest. Gracias
Valentin Despa
3
Buena respuesta, solo quería mencionar que hay una clase de Joomla desde 3.1 que maneja la salida JSON: API , Uso
fruppel
@ fl0r yeap, Valentin lo mencionó en la Valid JSON Responsesección.
Dmitry Rekun
20

Esta es una respuesta tardía para esta pregunta muy bien respondida, pero quería agregar esta solución de corte directo para aquellos que solo necesitan una forma simple de acceder a los datos de sus componentes con una llamada AJAX.

Con todas las versiones de Joomla, las posibilidades de terceros y los hacks que encontré durante varios días de búsqueda en Google, este fue el enfoque más simple que pude encontrar, y los comentarios son DEFINITIVAMENTE apreciados.

  1. Función agregada executea mi controlador principal existente
  2. Creé un subcontrolador con una función pública para las tareas que quería llamar con AJAX
  3. Usé la clase integrada Joomla JResponseJson para manejar la salida (¡ es realmente agradable! )

URL para llamar / ejecutar la tarea:

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

Controlador principal modificado \ 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);
        }
    }
}

Nuevo subcontrolador \ com_example \ controllers \ 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();
    }
}

Salida JSON representada

{
    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"
    }
}
PIB
fuente
11

La respuesta de Valentin es buena, pero es un poco compleja si todo lo que necesita hacer es agregar 1 o 2 llamadas ajax a un componente que ya está construido. Es perfectamente posible salirse con no hacer por separado controller.raw.phpo view.raw.phparchivos.

Para hacer esta llamada ajax

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

En el jobsubcontrolador

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
fuente
7

La respuesta de Valentin es buena.

Prefiero un controlador json que maneje la codificación y el manejo de errores para esto. Creé una clase base 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();
  }

}

Este controlador se extiende por la clase de controlador que hace el trabajo, algo como esto:

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

y llamas a la solicitud así:

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

El hash de token se genera mediante JSession :: getFormToken (). Entonces, la llamada completa completa podría verse así:

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

El segundo parámetro se establece en "falso" para que podamos usar esto en llamadas de JavaScript sin reescritura xml.

Harald Leithner
fuente
1
Bien, pero ¿por qué no usar la JResponseJsonclase para manejarlo?
Dmitry Rekun
JResponseJson se presentó en Joomla 3
Anibal el
No había Joomla SE donde pudiera preguntar;)
Harald Leithner
4

Si está 100% seguro de que no hay ningún complemento de terceros que agregue ninguna salida de Javascript, un json_encode puro funciona bien.

Pero ... por ejemplo, JomSocial agrega "" a todo el sitio.

Entonces ... un truco útil, envuelva json_encode con etiquetas y proceselo en el lado de Javascript.

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

Puede acceder a un controlador directamente utilizando el nombre del controlador en la tarea:

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

llamará a: controller.raw.php (el retorno es sin formato)

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

llamará a: controller.php (el retorno es html si no lo usa die;)

Dennis Heiden
fuente