Sometimes, you need to have your modules return data asynchronously
to the HTML page rendering, using client-side (JavaScript) code. DO NOT
use arbitrarily named .php files which will be accessed directly over the
web; this is a terrible and insecure practice. The best way to do that is
by using Joomla's com_ajax
component.
com_ajax is a special core component built into Joomla. By itself, it can't do much. Its job is to let non-component, first-class Joomla extension types (modules, plugins, and templates) handle requests and return results either as JSON or as any arbitrary format.
For modules, you would need to load a URL like this:
index.php?option=com_ajax&module=mod_example&method=something&format=raw
.
For backend modules you obviously need to use
administrator/index.php
.
The way this works is that com_ajax will create an instance of the
module's DIC (that is to say, use the code in our module's
services/provider.php
file), instantiate the helper
that has the name
, where
Modulename
HelperModulename
is the name of our module without
the mod_
prefix (e.g. Example
for
mod_example
), and call the method
where
Methodname
AjaxMethodname
is the value of the
method
URL parameter. In the example above, it would load the
ExampleHelper
helper of mod_example
and call its SomethingAjax
method.
If you use format=raw, one of the following will happen:
-
If you throw an exception or return a Throwable, it will set the HTTP status to the throwable's status code and return a body similar to "RuntimeException:Your throwable's message".
-
If you return a scalar (string, null, integer, float) it will return its string representation.
-
If you return an object or array it will try to first cast it as an array, then
implode()
it into a string. This is quite useless, so please don't do that.
If you use format=json it will try to convert your data to a JSON
representation using Joomla's
Joomla\CMS\Response\JsonResponse
class. The
returned JSON object has the following keys:
- success
-
Boolean true if your method has neither thrown an exception, nor returned a Throwable. Boolean false otherwise.
- message
-
Only when success is false. If your method threw an exception, or returned a Throwable object: the message of that throwable. Otherwise, this key is not set.
- messages
-
Any enqueued Joomla! application messages. The messages are categorised by type, therefore they may be returned out of order. For example:
messages: { "warning": [ "Some warning", "Another warning" ], "error": [ "The code went belly up. Whoopsie!" ] }
- data
-
Only when success is true. The data returned by your method.
Practical limitations
com_ajax does not go through the module's Dispatcher. As a result, you do not have access to the module parameters, nor can you push any Joomla! object (such the application object, the input object, etc) to the Helper.
If you need any module parameters to be used when handling an AJAX request you have to send them as part of the request (as GET or POST parameters), store them in the session, or pass the module's ID as part of the request (as a GET or POST parameter). Each method has limitations and security considerations:
-
Passing configuration as GET or POST parameters is unsafe. Anyone can read them. Passing secrets, or filesystem paths, is detrimental to security. Moreover, any data read by this method must be ASSUMED to have been tampered with by a malicious actor who is trying to hack your site. Do not make any assumptions about data types, ranges, or that the data will even make any sense. For these reasons, using this method is VERY STRONGLY DISCOURAGED.
-
Storing data in the session does not have the security considerations of passing data through GET or POST, but you must keep in mind that a user may try to open several pages at once, each one with a different instance of your module. This means that the data read from the session may be inconsistent and irrelevant to the current page. Depending on what your module does this could have security or privacy implications.
-
Passing the numeric module ID is much safer. You can very easily try to load a module by that ID and determine if it exists, if it's published, and if the current user should be able to see it (access level check). You can then load its parameters into a Joomla Registry object and process data in your code.
Generally speaking, having modules do AJAX is a problematic idea. While there are very few good use cases where this makes sense (and they all involve data which is always meant to be public), the majority of the uses cases I have seen in the real world display a blatant disregard for security and privacy. If you need to do complex processing maybe what you are doing does not belong in a module, but a component.
And, please, PLEASE, do test your modules with use cases that involve multiple but differently configured instances of your module on the same page, or what happens if you open three or four pages with differently configured instances of your module in very quick succession (open all pages before the first AJAX request has the chance to be executed by the browser), ideally with users with different privileges logged in. You will be surprised at how many problems you will catch this way.