Router usage

I’m creating an addon that must POST to a specific php file and function within it. I have no idea how to do this.
The front end is JavaScript (I must be and it’s not my choice. There is no changing this requirement). The JS script needs to POST to the backend in PHP. (the php file is going to be a method file in the package. For the example say it’s here:…/package/PackageName/scr/Complete/finish.php
The function inside the method file is called Completed()

This is the part of the JS that is a must;

try {
   const response = await fetch('finish.php', {
     method: 'POST',
     headers: {
       'Content-Type': 'application/json'
     },
     body: dataJsonString
   });

I’m pretty sure I need to set up a ROUTER in the controller.php of the package but I don’t understand how to do this properly. I need help please.
Keep in mind that while I’m fairly good a writing small simple apps, I’m really a newbie so you’ll need to spell it out for me. Oh, and if Router isn’t what I should do, please tell me (keeping in mind that the JS needs to be done something like that).
I’ve tried to make the example as simple as possible for you all so that I can get a response.

From a package controller on_start() method, you can do something like:

use Concrete\Core\Support\Facade\Route;
  
function  on_start() {

  // maps a route (any in-site url you like) to a class::method
  Route::register('/ccm/system/dialogs/block/button_nav/submit',
                              'ButtonNav\ButtonNavDialogController::submit');
  
  // maps a route to an anonymous function with parameters from the route
  Route::register('jl_form_reform/submit_form/{bID}/{cID}',
              function ($bID, $cID) {   }
  );
}

The first is from my Button Nav addon, the second from Form Reform. There are variations to map routes to any callable.

Thank you @JohntheFish but I’m still confused. That’s different than Concrete router instructions but yours makes more sense so let’s stick with how you explained it.

I’m having a as lot of trouble understanding the actual route. The actual route I’m working on is quite long so for simplicity let’s say this instead;
/packages/payment/src/payments/method/Payment.php
And inside that php there is MakePayment class and paynow() method
Is this my route:

Route::register('/packages/MyPayment/src/payments/method/Payment’
                              ‘MakePayment::paynow');

I’m pretty sure I have the directory structure starting in the wrong place.

But even after that, how do I point to that route in the JavaScript.

Thanks

You could be confusing 2 separate mappings:

(1) Namespace mapping. This is which directory path within a package maps to a namespace for classes within that package. Namespace mapping provides instructions to php’s autoloader about which file to load for classes provided by your package. For example, I could map

directory => namespace
src/JtF/FormReform => \JtF\FormReform

This kind of namespace mapping is a modern php thing. Concrete has its own interface to such configuration, but underneath its the same as you would get in any OO php application.

(2) Route mapping. This maps a url sub-path to the code that handles it. My previous examples are for this only and had nothing to do with (1). This is a convention that Concrete has adopted from underlying framework libraries and provides instructions to Concrete about which code to run for a specific incoming url. You just code the route as a full url into your javascript and Concrete then uses the mapping to run the associated php.

The core URL class can help you turn a route into a full url.

Both the above have their own sections in the documentation.

I think I might be getting it. I’ll try and get back here once I’ve had time. I will clarify though that I cannot find anywhere the Router documentation the way you’ve explained it. The link I sent from Concrete Documentation is completely different. But then I’ve always found the Concrete documentation very confusing. It feel like the person wrote it with an idea in their head but failed to share the idea. Lol. It’s probably just me because I’m a weak coder. But just to clarify, Concrete “Router Basic” https://documentation.concretecms.org/developers/framework/routing/routing-basics
Show it like:

$router->get(‘/api/customer/{customerId}, function($customerId) {
    return ‘The customer ID is: ‘ . $customerId;
});

Route::register is just a convenience static method that registers ->get and ->post. Otherwise it is the same. The following few pages cover mapping directly to a class::method, expanding from anonymous functions.

Or you can use an anonymous function to then call your class::method. That is what I do (stripped out) in the form_reform example above, with a bit of data checking first in the anonymous function.

You’ve been lots of help but I’m still confused. Im feeling a bit stupid here. I’m not understanding the class part of the route.
Is it the namespace: at the top of the method file plus the name of the class in that file???

Also, do I need to do anything special since the JS is POSTing json
I do have:

$json = file_get_contents(‘php://input’);

in the method that I’m trying to call. Maybe I’ve done the route correctly but did something else wrong.

For the handling code, $json = file_get_contents(‘php://input’) looks like a misdirection from code designed to work elsewhere, reading an input stream, not within a framework. Best to forget it. I don’t think it will work here.

The Concrete router works with an anonymous function or a fully qualified classname::methodname, which would be something like

“Your\Configured\Namespace\YourClass::YourMethod”

The Concrete router will then call that class::method and from within that method you can read the post data.

In the handling code, you would do one of:

(1) Dirty - look directly at the $_POST superglobal

Or the Concrete way using a Symfony parameter bag
(2) If your receiving class is a controller, you can do $this->request->request->all() or $this->request->request->get() for a specific parameter name
(3) Otherwise, you make an instance of the request object and get the parameter from that. $request = $app->make(Request::class);

John, I sent you a private email explaining what I’m trying to do. Disregard the stuff about the router. I got that figured out. Thanks for your help on that.
I’m having trouble with the symfony $request now though. I’ll keep trying.

I’m having another issue and I think it’s related to how the json data gets routed.
The JavaScript “fetch” sends the json (I know it’s being sent) but I can see that when it gets to the place it’s supposed to go, it’s empty. I can’t find anything on concrete but I googled and found other frameworks require an extra step. Am I missing a step sending and receiving JSON?
Thanks.

Ok so I think I figured out another way to get done what I needed to do, but still wouldn’t mind seeing an explanation as to how this json thing should happen. I’d like to learn.

Some diagnostic tips to see what the data is:

(A). Keep the browser developer console open on the Network tab. You can then look at any communication between the browser and the server and examine the query (get) parameters, payload (post) parameters and response.

(B) At the top of your route, or later in your processing:

echo "<pre>"
echo h(print_r($_POST,true)); // or $_REQUEST or $_GET
echo "</pre>"
die(); // optional, can be useful to just end here to prevent 
       // further output confusing things.

(C) As per B, wrapped in a <pre>, not as easy to read but safe against data containing objects that could be recursive.

var_dump_safe($_POST, true, 3); // '3' is the depth to dump to. 
                               // Adjust for more/less data

(D) Configure xdebug to follow what is happening in php . A complex topic in itself, both setting it up and understanding the output.

1 Like

Thank you so much for all your help John. People learning (like me) need people like you, to give valuable examples. Everyone learns differently and people that learn but examples need examples that show a complete process. It helped so much. Thanks again.

1 Like