컴포넌트에서 AJAX 호출을 수행하는 올바른 방법은 무엇입니까?


40

Joomla를위한 커스텀 컴포넌트를 개발 중입니다! 3.x이며 AJAX를 호출하여 일부 데이터를 검색하려고합니다. 올바른 방법은 무엇입니까?


중요한 조언은 Joomla의 흐름을 끊지 않습니다. 예를 들어 구성 요소 ajax 요청 onAfterRoute 이벤트가 거의 없으며 작업을 수행하고 여기에서 요청을 종료하십시오. 이로 인해 디버깅하기 어려운 오류가 발생합니다.
Shyam

앱을 닫지 않습니까? 더 정교하게 할 수 있습니까?
Dmitry Rekun

예, joomla가 앱을 닫으면 가장 좋습니다. 따라서 확장의 확장 성이 유지됩니다.
Shyam

여전히 완전히 이해하지 못합니다. 내가 말하는 것은 컨트롤러의 $ app-> close ()입니다. 당신도 같은 의미입니까? :)
Dmitry Rekun

예, 같은 말을합니다. 컨트롤러에서 앱을 닫아야하는 이유는 무엇입니까?
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 데이터를 반환하려면 컨트롤러에 모든 것을 넣는 방법을 아래에서 참조하십시오.

서브 컨트롤러

다음 과 같이 Ajax 요청을 서브 컨트롤러에 요청하면

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

(원시보기의 경우) 서브 컨트롤러 이름은이어야 abc.raw.php합니다.

이것은 또한 Abc라는 2 개의 서브 컨트롤러가있을 수 있음을 의미합니다.

당신이 JSON을 반환 할 경우, 사용하는 의미가 있습니다 format=jsonabc.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 3.2의 새로운 기능! -구성 요소를 구축하지 않고도 처리 요청을 할 수 있습니다

줌라! Ajax 인터페이스 -Joomla는 이제 플러그인 또는 모듈에서 Ajax 요청을 처리하는 간단한 방법을 제공합니다. Joomla를 사용하고 싶을 수도 있습니다! 아직 컴포넌트가 없거나 모듈에서 요청을해야하는 경우 Ajax 인터페이스.


9
내가 지금까지 joomla.stackexchange.com에서 본 최고의 품질 답변-훌륭하게 수행하고 막대를 높이는 방법. 잘 했어!
NivF007

동의하지만 어떻 JRequest습니까? $this->inputv3.x를 사용 하기 때문에 단순히 더 이상 사용되지 않습니까?
Dmitry Rekun

1
에 대한 귀하의 우려를 해결했습니다 JRequest. 감사
Valentin Despa

3
좋은 대답은 3.1 이후 JSON 출력을 처리하는 Joomla 클래스가 있다는 것을 언급하고 싶었습니다. API , Usage
fruppel

@ fl0r yeap, Valentin는 Valid JSON Response섹션 에서 언급했습니다 .
Dmitry Rekun

20

이것은 매우 잘 대답 된 질문에 대한 늦은 대답이지만, AJAX 호출로 구성 요소의 데이터를 얻는 간단한 방법이 필요한 사람들을 위해이 바로 가기 솔루션을 추가하고 싶었습니다.

며칠 동안 인터넷 검색을 통해 찾은 모든 Joomla 버전, 타사 가능성 및 해킹을 통해 이것은 내가 얻을 수있는 가장 간단한 접근 방식이었으며 피드백은 확실히 감사합니다.

  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

Valentin의 대답은 훌륭하지만 이미 작성된 구성 요소에 1 또는 2 개의 ajax 호출을 추가하는 것만으로도 지나치게 복잡합니다. 그것은 별도 제작하지으로 도망 완벽하게 가능 controller.raw.php또는 view.raw.php파일을.

이 아약스 전화를 걸려면

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

에서 jobsubcontroller

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

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"로 설정되어 있으므로 XML 재 작성없이 자바 스크립트 호출에서이 매개 변수를 사용할 수 있습니다.


1
좋았지 만 JResponseJson클래스를 사용 하여 처리 하지 않는 이유는 무엇입니까?
Dmitry Rekun

JResponseJson은 Joomla 3
Anibal에서

내가 요청할 수있는 Joomla SE는 없었습니다.)
Harald Leithner

4

Javascript 출력을 추가하는 타사 플러그인이 100 % 확실하지 않으면 순수한 json_encode가 작동합니다.

그러나 ... 예를 들어 JomSocial은 전체 사이트에 ""를 추가합니다.

그래서 ... 간단한 트릭, json_encode를 태그로 감싸고 자바 스크립트 측에서 처리하십시오.

echo '@START@' . json_encode(...) . '@END@';

3

작업에서 컨트롤러 이름을 사용하여 컨트롤러에 직접 액세스 할 수 있습니다.

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

다음을 호출합니다 : controller.raw.php (반품은 raw입니다)

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

다음을 호출합니다 : controller.php (사용하지 않으면 return은 html입니다 die;)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.