Porting Comments System using PHP and Ajax to Modx environment

Introduction

I’m a complete novice when it comes to html, css, php and Modx.

Despite lacking the knowledge I succeeded to run a comments system using PHP and Ajax under Xampp → localhost. This works without error and flawless. I used the comment system presented here
Now I want to run it under a Modx install.

My problem

Of course (for obvious reasons) it doesn’t run because Modx uses snippets for the php and it is not a simple copy and paste of the php files into snippets. There’s also some JS script involved in the main page. I’m not sure if I can drop that into a chunk. or just refer to it in the head section.

Basically there are 5 files:
1. index.php

2. comment-add.php (see below for code)

<?php
/**
 * This script is to add the comment to database.
 */
use Phppot\DataSource;
require_once __DIR__ . '/DataSource.php';
$database = new DataSource();
$sql = "INSERT INTO tbl_comment(parent_comment_id, comment, comment_sender_name) VALUES (?,?,?)";
$paramType = 'iss';
$paramValue = array(
    $_POST["comment_id"],
    $_POST["comment"],
    $_POST["name"]
);
$result = $database->insert($sql, $paramType, $paramValue);
echo $result;

3. comment-list.php (see below for code)

<?php
/**
 * This script is to list the comments in a nested order.
 */
use Phppot\DataSource;
require_once __DIR__ . '/DataSource.php';
$database = new DataSource();
$sql = "SELECT * FROM tbl_comment ORDER BY parent_comment_id asc, comment_id asc";
$result = $database->select($sql);
echo json_encode($result);

4. DataSource.php
5. jquery-3.2.1.min.js

Expected behavior

I’m not expecting a complete solution or crashcourse how to implement this into Modx. But I’m eager to learn. So the main question is: How to port and integrate this into Modx? For example the file “comment-list.php”. I have to transfer that code into a snippet. But what should I change so I can use it into a snippet? And how can I use the $result in another snippet, resource page, chunk etc.?

$result = $database->select($sql);
echo json_encode($result);

Or, in the “datasource.php” there are multiple references to the mandatory php files like:

function listComment() {
$.post("comment-list.php"

But the php code of “comment-list.php” has to be in a snippet. How should I refer to that snippet in the datasource.php to replace this phrase “comment-list.php”?

The same for:

$.ajax({
url: "comment-add.php",

How to refer to “comment-add.php”?

I realize this is an extensive question. But I hope somebody can hint me in the right direction.

Environment

I know that the code is not bulletproof or even vulnerable but that is for later to correct.
MODX version 3.0.1 pl, PHP version 8.1

MODX is very flexible, so there are different ways to achieve this.

It’s completely up to you where you place your javascript code. Both a separate file or a chunk (or even the template or the resource content) are viable options.


For the PHP files, you could leave them as they are, if you don’t need any additional MODX functionality.

Another way is to create a new resource as an AJAX endpoint. Choose “(empty)” as the template of this resource, set “Content Type” to JSON and put an uncached call to a custom snippet in the resource content: [[!myCustomSnippet]].

In the custom snippet you can pretty much use the same code you have right now. Just instead of __DIR__ for a file path use something like $modx->getOption('core_path') to get the path to the core/ directory (where PHP files are usually located).

Also if you use the same database for your custom table as you use for MODX, you can use code like this $stmt = $modx->prepare("..."); $stmt->execute(); to access the database table, without the need for the DataSource.php file.

To use this “AJAX endpoint” in you javascript code, change the url (for example in $.post("comment-list.php" ...) to the URL of the created resource.


There are also more challenging ways to achieve the same. For example: Creating a RESTful API or using an extra like QuickApi, generating a Custom Model for your database table, etc.

Thanks Harry, this definitely gets me going and I have a lot to study. Lucky enough I have already some knowledge about prepared statements (and a free day on wednesday ;o))

To use this “AJAX endpoint” in you javascript code, change the url (for example in $.post("comment-list.php" ... ) to the URL of the created resource.

When I do this should I hard code the full URL to the resource or link to the resource dynamically with
[[~123]]?

That will be replaced by the URL only if the code is being parsed by MODX (usually for display in the front end).

Porting PHP code to MODX snippets is fairly straightforward, as long as they don’t have interspersed HTML. If they do, you have to either put that HTML in a chunk and pull it in with $modx->getChunk() or convert it to PHP strings.

The other main change is that you need to replace echo and print statements with $output .= $whatever; and put return $output at the end. (You don’t have to use $output as the variable name, but pretty much everyone does.) That’s pretty much it.

If you need the output of a snippet somewhere else, you can save it in a file, the DB, or a $_SESSION variable. If it’s needed by another snippet, you can do this, but be aware that the value will always be a string:

$value = $modx->runSnippet('SomeSnippet');

Porting PHP code to MODX snippets is fairly straightforward, as long as they don’t have interspersed HTML.

Yes, that’s true but I’m struggling with certain lines in both the comment-add.php and comment-list.php. I can put the full php-code of these two files in two separate snippets but what are the equivalents of the two lines of code below?

use Phppot\DataSource;
require_once __DIR__ . '/DataSource.php';

And my second question is, can I put all the code of the datasource.php also in a snippet? I mean, is this good practice? I understand that datasource.php is a common database utility class and I’m not sure if Modx understands these lines of code.

I can’t think of any reason why they wouldn’t work as is, as long as the DataSource.php file is in the same directory as the PHP script with the require line, and Phppot\DataSource is referencing a class called DataSource that has Phppot as its namespace and has been included somewhere earlier in the code (or there’s an autoloader for it).

As for your second question. It depends on how it’s being used. If other files or snippet are “including” it as a file and calling it’s class methods, it’s best to leave it as a file. If it’s all self-contained, it could probably go in a snippet, though most MODX developers use a class file if the file contains nothing but the class code. MODX will cache the class code for you, so there’s no load time once its been accessed.

I’d put the DataSource.php file into a folder in core/components (for example core/components/commentssystem) and then create a snippet with code like this:

<?php
use Phppot\DataSource;
require_once $modx->getOption('core_path') . 'components/commentssystem/DataSource.php';
$database = new DataSource();
$sql = "SELECT * FROM tbl_comment ORDER BY parent_comment_id asc, comment_id asc";
$result = $database->select($sql);
return json_encode($result);

Alternatively, don’t use DataSource.php at all and query the data directly in your snippet:

<?php
$sql = "SELECT * FROM tbl_comment ORDER BY parent_comment_id asc, comment_id asc";
$stmt = $modx->prepare($sql);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
return json_encode($result);

In MODX 3 you could also use a bootstrap.php file to load custom classes.

For example:

  • Create a file bootstrap.php in core/components/commentssystem with the content
<?php
require_once $namespace['path'] . 'DataSource.php';
  • Create a new namespace in the MODX manager (Cogwheel-icon → “Namespaces”) with the “Core Path” = {core_path}components/commentssystem/

  • Remove the line require_once $modx->getOption('core_path') . 'components/commentssystem/DataSource.php'; from the snippet.

Whawww, I’m making progress thanks to my two coaches in this thread :grinning:

This was very helpfull Harry:

I’d put the DataSource.php file into a folder in core/components (for example core/components/commentssystem ) and then create a snippet with code like this:

<?php
use Phppot\DataSource;
require_once $modx->getOption('core_path') . 'components/commentssystem/DataSource.php';
$database = new DataSource();
$sql = "SELECT * FROM tbl_comment ORDER BY parent_comment_id asc, comment_id asc";
$result = $database->select($sql);
return json_encode($result);

Also this remark from Bob:

As for your second question. It depends on how it’s being used. If other files or snippet are “including” it as a file and calling it’s class methods, it’s best to leave it as a file.

I put the DataSource.php file into a folder in core/components

Well, what do have got now? I put the code of comment-add.php and comment-list.php in two different snippets and call these snippets in two separate resources without template and in the content only the snippet call [[comment_list]] and [[comment_add]]. In the Ajax part I refer to these resources with:

$.ajax({
                    url: "[[~18]]",
                    . . . )

And

$.ajax({
                    url: "[[~19]]",
                    . . . )

The script is running within Modx. The only downside is that it is not giving any output on the page but when I make a new comment in the form the comment is definitely stored in the database because I can see it.
What could be the problem that the end result is not visible on the page?

Snippet [[comment_list]]

<?php
/**
 * This script is to list the comments in a nested order.
 */
use Phppot\DataSource;
require_once $modx->getOption('core_path') . 'components/commentssystem/DataSource.php';
$database = new DataSource();
$sql = "SELECT * FROM tbl_comment ORDER BY parent_comment_id asc, comment_id asc";
$result = $database->select($sql);
return json_encode($result);

Snippet [[comment_add]]

<?php
/**
 * This script is to add the comment to database.
 */
use Phppot\DataSource;
require_once $modx->getOption('core_path') . 'components/commentssystem/DataSource.php';
$database = new DataSource();
$sql = "INSERT INTO tbl_comment(parent_comment_id, comment, comment_sender_name) VALUES (?,?,?)";
$paramType = 'iss';
$paramValue = array(
    $_POST["comment_id"],
    $_POST["comment"],
    $_POST["name"]
);
$result = $database->insert($sql, $paramType, $paramValue);
return json_encode($result);

To be complete, here the text of both files.
DataSource.php

Index.php (But the contents of this file is of course in a resource)

Screenshot database with result.

My site

When I go to your site and open the developer tools in the browser (tab “Network”), the AJAX request to https://yourdomain.com/comments-list.json returns a “404 Not Found” error.

Hmmm, strange. I used the developer in Firefox:

And in Chrome developer:

Not sure what changed, but the “AJAX endpoint” seems to work now.

I get the error message “Uncaught SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data” in the console of the developer tools.

I believe you have to remove this line → var data = JSON.parse(data); in your javascript code. I think the data is already parsed by jQuery.

OMG, that did the trick. IT WORKS ! ! ! Thanks very much both of you for staying with me and invest your time. Despite Modx is a complicated framework with it’s own Modx-language but I love the complete freedom. I like to learn from real examples because for me it’s easier to understand. A lot of the syntax is complete abracadabra for me but it’s always a challenge to get out of the comfort zone and learn new things. Again, thanks very much, I really appreciate it.