Jump to content

Which module methods are called from front-end rendered forms


rick
 Share

Recommended Posts

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

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'
      );

  }
}
  • Like 2
Link to comment
Share on other sites

@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");
//}

 

  • Like 2
Link to comment
Share on other sites

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. 

 

  • Like 1
Link to comment
Share on other sites

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.

  • Like 3
Link to comment
Share on other sites

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.

 

  • Like 1
Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...