WordPress’te Özel Profil Alanları Ekleme
WordPress kullanıcı profillerini genişletmek, özellikle kurumsal sitelerde ve e-ticaret projelerinde sık karşılaştığımız bir ihtiyaç. Varsayılan profil sayfası çok kısıtlı kalıyor; telefon numarası yok, departman bilgisi yok, sosyal medya linkleri eksik. İşte bu noktada functions.php devreye giriyor ve işleri ciddi ölçüde kolaylaştırıyor.
Neden Özel Profil Alanları Gerekli?
Standart WordPress kullanıcı profili temel bilgileri tutuyor: isim, e-posta, biyografi, birkaç sosyal medya linki. Ama gerçek dünya senaryolarında bu yetmiyor.
Bir müşteri portalı geliştiriyorsunuz diyelim. Kullanıcının şirket adı, vergi numarası, teslimat adresi, iletişim telefonu gibi bilgileri saklamanız gerekiyor. Ya da bir intranet sitesi kuruyorsunuz; çalışanın departmanı, pozisyonu, dahili telefonu, acil iletişim kişisi lazım. WooCommerce entegrasyonunda ise ek fatura adresi alanları, müşteri tipi (bireysel/kurumsal), indirim kodu gibi özel veriler profilde tutulabilir.
Bu ihtiyaçları karşılamak için plugin kullanmak her zaman doğru yol değil. Onlarca plugin yerine birkaç satır functions.php kodu çok daha temiz ve performanslı bir çözüm sunuyor.
Temel Yaklaşım: Hook’lar ve Meta Fonksiyonları
WordPress profil alanları iki temel hook üzerine kurulu:
show_user_profile: Kullanıcı kendi profilini düzenlediğinde çalışıredit_user_profile: Yönetici başka bir kullanıcının profilini düzenlediğinde çalışırpersonal_options_update: Kullanıcı kendi profilini kaydettiğinde çalışıredit_user_profile_update: Yönetici başka bir kullanıcıyı kaydettiğinde çalışır
Veri depolamak için update_user_meta(), okumak için get_user_meta() fonksiyonlarını kullanıyoruz. Bu meta veriler wp_usermeta tablosunda saklanıyor.
İlk Örnek: Basit Telefon Numarası Alanı
En basit senaryoyla başlayalım. Profil sayfasına telefon numarası alanı ekleyelim.
// functions.php
// Profil formuna telefon alanı ekle
function add_phone_field_to_profile( $user ) {
?>
<h3>İletişim Bilgileri</h3>
<table class="form-table">
<tr>
<th><label for="phone_number">Telefon Numarası</label></th>
<td>
<input type="tel"
name="phone_number"
id="phone_number"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'phone_number', true ) ); ?>"
class="regular-text" />
<p class="description">Cep telefonu veya sabit hat numaranız</p>
</td>
</tr>
</table>
<?php
}
add_action( 'show_user_profile', 'add_phone_field_to_profile' );
add_action( 'edit_user_profile', 'add_phone_field_to_profile' );
// Telefon alanını kaydet
function save_phone_field( $user_id ) {
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return false;
}
if ( isset( $_POST['phone_number'] ) ) {
update_user_meta( $user_id, 'phone_number', sanitize_text_field( $_POST['phone_number'] ) );
}
}
add_action( 'personal_options_update', 'save_phone_field' );
add_action( 'edit_user_profile_update', 'save_phone_field' );
Güvenlik açısından dikkat edilmesi gereken iki nokta var. Birincisi, current_user_can() kontrolü mutlaka yapılmalı. İkincisi, kaydedilen veri sanitize_text_field() ile temizlenmeli. Bu iki adımı atlamak ciddi güvenlik açıklarına yol açabilir.
Çoklu Alan Ekleme: Kurumsal Profil Örneği
Şimdi daha gerçekçi bir senaryo. Bir kurumsal intranet sitesi için kullanıcı profiline şirket bilgileri ekleyelim.
// functions.php
function add_company_profile_fields( $user ) {
$department = get_user_meta( $user->ID, 'department', true );
$position = get_user_meta( $user->ID, 'position', true );
$internal_ext = get_user_meta( $user->ID, 'internal_ext', true );
$start_date = get_user_meta( $user->ID, 'start_date', true );
?>
<h3>Şirket Bilgileri</h3>
<table class="form-table">
<tr>
<th><label for="department">Departman</label></th>
<td>
<select name="department" id="department">
<option value="">-- Seçiniz --</option>
<?php
$departments = array( 'IT', 'Muhasebe', 'İnsan Kaynakları', 'Pazarlama', 'Satış', 'Üretim' );
foreach ( $departments as $dept ) {
echo '<option value="' . esc_attr( $dept ) . '" ' . selected( $department, $dept, false ) . '>' . esc_html( $dept ) . '</option>';
}
?>
</select>
</td>
</tr>
<tr>
<th><label for="position">Pozisyon</label></th>
<td>
<input type="text" name="position" id="position"
value="<?php echo esc_attr( $position ); ?>"
class="regular-text" />
</td>
</tr>
<tr>
<th><label for="internal_ext">Dahili Hat</label></th>
<td>
<input type="number" name="internal_ext" id="internal_ext"
value="<?php echo esc_attr( $internal_ext ); ?>"
class="small-text" />
</td>
</tr>
<tr>
<th><label for="start_date">İşe Başlama Tarihi</label></th>
<td>
<input type="date" name="start_date" id="start_date"
value="<?php echo esc_attr( $start_date ); ?>" />
</td>
</tr>
</table>
<?php
}
add_action( 'show_user_profile', 'add_company_profile_fields' );
add_action( 'edit_user_profile', 'add_company_profile_fields' );
function save_company_profile_fields( $user_id ) {
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return false;
}
$fields = array( 'department', 'position', 'internal_ext', 'start_date' );
foreach ( $fields as $field ) {
if ( isset( $_POST[ $field ] ) ) {
update_user_meta( $user_id, $field, sanitize_text_field( $_POST[ $field ] ) );
}
}
}
add_action( 'personal_options_update', 'save_company_profile_fields' );
add_action( 'edit_user_profile_update', 'save_company_profile_fields' );
Bu yaklaşımda $fields dizisi üzerinden döngü kurmak kodun bakımını kolaylaştırıyor. Yeni bir alan eklemek istediğinizde sadece diziye eleman eklemeniz yeterli.
Dosya Yükleme Alanı: Profil Fotoğrafı Alternatifi
WordPress’in varsayılan Gravatar sistemi yerine kullanıcıların kendi fotoğraflarını yüklemesini istiyorsanız bu örnek işinize yarayacak.
// functions.php
function add_profile_picture_field( $user ) {
$profile_pic = get_user_meta( $user->ID, 'custom_profile_pic', true );
?>
<h3>Profil Fotoğrafı</h3>
<table class="form-table">
<tr>
<th><label for="custom_profile_pic">Fotoğraf Yükle</label></th>
<td>
<?php if ( $profile_pic ) : ?>
<img src="<?php echo esc_url( $profile_pic ); ?>"
style="width:100px; height:100px; object-fit:cover; border-radius:50%; margin-bottom:10px;" />
<br>
<label>
<input type="checkbox" name="remove_profile_pic" value="1" />
Mevcut fotoğrafı kaldır
</label>
<br><br>
<?php endif; ?>
<input type="file" name="custom_profile_pic" id="custom_profile_pic" accept="image/*" />
<p class="description">Maksimum 2MB, JPG veya PNG formatı önerilir.</p>
</td>
</tr>
</table>
<?php
}
add_action( 'show_user_profile', 'add_profile_picture_field' );
add_action( 'edit_user_profile', 'add_profile_picture_field' );
function save_profile_picture_field( $user_id ) {
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return false;
}
// Fotoğraf kaldırma isteği
if ( isset( $_POST['remove_profile_pic'] ) && $_POST['remove_profile_pic'] == '1' ) {
delete_user_meta( $user_id, 'custom_profile_pic' );
return;
}
// Yeni fotoğraf yükleme
if ( ! empty( $_FILES['custom_profile_pic']['name'] ) ) {
require_once( ABSPATH . 'wp-admin/includes/file.php' );
require_once( ABSPATH . 'wp-admin/includes/image.php' );
require_once( ABSPATH . 'wp-admin/includes/media.php' );
$attachment_id = media_handle_upload( 'custom_profile_pic', 0 );
if ( ! is_wp_error( $attachment_id ) ) {
$image_url = wp_get_attachment_url( $attachment_id );
update_user_meta( $user_id, 'custom_profile_pic', esc_url_raw( $image_url ) );
}
}
}
add_action( 'personal_options_update', 'save_profile_picture_field' );
add_action( 'edit_user_profile_update', 'save_profile_picture_field' );
Dosya yükleme işleminde form tagına enctype="multipart/form-data" özelliği eklemek gerekiyor. Bunun için de ek bir hook kullanıyoruz.
// Profil formuna enctype ekle
function add_enctype_to_profile_form() {
echo ' enctype="multipart/form-data"';
}
add_action( 'user_edit_form_tag', 'add_enctype_to_profile_form' );
WooCommerce Entegrasyonu: Müşteri Profil Alanları
E-ticaret sitelerinde müşteri profilini genişletmek çok değerli. Örneğin B2B bir mağazada müşteri tipine göre farklı fiyatlandırma yapıyorsanız bu bilgiyi profilde tutabilirsiniz.
// functions.php
function add_woo_customer_fields( $user ) {
// Sadece subscriber veya customer rolündeki kullanıcılara göster
if ( ! in_array( 'customer', (array) $user->roles ) && ! in_array( 'subscriber', (array) $user->roles ) ) {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
}
$customer_type = get_user_meta( $user->ID, 'customer_type', true );
$tax_number = get_user_meta( $user->ID, 'tax_number', true );
$company_name = get_user_meta( $user->ID, 'company_name', true );
$discount_rate = get_user_meta( $user->ID, 'discount_rate', true );
?>
<h3>Müşteri Bilgileri</h3>
<table class="form-table">
<tr>
<th><label for="customer_type">Müşteri Tipi</label></th>
<td>
<select name="customer_type" id="customer_type">
<option value="bireysel" <?php selected( $customer_type, 'bireysel' ); ?>>Bireysel</option>
<option value="kurumsal" <?php selected( $customer_type, 'kurumsal' ); ?>>Kurumsal</option>
<option value="bayi" <?php selected( $customer_type, 'bayi' ); ?>>Bayi</option>
</select>
</td>
</tr>
<tr class="kurumsal-field">
<th><label for="company_name">Firma Adı</label></th>
<td>
<input type="text" name="company_name" id="company_name"
value="<?php echo esc_attr( $company_name ); ?>"
class="regular-text" />
</td>
</tr>
<tr class="kurumsal-field">
<th><label for="tax_number">Vergi Numarası</label></th>
<td>
<input type="text" name="tax_number" id="tax_number"
value="<?php echo esc_attr( $tax_number ); ?>"
class="regular-text" />
<p class="description">Bireysel kullanıcılar için TC kimlik numarası</p>
</td>
</tr>
<?php if ( current_user_can( 'manage_options' ) ) : ?>
<tr>
<th><label for="discount_rate">İndirim Oranı (%)</label></th>
<td>
<input type="number" name="discount_rate" id="discount_rate"
value="<?php echo esc_attr( $discount_rate ); ?>"
min="0" max="100" class="small-text" />
<p class="description">Sadece yöneticiler bu alanı değiştirebilir.</p>
</td>
</tr>
<?php endif; ?>
</table>
<?php
}
add_action( 'show_user_profile', 'add_woo_customer_fields' );
add_action( 'edit_user_profile', 'add_woo_customer_fields' );
function save_woo_customer_fields( $user_id ) {
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return false;
}
$allowed_types = array( 'bireysel', 'kurumsal', 'bayi' );
if ( isset( $_POST['customer_type'] ) && in_array( $_POST['customer_type'], $allowed_types ) ) {
update_user_meta( $user_id, 'customer_type', $_POST['customer_type'] );
}
if ( isset( $_POST['company_name'] ) ) {
update_user_meta( $user_id, 'company_name', sanitize_text_field( $_POST['company_name'] ) );
}
if ( isset( $_POST['tax_number'] ) ) {
// Vergi numarasından sadece rakamları al
$tax_number = preg_replace( '/[^0-9]/', '', $_POST['tax_number'] );
update_user_meta( $user_id, 'tax_number', $tax_number );
}
// İndirim oranı sadece admin değiştirebilir
if ( current_user_can( 'manage_options' ) && isset( $_POST['discount_rate'] ) ) {
$rate = absint( $_POST['discount_rate'] );
$rate = min( 100, max( 0, $rate ) );
update_user_meta( $user_id, 'discount_rate', $rate );
}
}
add_action( 'personal_options_update', 'save_woo_customer_fields' );
add_action( 'edit_user_profile_update', 'save_woo_customer_fields' );
Bu örnekte dikkat çeken bir detay var: indirim oranını sadece yöneticilerin değiştirebilmesi için çift kontrol yapıyoruz. Hem alanı sadece yöneticilere gösteriyoruz hem de kaydetme sırasında tekrar current_user_can('manage_options') kontrolü yapıyoruz.
Kayıt Formuna Özel Alan Ekleme
Profil sayfası yeterli değil, kullanıcının kayıt olurken de bu bilgileri girmesini isteyebilirsiniz.
// functions.php
// Kayıt formuna alan ekle
function add_phone_to_registration_form() {
$phone = isset( $_POST['phone_number'] ) ? sanitize_text_field( $_POST['phone_number'] ) : '';
?>
<p>
<label for="phone_number">Telefon Numarası<br />
<input type="tel" name="phone_number" id="phone_number"
class="input" value="<?php echo esc_attr( $phone ); ?>" size="25" />
</label>
</p>
<?php
}
add_action( 'register_form', 'add_phone_to_registration_form' );
// Kayıt sırasında doğrulama
function validate_phone_on_registration( $errors, $sanitized_user_login, $user_email ) {
if ( isset( $_POST['phone_number'] ) && empty( $_POST['phone_number'] ) ) {
$errors->add( 'phone_error', '<strong>HATA:</strong> Telefon numarası zorunludur.' );
}
if ( isset( $_POST['phone_number'] ) && ! empty( $_POST['phone_number'] ) ) {
$phone = sanitize_text_field( $_POST['phone_number'] );
// Basit bir Türkiye telefon format kontrolü
if ( ! preg_match( '/^(+90|0)?[5][0-9]{9}$/', preg_replace('/s/', '', $phone) ) ) {
$errors->add( 'phone_format_error', '<strong>HATA:</strong> Geçerli bir telefon numarası giriniz.' );
}
}
return $errors;
}
add_filter( 'registration_errors', 'validate_phone_on_registration', 10, 3 );
// Kayıt tamamlanınca meta'yı kaydet
function save_phone_on_registration( $user_id ) {
if ( isset( $_POST['phone_number'] ) && ! empty( $_POST['phone_number'] ) ) {
update_user_meta( $user_id, 'phone_number', sanitize_text_field( $_POST['phone_number'] ) );
}
}
add_action( 'user_register', 'save_phone_on_registration' );
Kullanıcı Listesinde Özel Kolon Gösterme
Profil alanlarına eklediğiniz verileri admin panelindeki kullanıcı listesinde de gösterebilirsiniz. Bu özellikle yönetim açısından çok kullanışlı.
// functions.php
// Kullanıcı listesine yeni kolon ekle
function add_custom_user_columns( $columns ) {
$columns['department'] = 'Departman';
$columns['phone_number'] = 'Telefon';
return $columns;
}
add_filter( 'manage_users_columns', 'add_custom_user_columns' );
// Kolon içeriğini doldur
function fill_custom_user_columns( $output, $column_name, $user_id ) {
switch ( $column_name ) {
case 'department':
$dept = get_user_meta( $user_id, 'department', true );
return $dept ? esc_html( $dept ) : '<span style="color:#999;">-</span>';
case 'phone_number':
$phone = get_user_meta( $user_id, 'phone_number', true );
return $phone ? '<a href="tel:' . esc_attr( $phone ) . '">' . esc_html( $phone ) . '</a>' : '<span style="color:#999;">-</span>';
}
return $output;
}
add_filter( 'manage_users_custom_column', 'fill_custom_user_columns', 10, 3 );
// Kolona göre sıralama
function make_user_columns_sortable( $columns ) {
$columns['department'] = 'department';
return $columns;
}
add_filter( 'manage_users_sortable_columns', 'make_user_columns_sortable' );
Kullanıcı Meta Verilerini Frontend’de Kullanma
Eklediğiniz özel alanları tema dosyalarında veya shortcode ile göstermek için şu yaklaşımı kullanabilirsiniz.
// functions.php
// Kullanıcı meta bilgilerini gösteren shortcode
function user_profile_shortcode( $atts ) {
$atts = shortcode_atts( array(
'field' => 'phone_number',
'user_id' => 0,
), $atts );
$user_id = $atts['user_id'] ? intval( $atts['user_id'] ) : get_current_user_id();
if ( ! $user_id ) {
return '';
}
$allowed_fields = array( 'phone_number', 'department', 'position', 'company_name' );
if ( ! in_array( $atts['field'], $allowed_fields ) ) {
return '';
}
$value = get_user_meta( $user_id, $atts['field'], true );
return $value ? esc_html( $value ) : '';
}
add_shortcode( 'user_meta', 'user_profile_shortcode' );
// Tema içinde kullanım örneği
// echo do_shortcode('[user_meta field="department"]');
// ya da direkt PHP ile:
// $dept = get_user_meta(get_current_user_id(), 'department', true);
// WooCommerce sipariş notlarına müşteri tipini ekle
function add_customer_type_to_order( $order_id ) {
$order = wc_get_order( $order_id );
if ( ! $order ) return;
$user_id = $order->get_user_id();
if ( ! $user_id ) return;
$customer_type = get_user_meta( $user_id, 'customer_type', true );
$discount_rate = get_user_meta( $user_id, 'discount_rate', true );
if ( $customer_type ) {
$order->update_meta_data( '_customer_type', $customer_type );
$order->update_meta_data( '_customer_discount_rate', $discount_rate );
$order->save();
$order->add_order_note(
sprintf( 'Müşteri Tipi: %s | İndirim Oranı: %%%s', $customer_type, $discount_rate )
);
}
}
add_action( 'woocommerce_checkout_order_created', 'add_customer_type_to_order' );
Performans ve Güvenlik Notları
Özel profil alanları eklerken göz önünde bulundurulması gereken birkaç önemli nokta var.
Sanitizasyon her zaman zorunlu. Alan tipine göre doğru sanitizasyon fonksiyonunu seçin:
- Düz metin için
sanitize_text_field() - E-posta için
sanitize_email() - URL için
esc_url_raw() - Tam sayılar için
absint()veyaintval() - HTML içerik için
wp_kses_post()
Nonce kontrolü ekleyin. Büyük projelerde form işlemlerine nonce eklemek ek güvenlik sağlar.
get_user_meta() önbelleği. WordPress bu fonksiyonun sonuçlarını önbellekler. Aynı istek içinde birden fazla kez çağırsanız bile veritabanına tek sorgu gider.
Eski meta verileri temizleyin. Bir alan artık kullanılmıyorsa delete_user_meta() ile temizlemek veritabanını şişkinleşmekten korur.
Büyük kullanıcı listelerinde dikkat. Yüzlerce meta alan içeren kullanıcılarda get_user_meta() sorguları yavaşlayabilir. Sık kullanılan verileri bir arada tutmak için birden fazla alanı tek bir serialize edilmiş meta olarak saklayabilirsiniz, ancak bu durumda sıralama ve filtreleme zorlaşır.
Sonuç
WordPress profil alanları genişletme, aslında çok az kod gerektiren ama çok büyük fark yaratan bir özelleştirme. Kurumsal intranetlerden e-ticaret sitelerine, üyelik platformlarından müşteri portallarına kadar geniş bir yelpazede kullanabilirsiniz.
Önemli olan üç şeyi unutmamak: güvenlik kontrollerini asla atlamayın, veriyi kaydetmeden önce mutlaka sanitize edin, ve yetki kontrollerini hem görüntüleme hem de kaydetme aşamasında yapın. Bu üç adımı doğru yaparsanız plugin bağımlılığı olmadan, hafif ve bakımı kolay bir çözüme kavuşursunuz.
Kodu direkt functions.php‘ye yazmak yerine, temaya ait bir inc/ klasörü içinde ayrı dosyalara bölerek dahil etmek de uzun vadede çok daha temiz bir yapı sağlıyor. Özellikle çocuk tema kullanıyorsanız bu ayrım kodunuzun tema güncellemelerinden etkilenmemesini garantiliyor.
