JohnCMS 9.9: Migrating Old Custom Modules Using ?act= to the New Routing System

1
.

Hello JohnCMS developers and community members,

First of all, I would like to say that I am very happy with the new JohnCMS version. I really appreciate the hard work and improvements made by the developers. The new architecture, updated features, and improvements make JohnCMS feel more modern and powerful.

Thank you to the development team for continuing to improve this engine and keeping it active.

I would like to ask about migrating and adding custom modules in JohnCMS 9.9.x.

I have an older custom module that uses the ?act= system, for example:

/stats/?act=visitors
/stats/?act=browser
/library/?act=view&id=1

In previous JohnCMS versions, this method was commonly used to handle multiple pages or actions inside one module.

However, in JohnCMS 9.9 I see that the routing system has changed and modules now use:

modules/module_name/config/routes.php

I tried following the new routing documentation:

return static function (RouteCollection $router, User $user): void {
    $router->map(
        ['GET', 'POST'],
        '/stats',
        'modules/stats/index.php'
    );
};

The route file is loaded correctly, but I still need guidance on the correct approach for modules that contain multiple actions.

My questions:

  1. Is the old ?act= style still supported in JohnCMS 9.9?
  2. If it is supported, what is the recommended way to implement multiple actions inside one module?
  3. Should each action now become a separate route, for example:
/stats/visitors
/stats/browser
/stats/keywords

instead of:

/stats/?act=visitors
  1. Is there any example of a simple custom module that handles multiple pages/actions using the new routing system?

I am currently migrating my old custom modules to the new JohnCMS structure and I would like to follow the recommended method from the developers.

Thank you very much for your time and for your great work on JohnCMS.

.
Кадило крутится, лавэха мутится
15.07.2026 14:32

Hello!

First of all, thank you so much for the kind words!

To answer your questions about migrating your custom modules:

Yes, the old approach with file includes (using ?act=) will still work perfectly fine without any issues. If you are comfortable with this method and prefer to keep it for your migrated modules, you can absolutely continue using it. The engine does not block this way of handling actions.

However, if you want to follow modern practices, we highly recommend refactoring your modules to use the new routing and controller mechanism.

As a great reference point, you can look at the help module (modules/help). It contains very simple pages and is perfect for learning the new structure.

Quick guide on how to migrate to the new system:

Create the folder structure: Set up your folders to match the structure of the help module (including the src directory for your PHP classes).

Configure Composer Autoloading: You need to register your module's classes. Open composer.json in the root of your site and add your module's namespace to the autoload -> psr-4 section:

"YourVendorName\\Modules\\YourModule\\": "modules/moduleName/src/"

Regenerate the autoloader: Run this command in your project's root terminal to apply the changes:

composer dump-autoload

Create a simple Controller: Start by creating a very basic controller that just returns simple, plain text (e.g., "Hello World").

Map the route: Register this controller in your module's routes.php file.

Once you successfully get this simple text to load in your browser at your desired URL, you can start building out the actual logic, templates, and complex features.

If you run into any issues or need further help, feel free to ask here in this thread.

15.07.2026 15:19

Also, I've just updated the module creation manual in the documentation:

https://docs.johncms.com/moduli/sozdanie-modulya

Hope this helps with your migration!

.

Thank you for your explanation.

I have one more question.

If I want to keep the old ?act= style, for example:

/stats/?act=browser
/stats/?act=visitors
/stats/?act=keywords

what is the recommended way to register the route in routes.php?

Should I create only a single route for /stats and then continue handling $_GET['act'] inside index.php, or is there a better approach recommended for JohnCMS 9.9?

A simple example for this type of module would be very helpful.

Thank you.

.
Кадило крутится, лавэха мутится

drevils, You don't need to register routes with GET parameters (like ?act=) in your routes.php file. The routing system only matches the main URL path.

You only need to register the single main route, and then you can handle the $_GET['act'] parameter inside your controller or index.php file just like you did before.

.

Thank you for your explanation.

I followed your suggestion and registered only one main route while handling $_GET['act'] inside index.php.

My modules/stats/config/routes.php currently contains:

<?php

declare(strict_types=1);

use Johncms\Router\RouteCollection;
use Johncms\System\Users\User;

return static function (RouteCollection $router, User $user): void {
    $router->map(
        ['GET', 'POST'],
        '/stats',
        'modules/stats/index.php'
    );
};

However, when I open:

/stats

or

/stats/?act=browser

I still get a 404 Not Found error.

Am I missing something? Is there any additional configuration required to make a legacy module with index.php work in JohnCMS 9.9?

Thank you very much for your help.

.
Кадило крутится, лавэха мутится

There's nothing wrong with the code you posted.
So the 404 is coming from outside the file you showed, and the key question is which 404 you're seeing:

  • If it's the styled JohnCMS 404 page, the request reached index.php but the route didn't match.
  • If it's a plain 404 Not Found from the web server (nginx/apache), then /stats is never being forwarded to the front controller index.php at all — that's a web server rewrite issue, not a module issue.

The fastest way to tell them apart is to list the registered routes from the CLI:

php system/bin/console router:list

If /stats appears there, your module config is fine and the problem is in the web server rewrite rules. If it doesn't appear, the routes file isn't being loaded — in that case check the logs in data/ for a fatal error while loading one of the modules' routes.php.

Let me know which 404 you get and whether /stats shows up in router:list, and we can narrow it down from there.

.

Thank you for your explanation.

The 404 page is the JohnCMS styled 404 page, not the web server's default 404 page.

Unfortunately, I am using shared hosting, so I do not have SSH/CLI access and cannot run:

php system/bin/console router:list

Since the request reaches JohnCMS, does this mean that my routes.php file is not being loaded?

Is there another way to verify that the module routes are being registered without using the CLI?

Thank you again for your help.

.
Кадило крутится, лавэха мутится

drevils, extract this file in the site root.
Open in the browser: https://your-site/_diag_routes.php

Прикрепленные файлы:
.

Thank you very much.

I will upload the diagnostic file to the site root and run it. Then I will share the output here so we can continue troubleshooting.

I really appreciate your help.

Всего: 9