web-dev-qa-db-fra.com

Quelle est la bonne façon de faire un appel AJAX dans le composant?)?

Je développe un composant personnalisé pour Joomla! 3.x et souhaitez effectuer un appel AJAX à l'intérieur de celui-ci pour récupérer des données. Quelle est la manière appropriée de le faire?

40
Dmitry Rekun

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 aucun moyen officiel de régler ce problème, cela dépend en grande partie de la complexité et du degré de dépendance du 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, disons 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 (c’est plus propre et il suit la logique 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 , comme:

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

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=json Et 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 la documentation Génération de la 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 devez 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() est utile ici. Lisez la documentation sur Comment ajouter 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 (comme &lang=de).


Joomla! Interface Ajax

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

Interface Joomla! 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 avez besoin de faire des requêtes à partir d'un module que vous avez déjà.

47
Valentin Despa

Ceci 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'accéder aux 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. Ajout de la fonction 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! )

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

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

sortie JSON rendue

{
    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"
    }
}
20
GDP

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 tout à fait possible de ne pas séparer controller.raw.php ou view.raw.php des dossiers.

Pour faire cet appel ajax

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

Dans le sous-contrôleur job

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

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.

7
Harald Leithner

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@';
4
Anibal

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 (return is raw)

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

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

3
Dennis Heiden