Qual é a maneira correta de fazer uma chamada AJAX no componente?

40

Estou desenvolvendo um componente personalizado para o Joomla! 3.xe deseja fazer uma chamada AJAX dentro dele para recuperar alguns dados. Qual é a maneira correta de fazer isso?

Dmitry Rekun
fonte
Um conselho importante nunca é interromper o fluxo do Joomla. Por exemplo, poucos componentes isten ajax request onAfterRoute e executam a tarefa e eliminam a solicitação aqui. Isso causa erros difíceis de depurar.
Shyam
Você quer dizer - não feche um aplicativo? Você pode elaborar mais?
Dmitry Rekun
Sim, se o joomla fechar o aplicativo, será melhor. Portanto, a extensibilidade da sua extensão será mantida.
Shyam 23/04
Ainda não entendo completamente. O que eu estou falando é $ app-> close () no controlador. Você quer dizer o mesmo? :)
Dmitry Rekun
Sim, falando o mesmo ponto. Por que devemos fechar o aplicativo no controlador, enquanto o mesmo será feito pelo próprio joomla.
Shyam 23/04

Respostas:

47

Observe que esta resposta já tem alguns anos e não foi atualizada. Sinta-se livre para editar / comentar se você acha que algo não é mais exato.

Abstrato

Quase não existe uma maneira oficial de lidar com isso, depende muito da complexidade e do quanto você deseja confiar no padrão MVC para fazer o trabalho.

Abaixo estão algumas soluções possíveis que devem funcionar no Joomla 2.5 e 3.x. O código não é apresentado para um trabalho de copiar e colar, mas como uma idéia geral.

Antes do Joomla! 3.2 a única coisa que você precisa para usar os exemplos abaixo é a component. Após o Joomla 3.2 (para tarefas menos complexas), você pode lidar com solicitações de módulos e plugins.


Resposta HTML genérica (após MVC herdado)

Seu URL para a tarefa precisa ter a seguinte aparência:

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

Você criou o controlador que usará a visualização, digamos Abc, que conterá o arquivo view.raw.html (idêntico a um arquivo de visualização normal).

Abaixo você tem o código para gerar uma resposta HTML bruta:

/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 é a solução que eu usaria se tivesse que retornar HTML (é mais limpo e segue a lógica do Joomla). Para retornar dados JSON simples, veja abaixo como colocar tudo no controlador.

Subcontroladores

Se você fizer sua solicitação do Ajax para um subcontrolador , como:

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

Do que o nome do seu subcontratado (para a visualização bruta) precisa ser abc.raw.php.

Isso significa também que você terá / poderá ter 2 subcontroladores chamados Abc.

Se você retornar JSON, pode fazer sentido usar format=jsone abc.json.php. No Joomla 2.5. Eu tive alguns problemas para que essa opção funcionasse (de alguma forma, a saída foi corrompida), então usei raw.


Resposta JSON válida (após o MVC novo / legado)

Se você precisar gerar uma resposta JSON válida , consulte a página de documentos Gerando saída 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);

Você geralmente colocaria esse código no controlador (você chamará um modelo que retornará os dados que você codifica - um cenário muito comum). Se você precisar ir além, também poderá criar uma visualização JSON (view.json.php), semelhante ao exemplo bruto.


Segurança

Agora que a solicitação do Ajax está funcionando, não feche a página ainda. Leia abaixo.

Não se esqueça de procurar falsificações de pedidos. JSession::checkToken()venha a calhar aqui. Leia a documentação em Como adicionar anti-spoofing CSRF aos formulários


Sites multilíngues

Pode acontecer que, se você não enviar o nome do idioma na solicitação, o Joomla não traduzirá as seqüências de caracteres desejadas.

Considere anexar de alguma forma o parâmetro lang à sua solicitação (como &lang=de).


Joomla! Interface Ajax

Novo no Joomla 3.2! - permitiu que você fizesse solicitações de manipulação sem criar um componente

Joomla! Interface Ajax - O Joomla agora oferece uma maneira leve de lidar com a solicitação do Ajax em um plug-in ou módulo. Você pode querer usar o Joomla! Interface Ajax se você ainda não possui um componente ou se precisa fazer solicitações de um módulo que já possui.

Valentin Despa
fonte
9
Resposta da melhor qualidade que já vi no joomla.stackexchange.com até agora - muito bem feita e maneira de elevar a fasquia. Excelente trabalho!
NivF007
Concordo, mas e quanto JRequest? Ele está obsoleto, deve ser simplesmente $this->inputdesde que eu uso a v3.x?
Dmitry Rekun
11
Abordei sua preocupação em relação a JRequest. Obrigado
Valentin Despa
3
Resposta Nice, apenas queria dizer que há uma classe Joomla desde 3.1 que alças JSON de saída: API , Usage
fruppel
@ fl0r sim, Valentin mencionou na Valid JSON Responseseção.
Dmitry Rekun
20

Esta é uma resposta tardia para esta pergunta muito bem respondida, mas eu queria adicionar esta solução simples para aqueles que precisam apenas de uma maneira simples de acessar os dados de seus componentes com uma chamada AJAX.

Com todas as versões do Joomla, possibilidades de terceiros e hacks que eu encontrei ao longo de vários dias pesquisando no Google, essa foi a abordagem mais simples que eu pude criar - e o feedback é DEFINATAMENTE apreciado.

  1. Função adicionada executeao meu controlador principal existente
  2. Criou um subcontrolador com uma função pública para as tarefas que eu queria chamar com o AJAX
  3. Utilizou a classe JResponseJson incorporada no Joomla para lidar com a saída ( é muito bom! )

URL para chamar / executar a tarefa:

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

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

Saída JSON renderizada

{
    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
fonte
11

A resposta do Valentin é boa, mas é um pouco complexa, se tudo o que você precisa fazer é adicionar 1 ou 2 chamadas ajax a um componente que já foi criado. É perfeitamente possível evitar não criar arquivos controller.raw.phpou view.raw.phparquivos separados .

Para fazer esta chamada ajax

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

No 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
fonte
7

A resposta de Valentin é boa.

Eu prefiro um controlador json que lida com a codificação e manipulação de erros para isso, criei uma classe 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();
  }

}

Esse controlador é estendido pela classe do controlador que faz o trabalho, algo como isto:

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

e você chama a solicitação assim:

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

O hash do token é gerado por JSession :: getFormToken (). Portanto, a chamada completa completa pode ser assim:

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

O segundo parâmetro é definido como "false" para que possamos usá-lo em chamadas javascript sem reescrever xml.

Harald Leithner
fonte
11
Legal, mas por que não usar JResponseJsonclasse para lidar com isso?
Dmitry Rekun
O JResponseJson foi introduzido no Joomla 3
Anibal
Não houve Joomla SE onde eu poderia pedir;)
Harald Leithner
4

Se você tem 100% de certeza de que não há um plug-in thrid-party que adicione qualquer saída Javascript, um json_encode puro funciona.

Mas ... por exemplo, o JomSocial adiciona "" a todo o site.

Então ... um truque útil, envolva json_encode com tags e processe-o no lado do Javascript.

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

Você pode acessar um controlador diretamente usando o nome do controlador na tarefa:

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

chamará: controller.raw.php (o retorno é bruto)

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

chamará: controller.php (o retorno é html se você não usar die;)

Dennis Heiden
fonte