Как правильно сделать AJAX-вызов в компоненте?

40

Я разрабатываю пользовательский компонент для Joomla! 3.x и хотите сделать AJAX-вызов внутри него, чтобы получить некоторые данные. Как правильно это сделать?

Дмитрий Рекун
источник
Важный совет никогда не нарушать поток Joomla. Например, несколько компонентов isten ajax-запрос на событие onAfterRoute и выполнить задачу и убить запрос здесь сам. Это приводит к ошибкам, которые сложно отладить.
Шиам
Вы имеете в виду - не закрывать приложение? Можете ли вы рассказать подробнее?
Дмитрий Рекун
Да, если Joomla закроет приложение, оно будет лучшим. Таким образом, расширяемость вашего расширения будет сохранена.
Shyam
Все еще не понимаю полностью. То, о чем я говорю, это $ app-> close () в контроллере. Вы имеете в виду то же самое? :)
Дмитрий Рекун
Да, говорю то же самое. Почему мы должны закрывать приложение в контроллере, а то же самое будет делать сама Joomla.
Shyam

Ответы:

47

ОБРАТИТЕ ВНИМАНИЕ, ЭТОМУ ОТВЕТУ уже несколько лет, и он не был обновлен. Не стесняйтесь редактировать / комментировать, если считаете, что что-то уже не точное.

Аннотация

Практически нет официального способа справиться с этим, все зависит от сложности и от того, насколько вы хотите полагаться на шаблон MVC для выполнения работы.

Ниже приведены некоторые возможные решения, которые должны работать в Joomla 2.5 и 3.x. Код представлен не для задания копирования-вставки, а в качестве общей идеи.

До Joomla! 3.2 единственное, что вам нужно, чтобы использовать примеры ниже, это component. После Joomla 3.2 (для более низких сложных задач) вы можете обрабатывать запросы от модулей и плагинов.


Общий ответ HTML (в соответствии с устаревшим MVC)

Ваш URL для задачи должен выглядеть следующим образом:

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

AbcЗатем вы создаете контроллер, который будет использовать представление, скажем , которое будет содержать файл view.raw.html (идентичный обычному файлу представления).

Ниже у вас есть код для генерации необработанного 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";

Примечание: это решение я бы использовал, если бы мне пришлось возвращать HTML (он чище и следует логике Joomla). Для возврата простых данных JSON, смотрите ниже, как поместить все в контроллер.

Subcontrollers

Если вы отправите запрос Ajax субконтроллеру , например:

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

Чем должно быть ваше имя субконтроллера (для необработанного представления) abc.raw.php.

Это также означает, что у вас будет / может быть 2 субконтроллера с именем Abc.

Если вы возвращаете JSON, возможно, имеет смысл использовать format=jsonи abc.json.php. В Joomla 2.5. У меня были некоторые проблемы с получением этой опции (каким-то образом вывод был поврежден), поэтому я использовал raw.


Действительный ответ JSON (следующий новый / устаревший MVC)

Если вам необходимо сгенерировать правильный ответ JSON , проверьте страницу документы Генерирование 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);

Обычно вы помещаете этот код в контроллер (вы будете вызывать модель, которая будет возвращать закодированные вами данные - очень распространенный сценарий). Если вам нужно продвинуться дальше, вы также можете создать представление JSON (view.json.php), аналогичное необработанному примеру.


Безопасность

Теперь, когда Ajax-запрос работает, пока не закрывайте страницу. Читай ниже.

Не забудьте проверить подделку запроса. JSession::checkToken()пригодиться здесь. Прочитайте документацию о том, как добавить антиспуфинг CSRF в формы.


Многоязычные сайты

Может случиться так, что если вы не отправите имя языка в запросе, Joomla не переведет нужные вам языковые строки.

Попробуйте добавить параметр lang к вашему запросу (например &lang=de).


Joomla! Интерфейс Ajax

Новое в Joomla 3.2! - позволил вам обрабатывать запросы без сборки компонента

Joomla! Интерфейс Ajax - Joomla теперь предоставляет облегченный способ обработки запросов Ajax в плагине или модуле. Вы можете использовать Joomla! Ajax-интерфейс, если у вас еще нет компонента или если вам нужно делать запросы от модуля, который у вас уже есть.

Валентин Деспа
источник
9
Лучший качественный ответ, который я когда-либо видел на joomla.stackexchange.com - отлично сделано и способ поднять планку. Отличная работа!
NivF007
Согласитесь, а что JRequest? Это не рекомендуется, так $this->inputкак я использую v3.x?
Дмитрий Рекун
1
Я обратился к вашей озабоченности по поводу JRequest. Спасибо
Валентин Деспа
3
Хороший ответ, просто хотел упомянуть, что существует класс Joomla начиная с версии 3.1, который обрабатывает вывод JSON: API , использование
fruppel
@ fl0r да, Валентин упомянул об этом в Valid JSON Responseразделе.
Дмитрий Рекун
20

Это очень поздний ответ на этот очень хорошо отвеченный вопрос, но я хотел добавить это решение для тех, кому просто нужен простой способ получить данные своих компонентов с помощью вызова AJAX.

Со всеми версиями Joomla, сторонними возможностями и хакерами, которые я нашел за несколько дней поиска в Google, это был самый простой подход, который я мог придумать - и обратная связь ОПРЕДЕЛЕННО приветствуется.

  1. Добавлена ​​функция executeв мой существующий основной контроллер
  2. Создал субконтроллер с публичной функцией для задач, которые я хотел вызвать с помощью AJAX
  3. Использовал встроенный класс Joomla JResponseJson для обработки вывода ( это действительно хорошо! )

URL для вызова / выполнения задачи:

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

Модифицированный главный контроллер \ 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);
        }
    }
}

Новый субконтроллер \ 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();
    }
}

Визуализированный вывод 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"
    }
}
ВВП
источник
11

Ответ Валентина хороший, но он слишком сложный, если все, что вам нужно сделать, это добавить 1 или 2 вызова ajax к компоненту, который уже построен. Вполне возможно сойти с рук, не делая отдельные controller.raw.phpили view.raw.phpфайлы.

Чтобы сделать этот вызов AJAX

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

В 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();
    }
}
Spunkie
источник
7

Валентин ответил хорошо.

Я предпочитаю контроллер json, который обрабатывает кодирование и обработку ошибок, для этого я создал базовый класс 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();
  }

}

Этот контроллер расширяется классом контроллера, который выполняет работу, что-то вроде этого:

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

и вы называете запрос так:

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

Хэш токена генерируется JSession :: getFormToken (). Таким образом, полный завершенный вызов может выглядеть следующим образом:

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

Второй параметр имеет значение «false», поэтому мы можем использовать его в вызовах javascript без перезаписи xml.

Харальд Лейтнер
источник
1
Хорошо, но почему бы не использовать JResponseJsonкласс, чтобы справиться с этим?
Дмитрий Рекун
JResponseJson был представлен в Joomla 3
Anibal
Там не было Joomla SE, где я мог бы спросить;)
Харальд Лейтнер
4

Если вы на 100% уверены, что нет стороннего плагина, добавляющего какой-либо вывод Javascript, чистый json_encode работает нормально.

Но ... например, JomSocial добавляет "" ко всему сайту.

Итак ... удобный трюк, оберните json_encode тегами и обработайте его на стороне Javascript.

echo '@START@' . json_encode(...) . '@END@';
Анибал
источник
3

Вы можете получить доступ к контроллеру напрямую, используя имя контроллера в задаче:

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

вызовет: controller.raw.php (возврат необработанный)

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

вызовет: controller.php (возврат html, если вы не используете die;)

Деннис Хайден
источник