İşlem 2 adımdan oluşuyor:
- ön formu göster
- gönderimdeki verileri kaydet
Frontend'i göstermek için aklıma gelen 3 farklı yaklaşım var:
- daha "ön uç gibi" yapmak için yerleşik kayıt formunu, stilleri düzenleme vb.
- bir WordPress sayfası / postası kullanın ve bir kısa kod kullanarak formu görüntüleyin
- Herhangi bir sayfaya / yazıya bağlı olmayan, ancak belirli bir URL ile çağrılan özel bir şablon kullanın.
Bu cevap için ikincisini kullanacağım. Sebepler:
- yerleşik kayıt formunu kullanmak iyi bir fikir olabilir, yerleşik form kullanarak derin özelleştirmeler çok zor olabilir ve eğer form alanlarını özelleştirmek isterseniz ağrı artar
- bir WordPress sayfasını bir kısa kodla birlikte kullanmak çok güvenilir değildir ve ayrıca kısa biçimlerin işlevsellik için kullanılmaması gerektiğini düşünüyorum.
1: URL'yi oluşturun
Hepimiz bir WordPress sitesinin varsayılan kayıt formunun spam gönderenler için bir hedef olduğunu biliyoruz. Özel bir URL kullanmak, bu sorunu çözmek için bir yardımdır. Ayrıca değişken bir url de kullanmak istiyorum , yani kayıt formu url'si her zaman aynı olmamalıdır, bu spam gönderenlerin hayatını zorlaştırır. Hile url'de bir noce kullanarak yapılır :
/**
* 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' )
);
}
Bu işlevleri kullanmak, şablonlarda dinamik olsa bile kayıt formuna bir bağlantıyı görüntülemek kolaydır.
2: Url’yi, Custom_Reg\Custom_Reg
sınıfın ilk saplamasını tanıyın
Şimdi url'yi tanımamız gerekiyor. Pourpose için bir cevap yazmaya başlayacağım, sonra bir sınıf yazmaya başlayacağım:
<?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];
}
}
}
İşlev, url'nin ilk kısmına bakar ve home_url()
bizim olmayanımızla eşleşirse TRUE değerini döndürür. Bu fonksiyon isteğimizi kontrol etmek ve formumuzu göstermek için gerekli işlemleri yapmak için kullanılacaktır.
3: Custom_Reg\Form
sınıf
Şimdi bir form yazacağım, form biçimlendirmesini oluşturmaktan sorumlu olacak. Bir özelliği, formu görüntülemek için kullanılması gereken şablon dosya yolunu da saklamak için kullanacağım.
<?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;
}
}
Sınıf create
, her biri için arama yöntemi eklenmiş tüm alanları kapsayan form biçimlendirme oluşturur . Her alanın bir örneği olması gerekir Custom_Reg\FieldInterface
. Uygun olmayan doğrulama için ek bir gizli alan eklenir. Form yöntemi varsayılan olarak 'POST', ancak setVerb
yöntem kullanılarak 'GET' olarak ayarlanabilir . Oluşturulduktan sonra, işaretleme yöntemle $form
yankılanan nesne özelliğinin içine kaydedilir output()
, kancaya 'custom_registration_form'
bağlanır: form şablonunda çağrı yalnızca do_action( 'custom_registration_form' )
form çıktısını alır.
4: Varsayılan şablon
Dediğim gibi form için şablon kolayca geçersiz kılınabilir, ancak geri dönüş olarak temel bir şablona ihtiyacımız var. Buraya çok kaba bir şablon, gerçek bir şablondan çok bir kavram kanıtı yazdım.
<?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
arayüz
Her alan aşağıdaki arayüzü uygulayan bir nesne olmalıdır
<?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 = '');
}
Bu arayüzün uygulandığı sınıfların ne yapması gerektiğini açıkladığını düşünüyorum.
6: Bazı alanlar ekleme
Şimdi bazı alanlara ihtiyacımız var. Fields sınıflarını tanımladığımız 'fields.php' adında bir dosya oluşturabiliriz:
<?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' );
}
}
Varsayılan arabirim uygulamasını tanımlamak için bir temel sınıf kullanıyorum, ancak bir tanesi doğrudan arabirimi uygulayan veya temel sınıfı genişleten ve bazı yöntemleri geçersiz kılan çok özelleştirilmiş alanlar ekleyebilir.
Bu noktada formu görüntülemek için her şeye sahibiz, şimdi alanları doğrulamak ve kaydetmek için bir şeye ihtiyacımız var.
7: Custom_Reg\Saver
sınıf
<?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;
}
}
Bu sınıf, validate
alanları dolaştıran, bunları doğrulayan ve iyi verileri bir diziye kaydeden bir ( ) 2 ana yönteme sahiptir , ikincisi ( save
) tüm verileri veritabanına kaydeder ve e-posta yoluyla yeni kullanıcıya parola gönderir.
8: Tanımlanmış sınıfları kullanmak: Custom_Reg
sınıfı bitirmek
Şimdi tekrar Custom_Reg
sınıfta çalışabiliriz , tanımlanan nesneyi "yapıştırma" yöntemlerini ekleyerek bunları çalıştırabiliriz.
<?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];
}
}
}
Sınıfın yapıcı bir örneğini kabul Form
ve biri Saver
.
init()
yöntem (kullanma checkUrl()
) sonra url'nin ilk bölümüne home_url()
bakar ve doğru noce ile eşleşirse, formun zaten gönderilip gönderilmediğini kontrol eder, eğer öyleyse Saver
nesneyi kullanarak kullanıcı verilerini doğrular ve saklar, aksi takdirde sadece formu yazdırır .
init()
yöntem 'custom_reg_form_init'
, form örneğini argüman olarak geçen eylem kancasını da ateşler : bu kanca, alanlar eklemek, özel şablonu ayarlamak ve ayrıca form yöntemini özelleştirmek için kullanılmalıdır.
9: Bir şeyleri bir araya getirmek
Şimdi ana eklenti dosyasını yazabileceğiz, nereye yapabiliriz
- tüm dosyaları gerektir
- textdomain'i yükleyin
- Oldukça erken olan bir kanca kullanarak, örneğe hazır
Custom_Reg
sınıf ve çağrı init()
yöntemini kullanarak tüm süreci başlatmak
- Form sınıfına alanları eklemek için 'custom_reg_form_init' kullanın
Yani:
<?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: Kayıp görevler
Şimdi her şey çok güzel bitti. Sadece şablonu özelleştirmek zorundayız, muhtemelen temamıza özel bir şablon dosyası ekliyoruz.
Bu yolla yalnızca özel kayıt sayfasına özel stiller ve komut dosyaları ekleyebiliriz.
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( ... );
}
});
Bu yöntemi kullanarak, müşteri tarafı doğrulamasını yapmak için bazı js betiklerini sıkıştırabiliriz, örneğin bu . Bu betiğin çalışmasını sağlamak için gereken işaretleme, Custom_Reg\BaseField
sınıfı düzenlemeyle kolayca yapılabilir .
Kayıt e-postasını özelleştirmek istiyorsak, standart yöntemi kullanabiliriz ve metada kaydedilmiş özel verilerimiz varsa, bunları e-postada kullanabiliriz.
Muhtemelen uygulamak istediğimiz son görev, varsayılan kayıt formuna olan talebi engellemektir, bu kadar kolay:
add_action( 'login_form_register', function() { exit(); } );
Tüm dosyalar burada bir Gist'te bulunabilir .