Prosesnya melibatkan 2 langkah:
- perlihatkan formulir frontend
- simpan data pada saat pengiriman
Ada 3 pendekatan berbeda yang muncul di pikiran saya untuk menunjukkan frontend:
- gunakan formulir pendaftaran bawaan, gaya pengeditan, dll untuk menjadikannya lebih "seperti tampilan depan"
- menggunakan halaman / posting WordPress, dan menampilkan formulir menggunakan kode pendek
- gunakan template khusus yang tidak terhubung dengan halaman / postingan apa pun, tetapi dipanggil oleh url tertentu
Untuk jawaban ini saya akan menggunakan yang terakhir. Alasannya adalah:
- menggunakan formulir registrasi internal bisa menjadi ide yang bagus, kustomisasi mendalam bisa sangat sulit menggunakan formulir built-in, dan jika orang juga ingin mengkustomisasi bidang formulir peningkatan rasa sakit
- menggunakan halaman WordPress dalam kombinasi dengan kode pendek, tidak begitu dapat diandalkan, dan juga saya pikir shorcodes tidak boleh digunakan untuk fungsionalitas, hanya untuk pemformatan dan semacamnya.
1: Bangun url
Kita semua tahu bahwa formulir pendaftaran default dari situs WordPress sering menjadi target bagi spammer. Menggunakan url khusus adalah bantuan untuk menyelesaikan masalah ini. Selain itu saya ingin juga menggunakan url variabel , yaitu bentuk pendaftaran url tidak harus selalu sama, ini membuat hidup spammer lebih sulit. Caranya dilakukan menggunakan nonce di 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' )
);
}
Menggunakan fungsi ini mudah ditampilkan di templat tautan ke formulir pendaftaran meskipun dinamis.
2: Kenali url, rintisan pertama dari Custom_Reg\Custom_Reg
kelas pertama
Sekarang kita perlu mengenali url. Untuk tujuan saya akan mulai menulis kelas, yang akan selesai nanti dalam jawabannya:
<?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];
}
}
}
Fungsi melihat bagian pertama url setelahnya home_url()
, dan jika cocok dengan awal kita, ia mengembalikan BENAR. fungsi ini akan digunakan untuk memeriksa permintaan kami dan melakukan tindakan yang diperlukan untuk menampilkan formulir kami.
3: Custom_Reg\Form
Kelas
Sekarang saya akan menulis kelas, yang akan bertanggung jawab untuk menghasilkan markup formulir. Saya akan menggunakannya juga untuk menyimpan di properti path file template yang harus digunakan untuk menampilkan formulir.
<?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;
}
}
Kelas menghasilkan loop markup form yang melingkari semua bidang yang ditambahkan create
metode panggilan pada masing-masingnya. Setiap bidang harus merupakan instance dari Custom_Reg\FieldInterface
. Bidang tersembunyi tambahan ditambahkan untuk verifikasi nonce. Metode formulir adalah 'POST' secara default, tetapi dapat disetel ke 'GET' menggunakan setVerb
metode. Setelah dibuat markup disimpan di dalam $form
properti objek yang digaungkan dengan output()
metode, terhubung ke 'custom_registration_form'
kait: di Templat formulir, cukup panggildo_action( 'custom_registration_form' )
akan menampilkan formulir.
4: Template default
Seperti yang saya katakan templat untuk formulir dapat dengan mudah diganti, namun kita membutuhkan templat dasar sebagai cadangan. Saya akan menulis di sini template yang sangat kasar, lebih merupakan bukti konsep daripada template nyata.
<?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
Antarmuka
Setiap bidang harus menjadi objek yang mengimplementasikan antarmuka berikut
<?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 = '');
}
Saya pikir komentar menjelaskan kelas apa yang harus dilakukan dengan antarmuka ini.
6: Menambahkan beberapa bidang
Sekarang kita perlu beberapa bidang. Kita dapat membuat file bernama 'fields.php' di mana kita mendefinisikan kelas bidang:
<?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' );
}
}
Saya telah menggunakan kelas dasar untuk mendefinisikan implemantasi antarmuka standar, namun, seseorang dapat menambahkan bidang yang sangat khusus secara langsung mengimplementasikan antarmuka atau memperluas kelas dasar dan mengganti beberapa metode.
Pada titik ini kami memiliki segalanya untuk menampilkan formulir, sekarang kami membutuhkan sesuatu untuk memvalidasi dan menyimpan bidang.
7: Custom_Reg\Saver
Kelas
<?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;
}
}
Kelas itu, memiliki 2 metode utama, satu ( validate
) yang loop bidang, memvalidasinya dan menyimpan data yang baik ke dalam array, yang kedua ( save
) menyimpan semua data dalam database dan mengirim kata sandi melalui email ke pengguna baru.
8: Menggunakan kelas yang ditentukan: menyelesaikan Custom_Reg
kelas
Sekarang kita dapat bekerja lagi di Custom_Reg
kelas, menambahkan metode yang "menempelkan" objek yang didefinisikan dan membuatnya bekerja
<?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];
}
}
}
Konstruktor kelas menerima turunan dari Form
dan salah satu dari Saver
.
init()
metode (menggunakan checkUrl()
) lihat bagian pertama dari url setelahnya home_url()
, dan jika cocok dengan angka yang tepat, ia memeriksa apakah formulir sudah dikirim, jika menggunakan Saver
objek, ia memvalidasi dan menyimpan data pengguna, jika tidak cukup cetak formulir .
init()
metode juga menembakkan tindakan kait 'custom_reg_form_init'
melewati contoh formulir sebagai argumen: kait ini harus digunakan untuk menambahkan bidang, untuk menyiapkan Templat kustom dan juga untuk menyesuaikan metode formulir.
9: Menyatukan semuanya
Sekarang kita perlu menulis file plugin utama, di mana kita bisa
- memerlukan semua file
- memuat teksdomain
- mulai seluruh proses menggunakan
Custom_Reg
kelas dan panggilan instantiatinginit()
metode di atasnya menggunakan hook yang cukup awal
- gunakan 'custom_reg_form_init' untuk menambahkan bidang untuk membentuk kelas
Begitu:
<?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: Tugas yang hilang
Sekarang everithing sudah cukup. Kami baru saja menyesuaikan template, mungkin menambahkan file template kustom di tema kami.
Kami dapat menambahkan gaya dan skrip khusus hanya ke halaman pendaftaran khusus dengan cara ini
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( ... );
}
});
Dengan menggunakan metode itu kita dapat membuat beberapa skrip js untuk menangani validasi sisi klien, misalnya yang ini . Markup yang diperlukan untuk membuat skrip itu bekerja dapat dengan mudah ditangani mengedit Custom_Reg\BaseField
kelas.
Jika kami ingin menyesuaikan email pendaftaran, kami dapat menggunakan metode standar dan menyimpan data khusus di meta, kami dapat menggunakannya di email.
Tugas terakhir yang kami mungkin ingin kami terapkan adalah mencegah permintaan ke formulir pendaftaran default, semudah:
add_action( 'login_form_register', function() { exit(); } );
Semua file dapat ditemukan di Intisari di sini .