Quelle est la bonne façon de faire un appel AJAX en composant?

40

Je développe un composant personnalisé pour Joomla! 3.x et souhaitez faire un appel AJAX à l'intérieur pour récupérer des données. Quelle est la bonne façon de le faire?

Dmitry Rekun
la source
Un conseil important n'est jamais interrompre le flux de Joomla. Par exemple, peu de composants écoutent l'événement ajax request onAfterRoute et exécutent la tâche et suppriment la requête ici même. Cela cause des erreurs difficiles à déboguer.
Shyam
Voulez-vous dire - ne fermez pas une application? Pouvez-vous élaborer plus?
Dmitry Rekun
Oui, si joomla ferme l'application, ce sera mieux. L'extensibilité de votre extension sera donc maintenue.
Shyam
Je ne comprends toujours pas complètement. Ce dont je parle est $ app-> close () dans le contrôleur. Voulez-vous dire la même chose? :)
Dmitry Rekun
Oui, parler le même point. Pourquoi nous devrions fermer l'application dans le contrôleur, alors que joomla le fera lui-même.
Shyam

Réponses:

47

VEUILLEZ NOTER QUE CETTE RÉPONSE date déjà de quelques années et n’a pas été mise à jour. N'hésitez pas à éditer / commenter si vous pensez que quelque chose n'est plus exact.

Abstrait

Il n’existe pratiquement pas de solution officielle à ce problème, cela dépend en grande partie de la complexité et de la mesure dans laquelle vous souhaitez vous appuyer sur le modèle MVC pour effectuer le travail.

Vous trouverez ci-dessous quelques solutions possibles à ce qui devrait fonctionner dans Joomla 2.5 et 3.x. Le code n'est pas présenté pour un travail copier-coller, mais plutôt comme une idée générale.

Avant Joomla! 3.2 la seule chose dont vous avez besoin pour utiliser les exemples ci-dessous est un component. Après Joomla 3.2 (pour les tâches moins complexes), vous pouvez gérer les requêtes des modules et des plugins.


Réponse HTML générique (suivant MVC hérité)

Votre URL pour la tâche doit ressembler à ceci:

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

Vous créez ensuite le contrôleur qui utilisera la vue, par exemple Abc, qui contiendra le fichier view.raw.html (identique à un fichier de vue normale).

Vous trouverez ci-dessous le code permettant de générer une réponse HTML brute:

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

Remarque: C’est la solution que j’utiliserais si je devais retourner du code HTML (il est plus propre et respecte la logique de Joomla). Pour renvoyer des données JSON simples, voir ci-dessous comment tout mettre dans le contrôleur.

Sous-contrôleurs

Si vous faites votre demande Ajax à un sous - contrôleur , par exemple:

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

Que le nom de votre sous-contrôleur (pour la vue brute) doit être abc.raw.php.

Cela signifie également que vous aurez / pourrez avoir 2 sous-contrôleurs nommés Abc.

Si vous retournez JSON, il peut être judicieux d'utiliser format=jsonet abc.json.php. Dans Joomla 2.5. J'ai eu quelques problèmes à faire fonctionner cette option (la sortie était corrompue), alors j'ai utilisé raw.


Réponse JSON valide (après le nouveau / ancien legacy MVC)

Si vous devez générer une réponse JSON valide , consultez la page de documentation Génération d'une sortie 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);

Vous mettriez généralement ce code dans le contrôleur (vous appelerez un modèle qui renverra les données que vous encodez - un scénario très courant). Si vous avez besoin d'aller plus loin, vous pouvez également créer une vue JSON (view.json.php), similaire à l'exemple brut.


Sécurité

Maintenant que la requête Ajax fonctionne, ne fermez pas encore la page. Lire ci-dessous.

N'oubliez pas de vérifier les faux de demande. JSession::checkToken()être utile ici. Lisez la documentation sur Comment ajouter de l' anti-usurpation CSRF aux formulaires


Sites multilingues

Il peut arriver que si vous n'envoyez pas le nom de la langue dans la demande, Joomla ne traduira pas les chaînes de la langue souhaitée.

Pensez à ajouter en quelque sorte le paramètre lang à votre requête (du type &lang=de).


Joomla! Interface Ajax

Nouveau dans Joomla 3.2! - vous permet de faire des requêtes sans construire de composant

Joomla! Interface Ajax - Joomla fournit désormais un moyen léger de gérer les demandes Ajax dans un plugin ou un module. Vous voudrez peut-être utiliser le logiciel Joomla! Ajax Interface si vous ne possédez pas déjà de composant ou si vous devez faire des demandes à partir d'un module que vous avez déjà.

Valentin Despa
la source
9
Meilleure réponse de qualité que j’ai vue sur joomla.stackexchange.com jusqu’à présent - joliment fait et moyen de relever le niveau. Excellent travail!
NivF007
D'accord, mais qu'en est-il JRequest? C'est obsolète devrait-il être simplement $this->inputdepuis que j'utilise v3.x?
Dmitry Rekun
1
J'ai répondu à vos préoccupations concernant JRequest. Merci
Valentin Despa
3
Bonne réponse, je voulais juste mentionner qu'il existe une classe Joomla depuis la version 3.1 qui gère la sortie JSON: API , Utilisation
fruppel
@ fl0r yeap, Valentin l'a mentionné dans la Valid JSON Responsesection.
Dmitry Rekun
20

C'est une réponse tardive à cette question très bien répondue, mais je voulais ajouter cette solution parfaitement adaptée à ceux qui recherchent simplement un moyen simple d'obtenir les données de leurs composants avec un appel AJAX.

Avec toutes les versions de Joomla, les possibilités offertes par une tierce partie et les piratages que j'ai trouvés au cours de plusieurs jours passés sur google, c’était l’approche la plus simple que je pouvais proposer - et les retours sont définitivement appréciés.

  1. Fonction ajoutée executeà mon contrôleur principal existant
  2. Création d'un sous-contrôleur avec une fonction publique pour la ou les tâches que je voulais appeler avec AJAX
  3. Utilisation de la classe Joomla JResponseJson intégrée pour gérer la sortie ( c'est vraiment sympa! )

URL à appeler / exécuter la tâche:

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

Contrôleur principal modifié \ 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);
        }
    }
}

Nouveau sous-contrôleur \ 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();
    }
}

Rendu JSON rendu

{
    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
la source
11

La réponse de Valentin est bonne mais elle est un peu trop complexe si tout ce que vous avez à faire est d’ajouter 1 ou 2 appels ajax à un composant déjà construit. Il est parfaitement possible de se contenter de ne pas faire séparément controller.raw.phpou view.raw.phpfichiers.

Pour faire cet appel ajax

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

Dans le jobsous - contrôleur

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
la source
7

La réponse de Valentin est bonne.

Je préfère un contrôleur json qui gère l'encodage et la gestion des erreurs pour cela. J'ai créé une classe de 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();
  }

}

Ce contrôleur est étendu par la classe de contrôleur qui fait le travail, quelque chose comme ceci:

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

et vous appelez la requête comme ceci:

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

Le hachage du jeton est généré par JSession :: getFormToken (). Ainsi, l'appel complet complet pourrait ressembler à ceci:

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

Le second paramètre est défini sur "false" afin que nous puissions l'utiliser dans les appels javascript sans réécriture XML.

Harald Leithner
la source
1
Bien, mais pourquoi ne pas utiliser JResponseJsonclass pour le gérer?
Dmitry Rekun
JResponseJson a été introduit dans Joomla 3
Anibal le
Il n'y avait pas de Joomla SE où je pourrais demander;)
Harald Leithner
4

Si vous êtes sûr à 100% qu'il n'y a pas de plugin tiers qui ajoute une sortie Javascript, un json_encode pur fonctionne bien.

Mais ... par exemple, JomSocial ajoute "" à l'ensemble du site.

Alors ... un truc pratique, habillez json_encode avec des balises et traitez-le du côté Javascript.

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

Vous pouvez accéder directement à un contrôleur en utilisant le nom du contrôleur dans la tâche:

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

appellera: controller.raw.php (le retour est brut)

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

va appeler: controller.php (le retour est html si vous n'utilisez pas die;)

Dennis Heiden
la source