コンポーネントでAJAX呼び出しを行う適切な方法は何ですか?


40

Joomla!のカスタムコンポーネントを開発しています。3.xで、内部でAJAX呼び出しを行ってデータを取得したい。それを行う適切な方法は何ですか?


重要なアドバイスは、Joomlaの流れを壊さないことです。例えば、いくつかのコンポーネントは、ajaxリクエストonAfterRouteイベントを発生させ、タスクを実行し、ここでリクエストを強制終了します。これにより、デバッグが困難なエラーが発生します。
シャム14

つまり、アプリを閉じないでください。もっと詳しく説明していただけますか?
ドミトリーRekun 14

はい、joomlaがアプリを閉じた場合に最適です。したがって、拡張機能の拡張性は維持されます。
シャム14

それでも完全に理解していない。私が話しているのは、コントローラーの$ app-> close()です。同じ意味ですか?:)
ドミトリーRekun 14

はい、同じことを言っています。コントローラーでアプリを閉じる必要があるのはなぜですか、同じことがjoomla自体によって行われます。
シャム14

回答:


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

サブコントローラー名(rawビューの場合)よりもが必要abc.raw.phpです。

これは、Abcという名前の2つのサブコントローラーがあることを意味します。

JSONを返す場合、format=jsonand を使用するのが理にかなっていますabc.json.php。Joomla 2.5で。このオプションを機能させるためにいくつかの問題が発生したため(出力が破損したため)、そのまま使用しました。


有効な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 14

同意しますが、どうJRequestですか?$this->inputv3.xを使用しているため、単純に廃止されるべきですか?
ドミトリーRekun 14

1
についての懸念に対処しましたJRequest。ありがとう
バレンティンデスパ14

3
いい答え、JSON出力を処理する3.1以降のJoomlaクラスがあることを言及したかっただけです:APIUsage
fruppel 14

@ fl0rはい、バレンティンはそれをValid JSON Responseセクションで言及しました。
ドミトリーRekun 14

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

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

2番目のパラメーターは「false」に設定されているため、xmlの書き換えなしでjavascript呼び出しでこれを使用できます。


1
いいですが、なぜJResponseJsonクラスを使ってそれを処理しないのですか?
ドミトリーRekun 14

JResponseJsonは、Joomla 3
Anibal 14

質問できるJoomla SEはありませんでした;)
ハラルドリースナー14

4

Javascript出力を追加するサードパーティのプラグインがないことを100%確信している場合、純粋な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(戻り値はraw)

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

呼び出します:controller.php(使用しない場合、戻り値はhtmlですdie;

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.