กระบวนการเกี่ยวข้องกับ 2 ขั้นตอน:
- แสดงแบบฟอร์มส่วนหน้า
- บันทึกข้อมูลในการส่ง
มี 3 วิธีที่ต่างกันที่อยู่ในใจของฉันเพื่อแสดงส่วนหน้า:
- ใช้แบบฟอร์มการลงทะเบียนในตัวสไตล์การแก้ไข ฯลฯ เพื่อเพิ่ม "frontend like" ให้มากขึ้น
- ใช้หน้า / โพสต์ WordPress และแสดงแบบฟอร์มโดยใช้รหัสย่อ
- ใช้เทมเพลตอุทิศที่ไม่ได้เชื่อมต่อกับหน้า / โพสต์ใด ๆ แต่เรียกโดย url ที่ระบุ
สำหรับคำตอบนี้ฉันจะใช้อันหลัง เหตุผลคือ:
- การใช้แบบฟอร์มการลงทะเบียนในตัวอาจเป็นความคิดที่ดีการปรับแต่งแบบลึกอาจเป็นเรื่องยากมากโดยใช้แบบฟอร์มที่มีอยู่แล้วและถ้าหากใครต้องการปรับแต่งฟิลด์แบบฟอร์มด้วย
- ใช้หน้า WordPress ร่วมกับรหัสย่อ, ไม่น่าเชื่อถือ, และฉันคิดว่า shorcodes ไม่ควรใช้สำหรับการทำงาน, เพียงแค่สำหรับการจัดรูปแบบและเช่น
1: สร้าง URL
เราทุกคนรู้ว่ารูปแบบการลงทะเบียนเริ่มต้นของเว็บไซต์ WordPress มักจะเป็นเป้าหมายของผู้ส่งอีเมลขยะ การใช้ URL ที่กำหนดเองเป็นตัวช่วยในการแก้ปัญหานี้ นอกจากนี้ฉันต้องการใช้ตัวแปร URL เช่น URL แบบฟอร์มลงทะเบียนไม่ควรเหมือนกันเสมอสิ่งนี้ทำให้ผู้ส่งอีเมลขยะมีชีวิตที่ยากขึ้น เคล็ดลับจะทำโดยใช้nonceใน url:
/**
* Generate dynamic registration url
*/
function custom_registration_url() {
$nonce = urlencode( wp_create_nonce( 'registration_url' ) );
return home_url( $nonce );
}
/**
* Generate dynamic registration link
*/
function custom_registration_link() {
$format = '<a href="%s">%s</a>';
printf(
$format,
custom_registration_url(), __( 'Register', 'custom_reg_form' )
);
}
การใช้ฟังก์ชั่นนี้ง่ายต่อการแสดงในเทมเพลตลิงค์ไปยังแบบฟอร์มลงทะเบียนแม้ว่าจะเป็นแบบไดนามิก
2: รู้จัก url, stub แรกของCustom_Reg\Custom_Reg
คลาส
ตอนนี้เราต้องรู้จัก URL สำหรับเทเลเท็กซ์ฉันจะเริ่มเขียนชั้นเรียนซึ่งจะเสร็จในภายหลังในคำตอบ:
<?php
// don't save, just a stub
namespace Custom_Reg;
class Custom_Reg {
function checkUrl() {
$url_part = $this->getUrl();
$nonce = urlencode( wp_create_nonce( 'registration_url' ) );
if ( ( $url_part === $nonce ) ) {
// do nothing if registration is not allowed or user logged
if ( is_user_logged_in() || ! get_option('users_can_register') ) {
wp_safe_redirect( home_url() );
exit();
}
return TRUE;
}
}
protected function getUrl() {
$home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' );
$relative = trim(str_replace($home_path, '', esc_url(add_query_arg(array()))), '/');
$parts = explode( '/', $relative );
if ( ! empty( $parts ) && ! isset( $parts[1] ) ) {
return $parts[0];
}
}
}
ฟังก์ชั่นดูที่ส่วนแรกของ url after home_url()
และถ้ามันตรงกับ nonce ของเรามันจะคืนค่า TRUE ฟังก์ชั่นนี้จะถูกใช้เพื่อตรวจสอบคำขอของเราและดำเนินการที่จำเป็นเพื่อแสดงแบบฟอร์มของเรา
3: Custom_Reg\Form
ชั้นเรียน
ตอนนี้ฉันจะเขียนชั้นเรียนที่จะต้องรับผิดชอบในการสร้างมาร์กอัปแบบฟอร์ม ฉันจะใช้มันเพื่อจัดเก็บในพา ธ คุณสมบัติของไฟล์เทมเพลตที่ควรใช้เพื่อแสดงฟอร์ม
<?php
// file: Form.php
namespace Custom_Reg;
class Form {
protected $fields;
protected $verb = 'POST';
protected $template;
protected $form;
public function __construct() {
$this->fields = new \ArrayIterator();
}
public function create() {
do_action( 'custom_reg_form_create', $this );
$form = $this->open();
$it = $this->getFields();
$it->rewind();
while( $it->valid() ) {
$field = $it->current();
if ( ! $field instanceof FieldInterface ) {
throw new \DomainException( "Invalid field" );
}
$form .= $field->create() . PHP_EOL;
$it->next();
}
do_action( 'custom_reg_form_after_fields', $this );
$form .= $this->close();
$this->form = $form;
add_action( 'custom_registration_form', array( $this, 'output' ), 0 );
}
public function output() {
unset( $GLOBALS['wp_filters']['custom_registration_form'] );
if ( ! empty( $this->form ) ) {
echo $this->form;
}
}
public function getTemplate() {
return $this->template;
}
public function setTemplate( $template ) {
if ( ! is_string( $template ) ) {
throw new \InvalidArgumentException( "Invalid template" );
}
$this->template = $template;
}
public function addField( FieldInterface $field ) {
$hook = 'custom_reg_form_create';
if ( did_action( $hook ) && current_filter() !== $hook ) {
throw new \BadMethodCallException( "Add fields before {$hook} is fired" );
}
$this->getFields()->append( $field );
}
public function getFields() {
return $this->fields;
}
public function getVerb() {
return $this->verb;
}
public function setVerb( $verb ) {
if ( ! is_string( $verb) ) {
throw new \InvalidArgumentException( "Invalid verb" );
}
$verb = strtoupper($verb);
if ( in_array($verb, array( 'GET', 'POST' ) ) ) $this->verb = $verb;
}
protected function open() {
$out = sprintf( '<form id="custom_reg_form" method="%s">', $this->verb ) . PHP_EOL;
$nonce = '<input type="hidden" name="_n" value="%s" />';
$out .= sprintf( $nonce, wp_create_nonce( 'custom_reg_form_nonce' ) ) . PHP_EOL;
$identity = '<input type="hidden" name="custom_reg_form" value="%s" />';
$out .= sprintf( $identity, __CLASS__ ) . PHP_EOL;
return $out;
}
protected function close() {
$submit = __('Register', 'custom_reg_form');
$out = sprintf( '<input type="submit" value="%s" />', $submit );
$out .= '</form>';
return $out;
}
}
คลาสสร้างมาร์กอัปฟอร์มที่วนลูปฟิลด์ทั้งหมดที่เพิ่มcreate
วิธีการโทรในแต่ละฟิลด์ Custom_Reg\FieldInterface
แต่ละเขตจะต้องเป็นตัวอย่างของ มีการเพิ่มฟิลด์ที่ซ่อนอยู่เพิ่มเติมสำหรับการตรวจสอบที่ไม่ใช่ วิธีการแบบฟอร์มคือ 'POST' โดยค่าเริ่มต้น แต่สามารถกำหนดเป็น 'GET' โดยใช้setVerb
วิธีการ เมื่อสร้างมาร์กอัปแล้วจะถูกบันทึกไว้ภายใน$form
คุณสมบัติของวัตถุที่ถูกสะท้อนด้วยoutput()
วิธีการ'custom_registration_form'
เชื่อมต่อลงในhook: ในเทมเพลตของฟอร์มเพียงโทรdo_action( 'custom_registration_form' )
ออกจะส่งแบบฟอร์ม
4: เทมเพลตเริ่มต้น
ดังที่ฉันกล่าวว่าเทมเพลตสำหรับแบบฟอร์มสามารถทับได้ง่าย แต่เราต้องการเทมเพลตพื้นฐานเป็นทางเลือก ฉันจะเขียนเทมเพลตคร่าวๆที่นี่เพื่อพิสูจน์แนวคิดมากกว่าเทมเพลตจริง
<?php
// file: default_form_template.php
get_header();
global $custom_reg_form_done, $custom_reg_form_error;
if ( isset( $custom_reg_form_done ) && $custom_reg_form_done ) {
echo '<p class="success">';
_e(
'Thank you, your registration was submitted, check your email.',
'custom_reg_form'
);
echo '</p>';
} else {
if ( $custom_reg_form_error ) {
echo '<p class="error">' . $custom_reg_form_error . '</p>';
}
do_action( 'custom_registration_form' );
}
get_footer();
5: Custom_Reg\FieldInterface
อินเตอร์เฟซ
ทุกฟิลด์ควรเป็นวัตถุที่ใช้อินเทอร์เฟซต่อไปนี้
<?php
// file: FieldInterface.php
namespace Custom_Reg;
interface FieldInterface {
/**
* Return the field id, used to name the request value and for the 'name' param of
* html input field
*/
public function getId();
/**
* Return the filter constant that must be used with
* filter_input so get the value from request
*/
public function getFilter();
/**
* Return true if the used value passed as argument should be accepted, false if not
*/
public function isValid( $value = NULL );
/**
* Return true if field is required, false if not
*/
public function isRequired();
/**
* Return the field input markup. The 'name' param must be output
* according to getId()
*/
public function create( $value = '');
}
ฉันคิดว่าความคิดเห็นอธิบายว่าคลาสใดที่ใช้อินเตอร์เฟสนี้ควรทำอย่างไร
6: การเพิ่มบางฟิลด์
ตอนนี้เราต้องการบางสาขา เราสามารถสร้างไฟล์ชื่อ 'fields.php' โดยที่เรากำหนดคลาสของฟิลด์:
<?php
// file: fields.php
namespace Custom_Reg;
abstract class BaseField implements FieldInterface {
protected function getType() {
return isset( $this->type ) ? $this->type : 'text';
}
protected function getClass() {
$type = $this->getType();
if ( ! empty($type) ) return "{$type}-field";
}
public function getFilter() {
return FILTER_SANITIZE_STRING;
}
public function isRequired() {
return isset( $this->required ) ? $this->required : FALSE;
}
public function isValid( $value = NULL ) {
if ( $this->isRequired() ) {
return $value != '';
}
return TRUE;
}
public function create( $value = '' ) {
$label = '<p><label>' . $this->getLabel() . '</label>';
$format = '<input type="%s" name="%s" value="%s" class="%s"%s /></p>';
$required = $this->isRequired() ? ' required' : '';
return $label . sprintf(
$format,
$this->getType(), $this->getId(), $value, $this->getClass(), $required
);
}
abstract function getLabel();
}
class FullName extends BaseField {
protected $required = TRUE;
public function getID() {
return 'fullname';
}
public function getLabel() {
return __( 'Full Name', 'custom_reg_form' );
}
}
class Login extends BaseField {
protected $required = TRUE;
public function getID() {
return 'login';
}
public function getLabel() {
return __( 'Username', 'custom_reg_form' );
}
}
class Email extends BaseField {
protected $type = 'email';
public function getID() {
return 'email';
}
public function getLabel() {
return __( 'Email', 'custom_reg_form' );
}
public function isValid( $value = NULL ) {
return ! empty( $value ) && filter_var( $value, FILTER_VALIDATE_EMAIL );
}
}
class Country extends BaseField {
protected $required = FALSE;
public function getID() {
return 'country';
}
public function getLabel() {
return __( 'Country', 'custom_reg_form' );
}
}
ฉันใช้คลาสฐานเพื่อกำหนดอินเทอร์เฟซเริ่มต้นการดำเนินการอย่างไรก็ตามหนึ่งสามารถเพิ่มเขตข้อมูลที่กำหนดเองมากโดยตรงการใช้อินเทอร์เฟซหรือขยายคลาสพื้นฐานและเอาชนะวิธีการบางอย่าง
ณ จุดนี้เรามีทุกอย่างที่จะแสดงแบบฟอร์มตอนนี้เราต้องการสิ่งที่จะตรวจสอบและบันทึกเขตข้อมูล
7: Custom_Reg\Saver
ชั้นเรียน
<?php
// file: Saver.php
namespace Custom_Reg;
class Saver {
protected $fields;
protected $user = array( 'user_login' => NULL, 'user_email' => NULL );
protected $meta = array();
protected $error;
public function setFields( \ArrayIterator $fields ) {
$this->fields = $fields;
}
/**
* validate all the fields
*/
public function validate() {
// if registration is not allowed return false
if ( ! get_option('users_can_register') ) return FALSE;
// if no fields are setted return FALSE
if ( ! $this->getFields() instanceof \ArrayIterator ) return FALSE;
// first check nonce
$nonce = $this->getValue( '_n' );
if ( $nonce !== wp_create_nonce( 'custom_reg_form_nonce' ) ) return FALSE;
// then check all fields
$it = $this->getFields();
while( $it->valid() ) {
$field = $it->current();
$key = $field->getID();
if ( ! $field instanceof FieldInterface ) {
throw new \DomainException( "Invalid field" );
}
$value = $this->getValue( $key, $field->getFilter() );
if ( $field->isRequired() && empty($value) ) {
$this->error = sprintf( __('%s is required', 'custom_reg_form' ), $key );
return FALSE;
}
if ( ! $field->isValid( $value ) ) {
$this->error = sprintf( __('%s is not valid', 'custom_reg_form' ), $key );
return FALSE;
}
if ( in_array( "user_{$key}", array_keys($this->user) ) ) {
$this->user["user_{$key}"] = $value;
} else {
$this->meta[$key] = $value;
}
$it->next();
}
return TRUE;
}
/**
* Save the user using core register_new_user that handle username and email check
* and also sending email to new user
* in addition save all other custom data in user meta
*
* @see register_new_user()
*/
public function save() {
// if registration is not allowed return false
if ( ! get_option('users_can_register') ) return FALSE;
// check mandatory fields
if ( ! isset($this->user['user_login']) || ! isset($this->user['user_email']) ) {
return false;
}
$user = register_new_user( $this->user['user_login'], $this->user['user_email'] );
if ( is_numeric($user) ) {
if ( ! update_user_meta( $user, 'custom_data', $this->meta ) ) {
wp_delete_user($user);
return FALSE;
}
return TRUE;
} elseif ( is_wp_error( $user ) ) {
$this->error = $user->get_error_message();
}
return FALSE;
}
public function getValue( $var, $filter = FILTER_SANITIZE_STRING ) {
if ( ! is_string($var) ) {
throw new \InvalidArgumentException( "Invalid value" );
}
$method = strtoupper( filter_input( INPUT_SERVER, 'REQUEST_METHOD' ) );
$type = $method === 'GET' ? INPUT_GET : INPUT_POST;
$val = filter_input( $type, $var, $filter );
return $val;
}
public function getFields() {
return $this->fields;
}
public function getErrorMessage() {
return $this->error;
}
}
คลาสนั้นมี 2 วิธีหลักหนึ่งวิธี ( validate
) ที่วนรอบฟิลด์ตรวจสอบและบันทึกข้อมูลที่ดีลงในอาร์เรย์ส่วนที่สอง ( save
) บันทึกข้อมูลทั้งหมดในฐานข้อมูลและส่งรหัสผ่านทางอีเมลไปยังผู้ใช้ใหม่
8: การใช้คลาสที่กำหนด: สิ้นสุดCustom_Reg
คลาส
ตอนนี้เราสามารถทำงานอีกครั้งในCustom_Reg
ชั้นเรียนเพิ่มวิธีการที่ "ติดกาว" วัตถุที่กำหนดและทำให้พวกเขาทำงาน
<?php
// file Custom_Reg.php
namespace Custom_Reg;
class Custom_Reg {
protected $form;
protected $saver;
function __construct( Form $form, Saver $saver ) {
$this->form = $form;
$this->saver = $saver;
}
/**
* Check if the url to recognize is the one for the registration form page
*/
function checkUrl() {
$url_part = $this->getUrl();
$nonce = urlencode( wp_create_nonce( 'registration_url' ) );
if ( ( $url_part === $nonce ) ) {
// do nothing if registration is not allowed or user logged
if ( is_user_logged_in() || ! get_option('users_can_register') ) {
wp_safe_redirect( home_url() );
exit();
}
return TRUE;
}
}
/**
* Init the form, if submitted validate and save, if not just display it
*/
function init() {
if ( $this->checkUrl() !== TRUE ) return;
do_action( 'custom_reg_form_init', $this->form );
if ( $this->isSubmitted() ) {
$this->save();
}
// don't need to create form if already saved
if ( ! isset( $custom_reg_form_done ) || ! $custom_reg_form_done ) {
$this->form->create();
}
load_template( $this->getTemplate() );
exit();
}
protected function save() {
global $custom_reg_form_error;
$this->saver->setFields( $this->form->getFields() );
if ( $this->saver->validate() === TRUE ) { // validate?
if ( $this->saver->save() ) { // saved?
global $custom_reg_form_done;
$custom_reg_form_done = TRUE;
} else { // saving error
$err = $this->saver->getErrorMessage();
$custom_reg_form_error = $err ? : __( 'Error on save.', 'custom_reg_form' );
}
} else { // validation error
$custom_reg_form_error = $this->saver->getErrorMessage();
}
}
protected function isSubmitted() {
$type = $this->form->getVerb() === 'GET' ? INPUT_GET : INPUT_POST;
$sub = filter_input( $type, 'custom_reg_form', FILTER_SANITIZE_STRING );
return ( ! empty( $sub ) && $sub === get_class( $this->form ) );
}
protected function getTemplate() {
$base = $this->form->getTemplate() ? : FALSE;
$template = FALSE;
$default = dirname( __FILE__ ) . '/default_form_template.php';
if ( ! empty( $base ) ) {
$template = locate_template( $base );
}
return $template ? : $default;
}
protected function getUrl() {
$home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' );
$relative = trim( str_replace( $home_path, '', add_query_arg( array() ) ), '/' );
$parts = explode( '/', $relative );
if ( ! empty( $parts ) && ! isset( $parts[1] ) ) {
return $parts[0];
}
}
}
ตัวสร้างของคลาสยอมรับตัวอย่างForm
และหนึ่งในSaver
นั้น
init()
method (using checkUrl()
) ดูที่ส่วนแรกของ url after home_url()
และถ้าตรงกับ nonce ที่ถูกต้องมันจะตรวจสอบว่าฟอร์มถูกส่งไปแล้วหรือไม่ถ้าใช้Saver
object มันจะทำการตรวจสอบและบันทึก userdata มิฉะนั้นเพียงพิมพ์ฟอร์ม .
init()
วิธีการยังยิงตะขอการกระทำที่'custom_reg_form_init'
ผ่านอินสแตนซ์ของรูปแบบเป็นอาร์กิวเมนต์: เบ็ดนี้ควรใช้ในการเพิ่มเขตข้อมูลเพื่อติดตั้งแม่แบบที่กำหนดเองและเพื่อปรับแต่งวิธีการแบบฟอร์ม
9: รวบรวมทุกอย่างเข้าด้วยกัน
ตอนนี้เราต้องเขียนไฟล์ปลั๊กอินหลักซึ่งเราสามารถทำได้
- ต้องการไฟล์ทั้งหมด
- โหลดโดเมนข้อความ
- กระบวนการทั้งหมดเริ่มต้นโดยใช้
Custom_Reg
คลาสinstantiating และinit()
วิธีการโทรกับมันโดยใช้เบ็ดแรกพอสมควร
- ใช้ 'custom_reg_form_init' เพื่อเพิ่มฟิลด์ลงในคลาสฟอร์ม
ดังนั้น:
<?php
/**
* Plugin Name: Custom Registration Form
* Description: Just a rough plugin example to answer a WPSE question
* Plugin URI: https://wordpress.stackexchange.com/questions/10309/
* Author: G. M.
* Author URI: https://wordpress.stackexchange.com/users/35541/g-m
*
*/
if ( is_admin() ) return; // this plugin is all about frontend
load_plugin_textdomain(
'custom_reg_form',
FALSE,
plugin_dir_path( __FILE__ ) . 'langs'
);
require_once plugin_dir_path( __FILE__ ) . 'FieldInterface.php';
require_once plugin_dir_path( __FILE__ ) . 'fields.php';
require_once plugin_dir_path( __FILE__ ) . 'Form.php';
require_once plugin_dir_path( __FILE__ ) . 'Saver.php';
require_once plugin_dir_path( __FILE__ ) . 'CustomReg.php';
/**
* Generate dynamic registration url
*/
function custom_registration_url() {
$nonce = urlencode( wp_create_nonce( 'registration_url' ) );
return home_url( $nonce );
}
/**
* Generate dynamic registration link
*/
function custom_registration_link() {
$format = '<a href="%s">%s</a>';
printf(
$format,
custom_registration_url(), __( 'Register', 'custom_reg_form' )
);
}
/**
* Setup, show and save the form
*/
add_action( 'wp_loaded', function() {
try {
$form = new Custom_Reg\Form;
$saver = new Custom_Reg\Saver;
$custom_reg = new Custom_Reg\Custom_Reg( $form, $saver );
$custom_reg->init();
} catch ( Exception $e ) {
if ( defined('WP_DEBUG') && WP_DEBUG ) {
$msg = 'Exception on ' . __FUNCTION__;
$msg .= ', Type: ' . get_class( $e ) . ', Message: ';
$msg .= $e->getMessage() ? : 'Unknown error';
error_log( $msg );
}
wp_safe_redirect( home_url() );
}
}, 0 );
/**
* Add fields to form
*/
add_action( 'custom_reg_form_init', function( $form ) {
$classes = array(
'Custom_Reg\FullName',
'Custom_Reg\Login',
'Custom_Reg\Email',
'Custom_Reg\Country'
);
foreach ( $classes as $class ) {
$form->addField( new $class );
}
}, 1 );
10: งานที่ขาดหายไป
ตอนนี้ค่อนข้างเรียบร้อยแล้ว เราเพียงแค่ปรับแต่งเทมเพลตซึ่งอาจเพิ่มไฟล์เทมเพลตที่กำหนดเองในธีมของเรา
เราสามารถเพิ่มสไตล์และสคริปต์เฉพาะลงในหน้าการลงทะเบียนที่กำหนดเองด้วยวิธีนี้
add_action( 'wp_enqueue_scripts', function() {
// if not on custom registration form do nothing
if ( did_action('custom_reg_form_init') ) {
wp_enqueue_style( ... );
wp_enqueue_script( ... );
}
});
โดยใช้วิธีการที่เราสามารถ enqueue js บางสคริปต์เพื่อตรวจสอบด้านการจัดการลูกค้าเช่นนี้ มาร์กอัปที่จำเป็นในการทำให้งานสคริปต์นั้นสามารถจัดการได้อย่างง่ายดายแก้ไขCustom_Reg\BaseField
ชั้นเรียน
หากเราต้องการปรับแต่งอีเมลการลงทะเบียนเราสามารถใช้วิธีมาตรฐานและบันทึกข้อมูลที่กำหนดเองในเมตาเราสามารถใช้ประโยชน์จากอีเมลเหล่านั้นได้
งานสุดท้ายที่เราอาจต้องการนำมาใช้คือป้องกันการร้องขอไปยังแบบฟอร์มการลงทะเบียนเริ่มต้นง่ายเหมือน:
add_action( 'login_form_register', function() { exit(); } );
ไฟล์ทั้งหมดที่สามารถพบได้ในกระทู้ที่นี่