WordPress Menü Öğelerinin Öncesine ve Sonrasına İçerik Ekleme

WordPress ile çalışırken menü öğelerini özelleştirmek istediğinizde, çoğu zaman tema editörüne veya sayfa şablonlarına dokunmak zorunda kalırsınız. Ama functions.php üzerinden yapabileceğiniz bazı hileler var ki bunlar hem daha temiz hem de güncelleme sonrası kaybolmayan çözümler sunuyor. Menü öğelerinin öncesine ve sonrasına içerik eklemek de tam olarak bu kategoriye giriyor. İkon eklemek, rozet göstermek, “YENİ” etiketi yapıştırmak veya promosyon metni koymak gibi ihtiyaçlar için walker sınıfını ve nav_menu_item_args filtresini kullanmak en doğru yol.

WordPress Menü Sistemini Anlamak

WordPress menü sistemi aslında oldukça katmanlı bir yapıya sahip. Bir menü oluşturduğunuzda arka planda wp_nav_menu() fonksiyonu devreye giriyor. Bu fonksiyon bir Walker_Nav_Menu sınıfı kullanarak menü öğelerini HTML’e çeviriyor.

Her menü öğesi için temel akış şu şekilde işliyor:

  • start_lvl(): Alt menü listesi başlar
  • start_el(): Menü öğesi başlar (li ve a etiketleri açılır)
  • end_el(): Menü öğesi biter
  • end_lvl(): Alt menü listesi biter

Biz bu süreçte içerik eklemek için iki ana yöntem kullanacağız. Birincisi Walker_Nav_Menu sınıfını extend etmek, ikincisi ise nav_menu_item_args filtresini kullanmak. Her iki yöntemin de kendi avantajları ve kullanım senaryoları var.

Yöntem 1: nav_menu_item_args Filtresi

En hızlı ve temiz çözüm nav_menu_item_args filtresi. Bu filtre WordPress 4.8.0 ile geldi ve menü öğesi argümanlarını değiştirmenize izin veriyor. before, after, link_before ve link_after parametrelerini bu filtre üzerinden manipüle edebilirsiniz.

Bu parametreler şu anlama geliyor:

Tüm Menü Öğelerine İkon Eklemek

En klasik senaryo: Font Awesome ikonları menü öğelerinin başına eklemek.

add_filter( 'nav_menu_item_args', 'add_icon_to_menu_items', 10, 3 );

function add_icon_to_menu_items( $args, $item, $depth ) {
    // Sadece belirli bir menüye uygula
    if ( $args->theme_location === 'primary' ) {
        $args->link_before = '<i class="fas fa-chevron-right menu-icon"></i>';
    }
    return $args;
}

Sadece Belirli Menü Öğelerine İçerik Eklemek

Pratikte çok daha fazla işinize yarayacak olan şu: belirli menü öğelerini ID’lerine veya CSS sınıflarına göre hedef alarak içerik eklemek.

add_filter( 'nav_menu_item_args', 'add_badge_to_specific_menu_item', 10, 3 );

function add_badge_to_specific_menu_item( $args, $item, $depth ) {
    // Menü öğesinin başlığına göre kontrol
    if ( $item->title === 'İletişim' ) {
        $args->after = '<span class="menu-badge menu-badge--new">YENİ</span>';
    }
    
    // Menü öğesi ID'sine göre kontrol
    if ( $item->ID === 42 ) {
        $args->after = '<span class="menu-badge menu-badge--hot">🔥 HOT</span>';
    }
    
    // CSS sınıfına göre kontrol
    if ( in_array( 'menu-item-promo', $item->classes ) ) {
        $args->before = '<span class="promo-star">⭐</span>';
        $args->after = '<span class="promo-label">%20 İndirim</span>';
    }
    
    return $args;
}

WooCommerce Sepet Sayacı Eklemek

E-ticaret sitelerinde “Sepet” menü öğesinin yanına ürün sayısını göstermek çok yaygın bir ihtiyaç. İşte bunu functions.php ile nasıl yapacağınız:

add_filter( 'nav_menu_item_args', 'add_cart_count_to_menu', 10, 3 );

function add_cart_count_to_menu( $args, $item, $depth ) {
    // WooCommerce aktif mi kontrol et
    if ( ! function_exists( 'WC' ) ) {
        return $args;
    }
    
    // "Sepet" başlıklı menü öğesini hedef al
    if ( strtolower( $item->title ) === 'sepet' || strtolower( $item->title ) === 'cart' ) {
        $cart_count = WC()->cart->get_cart_contents_count();
        
        if ( $cart_count > 0 ) {
            $args->after = '<span class="cart-count">' . $cart_count . '</span>';
        }
    }
    
    return $args;
}

// Sepet güncellendiğinde menüyü AJAX ile yenile
add_filter( 'woocommerce_add_to_cart_fragments', 'woocommerce_header_add_to_cart_fragment' );

function woocommerce_header_add_to_cart_fragment( $fragments ) {
    $cart_count = WC()->cart->get_cart_contents_count();
    $fragments['.cart-count'] = '<span class="cart-count">' . $cart_count . '</span>';
    return $fragments;
}

Yöntem 2: Walker Sınıfını Extend Etmek

Daha karmaşık senaryolarda veya HTML yapısı üzerinde tam kontrol istediğinizde Walker_Nav_Menu sınıfını extend etmek gerekiyor. Bu yaklaşım daha fazla kod yazmanızı gerektiriyor ama karşılığında inanılmaz bir esneklik sağlıyor.

Temel Walker Sınıfı

class Custom_Walker_Nav_Menu extends Walker_Nav_Menu {
    
    /**
     * Menü öğesinin başlangıcında çalışır
     * Link ve içerik burada oluşturulur
     */
    public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
        
        $indent = ( $depth ) ? str_repeat( "t", $depth ) : '';
        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $classes[] = 'menu-item-' . $item->ID;
        
        $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
        $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
        
        $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args, $depth );
        $id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
        
        $output .= $indent . '<li' . $id . $class_names .'>';
        
        // Link argümanları
        $atts = array();
        $atts['title']  = ! empty( $item->attr_title ) ? $item->attr_title : '';
        $atts['target'] = ! empty( $item->target )     ? $item->target     : '';
        $atts['rel']    = ! empty( $item->xfn )        ? $item->xfn        : '';
        $atts['href']   = ! empty( $item->url )        ? $item->url        : '';
        
        $atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
        
        $attributes = '';
        foreach ( $atts as $attr => $value ) {
            if ( ! empty( $value ) ) {
                $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
                $attributes .= ' ' . $attr . '="' . $value . '"';
            }
        }
        
        // Önceki içerik (link dışında)
        $item_output = isset( $args->before ) ? $args->before : '';
        
        // Link açılışı
        $item_output .= '<a' . $attributes . '>';
        
        // Link içi önceki içerik
        $item_output .= isset( $args->link_before ) ? $args->link_before : '';
        
        // Ana menü öğesi içeriği
        $item_output .= apply_filters( 'the_title', $item->title, $item->ID );
        
        // Alt menü göstergesi ekle (depth 0 için)
        if ( $depth === 0 && in_array( 'menu-item-has-children', $classes ) ) {
            $item_output .= '<span class="submenu-indicator"><i class="fas fa-angle-down"></i></span>';
        }
        
        // Link içi sonraki içerik
        $item_output .= isset( $args->link_after ) ? $args->link_after : '';
        
        // Link kapanışı
        $item_output .= '</a>';
        
        // Sonraki içerik (link dışında)
        $item_output .= isset( $args->after ) ? $args->after : '';
        
        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }
}

Bu walker’ı kullanmak için wp_nav_menu() çağrısında belirtmeniz gerekiyor:

wp_nav_menu( array(
    'theme_location' => 'primary',
    'walker'         => new Custom_Walker_Nav_Menu(),
    'menu_class'     => 'nav-menu',
    'container'      => 'nav',
) );

Gerçek Dünya Senaryosu: Restoran Menüsü

Diyelim ki bir restoran sitesi yapıyorsunuz. Menü öğelerinin “Yeni”, “Popüler”, “Vejeteryan” gibi etiketler alması gerekiyor. Bu bilgiyi WordPress’in menü öğesi meta verilerine kaydetmek ve sonra çekmek mümkün.

// Menü öğelerine özel alan ekle (wp-admin > Appearance > Menus)
add_filter( 'wp_setup_nav_menu_item', 'add_menu_item_badge_field' );

function add_menu_item_badge_field( $menu_item ) {
    $menu_item->badge_text = get_post_meta( $menu_item->ID, '_menu_item_badge', true );
    return $menu_item;
}

// Admin panelinde menü öğesi düzenleme alanına input ekle
add_action( 'wp_nav_menu_item_custom_fields', 'menu_item_badge_field_input', 10, 4 );

function menu_item_badge_field_input( $item_id, $item, $depth, $args ) {
    $badge = get_post_meta( $item_id, '_menu_item_badge', true );
    ?>
    <p class="field-custom-badge description description-wide">
        <label for="edit-menu-item-badge-<?php echo $item_id; ?>">
            Rozet Metni (ör: YENİ, HOT, %20)
            <input type="text" 
                   id="edit-menu-item-badge-<?php echo $item_id; ?>" 
                   class="widefat edit-menu-item-badge" 
                   name="menu-item-badge[<?php echo $item_id; ?>]" 
                   value="<?php echo esc_attr( $badge ); ?>">
        </label>
    </p>
    <?php
}

// Kaydetme işlemi
add_action( 'wp_update_nav_menu_item', 'save_menu_item_badge_field', 10, 3 );

function save_menu_item_badge_field( $menu_id, $menu_item_db_id, $args ) {
    if ( isset( $_POST['menu-item-badge'][ $menu_item_db_id ] ) ) {
        $badge = sanitize_text_field( $_POST['menu-item-badge'][ $menu_item_db_id ] );
        update_post_meta( $menu_item_db_id, '_menu_item_badge', $badge );
    } else {
        delete_post_meta( $menu_item_db_id, '_menu_item_badge' );
    }
}

// Rozeti menüde göster
add_filter( 'nav_menu_item_args', 'display_menu_item_badge', 10, 3 );

function display_menu_item_badge( $args, $item, $depth ) {
    $badge = get_post_meta( $item->ID, '_menu_item_badge', true );
    
    if ( ! empty( $badge ) ) {
        // Rozet metnine göre CSS sınıfı belirle
        $badge_class = 'menu-badge';
        
        if ( strtolower( $badge ) === 'yeni' || strtolower( $badge ) === 'new' ) {
            $badge_class .= ' menu-badge--new';
        } elseif ( strtolower( $badge ) === 'hot' || strtolower( $badge ) === 'popüler' ) {
            $badge_class .= ' menu-badge--hot';
        } elseif ( strpos( $badge, '%' ) !== false ) {
            $badge_class .= ' menu-badge--discount';
        }
        
        $args->after = '<span class="' . esc_attr( $badge_class ) . '">' . esc_html( $badge ) . '</span>';
    }
    
    return $args;
}

CSS ile Menü Rozetlerini Stilize Etmek

Fonksiyonel kodu yazdık, şimdi görsel kısım. Bu stilleri temanızın style.css dosyasına veya Customizer > Additional CSS alanına ekleyebilirsiniz.

/* functions.php içinde wp_head action ile inline CSS eklemek */
add_action( 'wp_head', 'menu_badge_inline_styles' );

function menu_badge_inline_styles() {
    ?>
    <style type="text/css">
    .menu-badge {
        display: inline-block;
        padding: 2px 6px;
        font-size: 10px;
        font-weight: 700;
        line-height: 1;
        border-radius: 3px;
        margin-left: 5px;
        vertical-align: middle;
        text-transform: uppercase;
    }
    
    .menu-badge--new {
        background-color: #28a745;
        color: #ffffff;
    }
    
    .menu-badge--hot {
        background-color: #dc3545;
        color: #ffffff;
        animation: pulse 1.5s infinite;
    }
    
    .menu-badge--discount {
        background-color: #fd7e14;
        color: #ffffff;
    }
    
    .cart-count {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 20px;
        height: 20px;
        background-color: #0073aa;
        color: white;
        border-radius: 50%;
        font-size: 11px;
        font-weight: bold;
        margin-left: 4px;
    }
    
    @keyframes pulse {
        0% { opacity: 1; }
        50% { opacity: 0.7; }
        100% { opacity: 1; }
    }
    
    .submenu-indicator {
        margin-left: 4px;
        font-size: 12px;
        transition: transform 0.3s ease;
    }
    
    .menu-item-has-children:hover .submenu-indicator {
        transform: rotate(180deg);
    }
    </style>
    <?php
}

Derinliğe Göre Farklı İçerik Eklemek

$depth parametresi çok değerli. Üst düzey menü öğelerine farklı, alt menü öğelerine farklı içerik ekleyebilirsiniz.

add_filter( 'nav_menu_item_args', 'depth_based_menu_content', 10, 3 );

function depth_based_menu_content( $args, $item, $depth ) {
    
    // Sadece primary menüyü hedef al
    if ( ! isset( $args->theme_location ) || $args->theme_location !== 'primary' ) {
        return $args;
    }
    
    if ( $depth === 0 ) {
        // Üst düzey öğeler: Alt menüsü varsa ok ikonu ekle
        if ( in_array( 'menu-item-has-children', (array) $item->classes ) ) {
            $args->link_after = ' <span class="dropdown-arrow" aria-hidden="true">▾</span>';
        }
    } elseif ( $depth === 1 ) {
        // Birinci seviye alt menü: Sol ok ekle
        $args->link_before = '<span class="sub-arrow" aria-hidden="true">→</span> ';
    } elseif ( $depth >= 2 ) {
        // İkinci seviye ve daha derin: Çift ok ekle
        $args->link_before = '<span class="sub-arrow deep" aria-hidden="true">⇒</span> ';
    }
    
    return $args;
}

Koşullu İçerik: Giriş Yapmış Kullanıcılar İçin

Kullanıcı durumuna göre menü öğesi içeriğini değiştirmek de sık karşılaşılan bir senaryo.

add_filter( 'nav_menu_item_args', 'conditional_menu_content_by_user', 10, 3 );

function conditional_menu_content_by_user( $args, $item, $depth ) {
    
    // "Hesabım" veya "My Account" öğesi için
    if ( in_array( $item->object, array( 'page' ) ) ) {
        
        if ( is_user_logged_in() ) {
            $current_user = wp_get_current_user();
            
            // Kullanıcı avatarını ekle
            $avatar = get_avatar( $current_user->ID, 20, '', '', array( 'class' => 'menu-avatar' ) );
            $args->link_before = $avatar;
            
            // Kullanıcı adını sonuna ekle
            if ( strtolower( $item->title ) === 'hesabım' ) {
                $args->after = '<span class="menu-username">Merhaba, ' . esc_html( $current_user->display_name ) . '</span>';
            }
            
        } else {
            // Giriş yapmamış kullanıcılar için login ikonu
            if ( strtolower( $item->title ) === 'hesabım' || strtolower( $item->title ) === 'giriş yap' ) {
                $args->link_before = '<i class="fas fa-user-circle" aria-hidden="true"></i> ';
            }
        }
    }
    
    return $args;
}

Performans Notları

Bu filtreleri kullanırken dikkat etmeniz gereken birkaç önemli nokta var.

  • Gereksiz veritabanı sorgusu yazmayın: Her menü öğesi için get_post_meta() çağırıyorsanız, önce menü öğesinin hedef olup olmadığını kontrol edin
  • Transient kullanın: WooCommerce sepet sayısı gibi dinamik veriler için AJAX fragmentlerini tercih edin, her sayfa yüklemede direkt sorgu atmayın
  • CSS dosyasını inline yazmayın: Üretim ortamında wp_enqueue_style() ile harici CSS dosyası kullanmak her zaman daha iyi
  • Escape etmeyi unutmayın: Menüye eklediğiniz her HTML içeriğini esc_html(), esc_attr() veya wp_kses_post() ile sanitize edin
  • functions.php cache’ini temizleyin: Büyük değişikliklerden sonra object cache varsa temizlemek gerekebilir

Yaygın Hatalar ve Çözümleri

Menü filtreleriyle çalışırken en sık karşılaşılan sorunlar şunlar:

  • $args->after yerine $args[‘after’] yazmak: Menü argümanları obje olarak geliyor, dizi değil. Her zaman -> operatörünü kullanın
  • theme_location kontrolü yapmamak: Filtre tüm menülere uygulanır. Sadece belirli bir menüyü hedefliyorsanız mutlaka $args->theme_location kontrolü yapın
  • Walker ile filtre çakışması: Eğer hem özel walker hem de nav_menu_item_args filtresi kullanıyorsanız, walker içindeki $args->before/$args->after kullanımları çelişebilir. Tek yöntem seçin
  • HTML entity encode sorunu: Türkçe karakterler içeren rozetlerde esc_html() kullanırken bazı karakterler entity’ye dönüşebilir. wp_specialchars_decode() ile düzeltebilirsiniz

Sonuç

WordPress menü sistemine içerik eklemek, ilk bakışta karmaşık görünse de aslında birkaç hook ve filtreden ibaret. nav_menu_item_args filtresi hızlı ve temiz çözümler için ideal, Walker_Nav_Menu extend etmek ise tam kontrol gerektiren durumlar için.

Gerçek projelerinizde en çok işinize yarayacak kombinasyon şu: rozetler ve ikonlar için nav_menu_item_args filtresi kullanın, WordPress admin panelinden yönetilebilir olması için özel menü meta alanları ekleyin. Böylece müşteriniz sizden bağımsız olarak menü rozetlerini güncelleyebilir.

Kod kalitesi açısından bakarsak, bu fonksiyonları her zaman bir plugin içinde tutmak en doğrusu. Ama küçük projeler veya müşteri sitelerinde tema functions.php dosyasına koymak da tamamen kabul edilebilir. Önemli olan, child theme kullanıyor olmak ve ana tema güncellemelerinde kodunuzun kaybolmaması.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir