Cách thích hợp để thực hiện cuộc gọi AJAX trong thành phần là gì?


40

Tôi đang phát triển một thành phần tùy chỉnh cho Joomla! 3.x và muốn thực hiện cuộc gọi AJAX bên trong nó để lấy một số dữ liệu. Cách thích hợp để làm điều đó là gì?


Lời khuyên quan trọng là không bao giờ phá vỡ dòng chảy của Joomla. Ví dụ, một vài thành phần yêu cầu ajax trên sự kiệnAfterRoute và thực hiện nhiệm vụ và hủy yêu cầu ở đây. Điều này gây ra lỗi khó gỡ lỗi.
Shyam

Ý bạn là - không đóng Ứng dụng? Bạn có thể giải thích nhiều hơn?
Dmitry Rekun

Có, nếu j Joomla đóng ứng dụng, nó sẽ là tốt nhất. Vì vậy, khả năng mở rộng của tiện ích mở rộng của bạn sẽ được duy trì.
Shyam

Vẫn chưa hiểu hết. Điều tôi đang nói là $ app-> close () trong bộ điều khiển. Bạn có nghĩa là giống nhau? :)
Dmitry Rekun

Vâng, nói cùng một điểm. Tại sao chúng ta nên đóng ứng dụng trong bộ điều khiển, trong khi điều tương tự sẽ được thực hiện bởi chính j Joomla.
Shyam

Câu trả lời:


47

XIN LƯU Ý R ANNG TRẢ LỜI NÀY đã vài năm tuổi và chưa được cập nhật. Hãy chỉnh sửa / bình luận nếu bạn nghĩ rằng một cái gì đó không còn chính xác.

trừu tượng

Hầu như không có cách nào thực sự chính thức để giải quyết vấn đề này, nó phụ thuộc nhiều vào độ phức tạp và mức độ bạn muốn dựa vào mẫu MVC để thực hiện công việc.

Dưới đây là một số giải pháp khả thi những gì sẽ hoạt động trong Joomla 2.5 và 3.x. Mã này không được trình bày cho một công việc sao chép - dán mà là một ý tưởng chung.

Trước Joomla! 3.2 điều duy nhất bạn cần sử dụng các ví dụ dưới đây là a component. Sau Joomla 3.2 (đối với các tác vụ phức tạp thấp hơn), bạn có thể xử lý yêu cầu từ các mô-đun và plugin.


Phản hồi HTML chung (theo MVC kế thừa)

URL của bạn cho tác vụ cần phải trông như thế này:

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

Bạn hơn là tạo bộ điều khiển sẽ sử dụng chế độ xem, giả sử Abc, trong đó sẽ chứa tệp view.raw.html (giống hệt với tệp xem thông thường).

Dưới đây bạn có mã để tạo phản hồi HTML thô:

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

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

/view/abc/tmpl/default.php

<?php

echo "Hello World from /views/abc/tmpl/default.php";

Lưu ý: Đây là giải pháp tôi sẽ sử dụng nếu tôi phải trả về HTML (nó sạch hơn và tuân theo logic Joomla). Để trả về dữ liệu JSON đơn giản, hãy xem bên dưới cách đặt mọi thứ vào bộ điều khiển.

Kiểm soát viên

Nếu bạn thực hiện yêu cầu Ajax của bạn đến một subcontroller , như:

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

Hơn tên điều khiển con của bạn (cho chế độ xem thô) cần phải có abc.raw.php.

Điều này cũng có nghĩa là bạn sẽ / có thể có 2 người điều khiển phụ tên là Abc.

Nếu bạn trả về JSON, nó có thể có ý nghĩa để sử dụng format=jsonabc.json.php. Trong Joomla 2.5. Tôi có một số vấn đề khiến tùy chọn này hoạt động (bằng cách nào đó đầu ra bị hỏng), vì vậy tôi đã sử dụng thô.


Phản hồi JSON hợp lệ (theo MVC mới / kế thừa)

Nếu bạn cần tạo phản hồi JSON hợp lệ , hãy xem trang tài liệu Tạo đầu ra 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);

Bạn thường sẽ đặt mã này vào bộ điều khiển (bạn sẽ gọi một mô hình sẽ trả về dữ liệu bạn mã hóa - một kịch bản rất phổ biến). Nếu bạn cần đưa nó đi xa hơn, bạn cũng có thể tạo chế độ xem JSON (view.json.php), tương tự như ví dụ thô.


Bảo vệ

Bây giờ yêu cầu Ajax đang hoạt động, chưa đóng trang. Đọc dưới đây.

Đừng quên kiểm tra các giả mạo yêu cầu. JSession::checkToken()đến đây có ích Đọc tài liệu về Cách thêm CSRF chống giả mạo vào biểu mẫu


Trang web đa ngôn ngữ

Có thể xảy ra là nếu bạn không gửi tên ngôn ngữ trong yêu cầu, Joomla sẽ không dịch các chuỗi ngôn ngữ bạn muốn.

Xem xét nối thêm bằng cách nào đó lang param vào yêu cầu của bạn (như &lang=de).


Joomla! Giao diện Ajax

Mới trong Joomla 3.2! - cho phép bạn thực hiện các yêu cầu xử lý mà không cần xây dựng một thành phần

Joomla! Giao diện Ajax - Joomla hiện cung cấp một cách nhẹ nhàng để xử lý yêu cầu Ajax trong một plugin hoặc mô-đun. Bạn có thể muốn sử dụng Joomla! Giao diện Ajax nếu bạn chưa có thành phần hoặc nếu bạn cần thực hiện các yêu cầu từ mô-đun mà bạn đã có.


9
Câu trả lời chất lượng tốt nhất mà tôi đã thấy trên j Joomla.stackexchange.com cho đến nay - được thực hiện độc đáo và cách để nâng tầm. Công việc tuyệt vời!
NivF007

Đồng ý, nhưng còn JRequest? Nó không được chấp nhận nếu $this->inputtôi sử dụng v3.x?
Dmitry Rekun

1
Tôi giải quyết mối quan tâm của bạn về JRequest. Cảm ơn
Valentin Despa

3
Câu trả lời hay, chỉ muốn đề cập rằng có một lớp Joomla kể từ 3.1 xử lý đầu ra JSON: API , Cách sử dụng
fruppel

@ fl0r yeap, Valentin đã đề cập đến nó trong Valid JSON Responsephần.
Dmitry Rekun

20

Đây là một câu trả lời muộn cho câu hỏi được trả lời rất tốt này, nhưng tôi muốn thêm giải pháp cắt xén này cho những người chỉ cần một cách đơn giản để lấy dữ liệu của các thành phần của họ bằng một cuộc gọi AJAX.

Với tất cả các phiên bản Joomla, khả năng của bên thứ 3 và các bản hack mà tôi đã tìm thấy trong nhiều ngày làm việc, đây là cách tiếp cận đơn giản nhất tôi có thể đưa ra - và phản hồi được DEFINATELY đánh giá cao.

  1. Đã thêm chức năng executevào bộ điều khiển chính hiện có của tôi
  2. Tạo một bộ điều khiển con với chức năng công khai cho (các) tác vụ mà tôi muốn gọi bằng AJAX
  3. Đã sử dụng lớp Joomla JResponseJson tích hợp để xử lý đầu ra ( nó thực sự rất hay! )

URL để gọi / thực hiện tác vụ:

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

Bộ điều khiển chính đã sửa đổi \ 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);
        }
    }
}

Bộ điều khiển mới \ com_example \ controls \ 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();
    }
}

Kết xuất JSON được kết xuất

{
    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

Câu trả lời của Valentin là tốt nhưng hơi phức tạp một chút nếu tất cả những gì bạn cần làm là thêm 1 hoặc 2 cuộc gọi ajax vào một thành phần đã được xây dựng. Hoàn toàn có thể thoát khỏi việc không tạo các tệp controller.raw.phphoặc view.raw.phptệp riêng biệt .

Để thực hiện cuộc gọi ajax này

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

Trong 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

Câu trả lời của Valentin là tốt.

Tôi thích một bộ điều khiển json xử lý mã hóa và xử lý lỗi cho việc này Tôi đã tạo một lớp cơ sở 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();
  }

}

Bộ điều khiển này được mở rộng bởi lớp trình điều khiển thực hiện công việc, đại loại như thế này:

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

và bạn gọi yêu cầu như thế này:

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

Hàm băm token được tạo bởi JSession :: getFormToken (). Vì vậy, cuộc gọi hoàn chỉnh hoàn chỉnh có thể giống như thế này:

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

Tham số thứ hai được đặt thành "false" để chúng tôi có thể sử dụng tham số này trong các cuộc gọi javascript mà không cần viết lại xml.


1
Đẹp, nhưng tại sao không sử dụng JResponseJsonlớp để xử lý nó?
Dmitry Rekun

JResponseJson đã được giới thiệu trong Joomla 3
Anibal

Không có Joomla SE nơi tôi có thể hỏi;)
Harald Leithner

4

Nếu bạn chắc chắn 100% không có plugin bên thrid nào thêm bất kỳ đầu ra Javascript nào, thì json_encode thuần hoạt động.

Nhưng ... ví dụ JomSocial thêm "" vào toàn bộ trang.

Vì vậy, ... một mẹo hữu ích, bọc json_encode bằng các thẻ và xử lý nó ở phía Javascript.

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

3

Bạn có thể truy cập bộ điều khiển trực tiếp bằng cách sử dụng tên bộ điều khiển trong tác vụ:

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

sẽ gọi: control.raw.php (trả về là raw)

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

sẽ gọi: controller.php (return là html nếu bạn không sử dụng die;)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.