rick Posted April 23, 2017 Share Posted April 23, 2017 Hello all, I am confused about how a specific module method is invoked by a form rendered on the front-end and then submitted. My confusion, or rather ignorance, stems from not finding any references describing how I (or ProcessWire) determine whether an action is adding or editing a set of data. I've gotten myself surrounded by so many trees at the moment from reading blog articles and forum entries, and dissecting existing modules. I've seen <code>___execute</code> method based on url, <code>___action</code> used with suffixed name, and others with no discernible means. I can create and install a skeletal module, create and edit the module config, retrieve data from page(s), and render a form on the front-end, all without issues. But so far I am unable to get a submitted form to invoke a particular class method for saving a new data set, editing an existing data set, etc. What is the proper, ProcessWire efficient, way to have a submitted front-end form call the appropriate module method? Link to comment Share on other sites More sharing options...
abdus Posted April 23, 2017 Share Posted April 23, 2017 I am not sure you can access Process modules (pages with admin template) as a guest. If you need to know what page the request is sent to, and perform operations depending on the page, or need some functionality from other autoload modules, then you'd do your checks under ready() function. If you don't, then both init() and ready() is fine. To intercept the requests, you can create a new template called api in your install() function (and remove in uninstall() function), then set it to accept urlSegments (as many as you want), then create a page with that template under root page, then inside your module check if request is made to that page and to that specific urlSegment. Optionally return 404 for all other urlSegments you're not interested in like this <?php $method = $this->sanitizer->alpha($this->input->urlSegment1); if(!$method) return; if (method_exists($this, $method)) { $this->{$method}($param1, $param2); } else { // method does not exist throw new Wire404Exception(); } You can build such a module like this <form action="/api/sendmail"> ... </form> <?php namespace ProcessWire; class MyModule extends Wire implements Module { public static function getModuleInfo () { return [ // ... 'autoload' => true // ... ] } public function ready() { if ($this->wire()->input->requestMethod() === 'POST') { // perform other checks if($this->input->url !== '/api/sendmail') return; // this is the request we should be intercepting $data = $this->wire()->input->post; $name = $this->sanitizer->text($data->name); $email = $this->sanitizer->email($data->name); if($this->sendMail($name, $email)) { // email sent successfully } else { // email failed } } } public function sendMail($name, $email) { $mailer = $this->wire()->mail; // check if any mail is sent successfully return 0 < $mailer->send( 'test@example.com', // to address 'pw@myhost.com', // from address 'Test post, please ignore', // subject 'Hey there, this is a test mail' ); } } 2 Link to comment Share on other sites More sharing options...
horst Posted April 23, 2017 Share Posted April 23, 2017 @rick: In your code that handles the form input, you simply can call the module or public module method: //if(<condition for form was posted>) { $myModule = $modules->get("MyModulesName"); // get a module handle $myModule->passNewDataIn($input->post->someData); // call the public method for data input // rest is done in modules method, ... // if you are not in template scope, you can use $myModule = wire("modules")->get("MyModulesName"); //} 2 Link to comment Share on other sites More sharing options...
rick Posted April 23, 2017 Author Share Posted April 23, 2017 Hi @abdus, @horst Thanks for your replies. abdus, If I understand you correctly, ProcessWire has it's own set of "magic functions" relating to module classes similar to how it automatically handles template files, such as <code>init.php</code> and <code>ready.php</code>, where init() and ready() are automatically executed within a module if present. In Ryan's module, ImportPagesCSV, it appears that module functions are automatically executed based on URL segments (which are not defined in a template): Quote /** * Executed when root url for module is accessed * */ public function ___execute() { ... /** * Executed when ./fields/ url for module is accessed * */ public function ___executeFields() { Maybe I am making things far more complicated than they really are, which is highly likely, but this is the basis of my confusion. Information like what you both have posted needs to be documented somewhere so it's easy to find. Thank you both for your replies. 1 Link to comment Share on other sites More sharing options...
abdus Posted April 23, 2017 Share Posted April 23, 2017 Process modules (ones that extend Process class) can have execute[Action] methods that are called with their respective urlSegments. In case of your example, the method comments portrait the exact conditions where ___executeFields is executed with /fields urlSegment, for instance. The problem with this approach is that you cannot (or should not) use them on frontend, as Process modules are intended for backend use (unless I'm mistaken). However, calling the method depending on urlSegment is quite simple with the snippet I posted earlier. You can use something similar to following to call execute[Action] methods <?php public function ready() { // accept only single urlSegment if($this->input->urlSegment2) throw new Wire404Error(); // always sanitize user input $method = $this->sanitizer->alpha($this->input->urlSegment1); if(!$method) return; // or perform more sophisticated checks // respond to execute[Action] methods // such as executeList() with /list urlSegment // or executeFields() for /fields urlSegment // create camelCase method name // /json urlSegment will be intercepted by executeJson() function $method = "execute" . ucfirst($method); if (method_exists($this, $method)) { $this->{$method}($param1, $param2); } else { // method does not exist throw new Wire404Exception(); } } public function executeJson() { echo json_encode(['a' => 1, 'b' => 2]); } Put this inside your init() or ready() method in your module to relay the request to specific functions. 3 Link to comment Share on other sites More sharing options...
rick Posted April 23, 2017 Author Share Posted April 23, 2017 You are correct that process modules should be restricted to back-end use. It was my mistake for using that example and applying it to front-end use. I am not writing any process modules, though. I believe I have enough to continue converting my olden-days code to use in ProcessWire. I understand your URL segment example, too. I'm sure I will make use of it soon. Thank you for your help. 1 Link to comment Share on other sites More sharing options...
abdus Posted April 23, 2017 Share Posted April 23, 2017 Glad to be of help @rick Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now