Shortcode'y z ostatnimi, przyszłymi wpisami lub wpisami danego autora
PL

Shortcode'y z ostatnimi, przyszłymi wpisami lub wpisami danego autora

5.00 /5 - (27 głosów )
Spis treści

Shortcode’y w WordPress to potężne narzędzie do tworzenia dynamicznych elementów treści. W 2026 roku, mimo że Gutenberg Bloki są preferowanym rozwiązaniem, shortcode’y wciąż mają swoje zastosowanie – szczególnie dla zaawansowanych funkcjonalności wymagających logiki PHP.

W tym przewodniku nauczysz się tworzyć profesjonalne shortcode’y do wyświetlania list wpisów, które są bezpieczne, wydajne i zgodne z najlepszymi praktykami WordPress.

Dlaczego shortcode’y w 2026?

Mimo że Gutenberg Bloki są przyszłością WordPress, shortcode’y mają swoje miejsce:

Zalety shortcode’ów:

  • Szybka implementacja – nie wymagają kompilacji JavaScript
  • Dostęp do pełnej logiki PHP – łatwy dostęp do bazy danych, hooków WordPress
  • Kompatybilność wsteczna – działają w Classic Editor i Gutenberg
  • Prostota – dla prostych funkcji nie potrzebujesz React/JSX

Kiedy używać shortcode’ów:

  • Dynamiczne listy wpisów z zaawansowaną logiką
  • Integracje z zewnętrznymi API
  • Funkcje wymagające bezpośredniego dostępu do PHP

Kiedy używać Bloków Gutenberg:

  • Edytowalne interfejsy użytkownika
  • Wizualne edytowanie w Gutenbergu
  • Złożone layouty z wieloma opcjami

Podstawowa struktura shortcode’a

Każdy shortcode w WordPress składa się z trzech elementów:

  1. Funkcja callback – wykonuje logikę i zwraca HTML
  2. Rejestracja shortcode’aadd_shortcode()
  3. Atrybuty – parametry przekazywane przez użytkownika

Minimalny przykład

/**
 * Basic shortcode structure
 */
function wppoland_basic_shortcode( $atts, $content = null, $tag = '' ) {
    // $atts - array of attributes
    // $content - content between opening and closing tags
    // $tag - shortcode tag name
    
    // Parse attributes with defaults
    $atts = shortcode_atts( [
        'number' => 5,
    ], $atts, $tag );
    
    // Your logic here
    $output = '<div class="shortcode-output">';
    $output .= 'Number: ' . esc_html( $atts['number'] );
    $output .= '</div>';
    
    return $output;
}
add_shortcode( 'my_shortcode', 'wppoland_basic_shortcode' );

Użycie:

[my_shortcode number="10"]

Zaawansowany shortcode do list wpisów

Oto kompletna, produkcyjna wersja shortcode’a do wyświetlania list wpisów:

/**
 * Advanced post list shortcodes
 * Supports: latest posts, category posts, author posts, future posts
 * 
 * @param array $atts Shortcode attributes
 * @param string|null $content Content between tags (not used)
 * @param string $tag Shortcode tag name
 * @return string HTML output
 */
function wppoland_post_lists_shortcode( $atts, $content = null, $tag ) {
    // Security: Only allow specific tags
    $allowed_tags = ['latest_posts', 'category_posts', 'author_posts', 'future_posts'];
    if ( ! in_array( $tag, $allowed_tags, true ) ) {
        return '';
    }
    
    // Get current post context
    global $post;
    $current_post_id = $post ? $post->ID : 0;
    $current_author_id = $post ? $post->post_author : 0;
    
    // Parse and sanitize attributes
    $atts = shortcode_atts( [
        'number'          => 5,
        'exclude_current' => 'yes',
        'orderby'         => 'date',
        'order'           => 'DESC',
        'category'        => '',
        'author'          => '',
        'post_type'       => 'post',
        'show_date'       => 'yes',
        'show_excerpt'    => 'no',
        'class'           => '',
    ], $atts, $tag );
    
    // Sanitize inputs
    $number = absint( $atts['number'] );
    $number = min( $number, 50 ); // Limit to 50 for performance
    $exclude_current = $atts['exclude_current'] === 'yes';
    $orderby = sanitize_key( $atts['orderby'] );
    $order = strtoupper( $atts['order'] ) === 'ASC' ? 'ASC' : 'DESC';
    $post_type = sanitize_key( $atts['post_type'] );
    $show_date = $atts['show_date'] === 'yes';
    $show_excerpt = $atts['show_excerpt'] === 'yes';
    $css_class = sanitize_html_class( $atts['class'] );
    
    // Build query arguments based on shortcode tag
    $query_args = [
        'post_type'      => $post_type,
        'posts_per_page' => $number,
        'orderby'        => $orderby,
        'order'          => $order,
        'post_status'    => 'publish',
        'no_found_rows'  => true, // Performance: don't count total posts
        'update_post_meta_cache' => false, // Performance: don't cache meta
        'update_post_term_cache' => false, // Performance: don't cache terms
    ];
    
    // Exclude current post if needed
    if ( $exclude_current && $current_post_id > 0 ) {
        $query_args['post__not_in'] = [ $current_post_id ];
    }
    
    // Build query based on shortcode type
    switch ( $tag ) {
        case 'latest_posts':
            // No additional args needed - shows latest posts
            break;
            
        case 'category_posts':
            // Get categories from current post
            if ( $current_post_id > 0 ) {
                $categories = get_the_category( $current_post_id );
                if ( ! empty( $categories ) ) {
                    $category_id = $categories[0]->term_id;
                    $query_args['cat'] = $category_id;
                } else {
                    // No categories found, return empty
                    return '';
                }
            } else {
                // Not in post context, return empty
                return '';
            }
            break;
            
        case 'author_posts':
            // Use current post author or specified author
            if ( ! empty( $atts['author'] ) ) {
                $author_id = absint( $atts['author'] );
            } elseif ( $current_author_id > 0 ) {
                $author_id = $current_author_id;
            } else {
                return '';
            }
            $query_args['author'] = $author_id;
            break;
            
        case 'future_posts':
            // Show scheduled posts
            $query_args['post_status'] = 'future';
            $query_args['orderby'] = 'date';
            $query_args['order'] = 'ASC';
            break;
    }
    
    // Override category if specified
    if ( ! empty( $atts['category'] ) && $tag !== 'category_posts' ) {
        $category_id = absint( $atts['category'] );
        if ( $category_id > 0 ) {
            $query_args['cat'] = $category_id;
        }
    }
    
    // Execute query
    $posts_query = new WP_Query( $query_args );
    
    // Check if we have posts
    if ( ! $posts_query->have_posts() ) {
        wp_reset_postdata();
        return '';
    }
    
    // Build output
    $output = '<ul class="post-list post-list-' . esc_attr( $tag );
    if ( ! empty( $css_class ) ) {
        $output .= ' ' . esc_attr( $css_class );
    }
    $output .= '">';
    
    while ( $posts_query->have_posts() ) {
        $posts_query->the_post();
        
        $output .= '<li class="post-list-item post-list-item-' . get_the_ID() . '">';
        $output .= '<a href="' . esc_url( get_permalink() ) . '" ';
        $output .= 'title="' . esc_attr( get_the_title() ) . '">';
        $output .= esc_html( get_the_title() );
        $output .= '</a>';
        
        // Show date if requested
        if ( $show_date ) {
            $output .= ' <span class="post-date">';
            $output .= '<time datetime="' . esc_attr( get_the_date( 'c' ) ) . '">';
            $output .= esc_html( get_the_date() );
            $output .= '</time>';
            $output .= '</span>';
        }
        
        // Show excerpt if requested
        if ( $show_excerpt ) {
            $excerpt = get_the_excerpt();
            if ( ! empty( $excerpt ) ) {
                $output .= '<p class="post-excerpt">' . esc_html( wp_trim_words( $excerpt, 20 ) ) . '</p>';
            }
        }
        
        $output .= '</li>';
    }
    
    $output .= '</ul>';
    
    // Reset post data (CRITICAL!)
    wp_reset_postdata();
    
    return $output;
}

// Register all shortcodes
add_shortcode( 'latest_posts', 'wppoland_post_lists_shortcode' );
add_shortcode( 'category_posts', 'wppoland_post_lists_shortcode' );
add_shortcode( 'author_posts', 'wppoland_post_lists_shortcode' );
add_shortcode( 'future_posts', 'wppoland_post_lists_shortcode' );

Użycie shortcode’ów

Podstawowe przykłady

Najnowsze wpisy:

[latest_posts number="5"]

Wpisy z tej samej kategorii:

[category_posts number="3" exclude_current="yes"]

Wpisy tego samego autora:

[author_posts number="5" show_date="yes"]

Zaplanowane wpisy:

[future_posts number="10"]

Zaawansowane opcje

Z datą i fragmentem:

[latest_posts number="5" show_date="yes" show_excerpt="yes"]

Z niestandardową klasą CSS:

[category_posts number="3" class="my-custom-class"]

Z konkretną kategorią:

[latest_posts category="5" number="10"]

Z konkretnym autorem:

[author_posts author="1" number="5"]

Sortowanie:

[latest_posts number="5" orderby="title" order="ASC"]

Bezpieczeństwo i walidacja

1. Sanityzacja danych wejściowych

Zawsze sanityzuj dane użytkownika:

// ❌ ZŁE - brak sanityzacji
$number = $atts['number'];

// ✅ DOBRE - sanityzacja
$number = absint( $atts['number'] );
$number = min( $number, 50 ); // Limit maksymalny

2. Escapowanie outputu

Zawsze escapuj dane wyjściowe:

// ❌ ZŁE - brak escapowania
$output = '<a href="' . get_permalink() . '">' . get_the_title() . '</a>';

// ✅ DOBRE - escapowanie
$output = '<a href="' . esc_url( get_permalink() ) . '">';
$output .= esc_html( get_the_title() ) . '</a>';

3. Nonce dla edytowalnych shortcode’ów

Jeśli shortcode pozwala na edycję (np. przez AJAX), użyj nonce:

/**
 * Shortcode with nonce for AJAX operations
 */
function wppoland_editable_shortcode( $atts ) {
    $nonce = wp_create_nonce( 'wppoland_shortcode_action' );
    
    $output = '<div class="editable-content" data-nonce="' . esc_attr( $nonce ) . '">';
    $output .= 'Content here';
    $output .= '</div>';
    
    return $output;
}

Optymalizacja wydajności

1. Ograniczenie zapytań do bazy

// ✅ DOBRE - optymalizacja WP_Query
$query_args = [
    'no_found_rows'          => true, // Nie licz wszystkich postów
    'update_post_meta_cache' => false, // Nie cache'uj meta
    'update_post_term_cache' => false, // Nie cache'uj termów
];

2. Cache’owanie wyników

Dla często używanych shortcode’ów warto cache’ować wyniki:

/**
 * Cached shortcode output
 */
function wppoland_cached_post_list( $atts, $content = null, $tag ) {
    // Create cache key
    $cache_key = 'wppoland_shortcode_' . $tag . '_' . md5( serialize( $atts ) );
    
    // Try to get from cache
    $output = get_transient( $cache_key );
    
    if ( false === $output ) {
        // Generate output
        $output = wppoland_post_lists_shortcode( $atts, $content, $tag );
        
        // Cache for 1 hour
        set_transient( $cache_key, $output, HOUR_IN_SECONDS );
    }
    
    return $output;
}

3. Lazy Loading dla długich list

Dla list z wieloma wpisami rozważ lazy loading:

/**
 * Shortcode with lazy loading support
 */
function wppoland_lazy_post_list( $atts ) {
    $atts = shortcode_atts( [
        'number' => 10,
        'load_more' => 'yes',
    ], $atts );
    
    $output = '<div class="lazy-post-list" data-posts="' . esc_attr( $atts['number'] ) . '">';
    $output .= '<div class="post-list-container">';
    // Initial posts loaded via AJAX
    $output .= '</div>';
    
    if ( $atts['load_more'] === 'yes' ) {
        $output .= '<button class="load-more-posts">Wczytaj więcej</button>';
    }
    
    $output .= '</div>';
    
    return $output;
}

Stylowanie shortcode’ów

CSS dla list wpisów

/* Basic styling */
.post-list {
    list-style: none;
    padding: 0;
    margin: 1.5em 0;
}

.post-list-item {
    padding: 0.75em 0;
    border-bottom: 1px solid #e0e0e0;
}

.post-list-item:last-child {
    border-bottom: none;
}

.post-list-item a {
    text-decoration: none;
    color: #333;
    font-weight: 500;
    transition: color 0.2s;
}

.post-list-item a:hover {
    color: #0073aa;
}

.post-date {
    display: block;
    font-size: 0.875em;
    color: #666;
    margin-top: 0.25em;
}

.post-excerpt {
    font-size: 0.9em;
    color: #555;
    margin-top: 0.5em;
    line-height: 1.5;
}

Testowanie shortcode’ów

Unit testy

/**
 * Test shortcode functionality
 */
class Wppoland_Shortcode_Test extends WP_UnitTestCase {
    
    public function test_latest_posts_shortcode() {
        // Create test posts
        $post1 = $this->factory->post->create( [
            'post_title' => 'Test Post 1',
            'post_date' => '2026-01-01 12:00:00'
        ] );
        
        $post2 = $this->factory->post->create( [
            'post_title' => 'Test Post 2',
            'post_date' => '2026-01-02 12:00:00'
        ] );
        
        // Test shortcode
        $output = do_shortcode( '[latest_posts number="2"]' );
        
        // Assertions
        $this->assertStringContainsString( 'Test Post 1', $output );
        $this->assertStringContainsString( 'Test Post 2', $output );
    }
}

Migracja do Gutenberg Bloków (2026)

Shortcode’y są przydatne, ale Gutenberg Bloki są przyszłością. Oto jak zmigrować:

1. Utwórz Block z render_callback

/**
 * Register block that uses shortcode logic
 */
register_block_type( 'wppoland/post-list', [
    'attributes' => [
        'number' => [
            'type' => 'number',
            'default' => 5,
        ],
        'showDate' => [
            'type' => 'boolean',
            'default' => true,
        ],
    ],
    'render_callback' => function( $attributes ) {
        // Reuse shortcode logic
        return wppoland_post_lists_shortcode( $attributes, null, 'latest_posts' );
    },
] );

2. Stopniowa migracja

  1. Faza 1: Shortcode’y działają równolegle z blokami
  2. Faza 2: Nowe treści używają bloków
  3. Faza 3: Stare shortcode’y są automatycznie konwertowane

Podsumowanie: Best Practices

✅ DOBRE praktyki

  1. Sanityzacja – zawsze sanityzuj dane wejściowe
  2. Escapowanie – escapuj wszystkie dane wyjściowe
  3. Performance – używaj no_found_rows, cache’uj wyniki
  4. Reset postdata – zawsze wywołuj wp_reset_postdata()
  5. Dokumentacja – dokumentuj wszystkie atrybuty
  6. Testowanie – pisz testy dla shortcode’ów

❌ ZŁE praktyki

  1. Brak sanityzacji – nigdy nie ufaj danym użytkownika
  2. Brak escapowania – XSS vulnerabilities
  3. Zapominanie wp_reset_postdata() – psuje główną pętlę
  4. Nieskończone zapytania – zawsze limituj liczbę postów
  5. Hardcoded wartości – używaj atrybutów i filtrów

Przykłady użycia w praktyce

Sekcja “Zobacz również”

<h2>Zobacz również</h2>
[category_posts number="3" exclude_current="yes" show_date="yes"]

Sekcja “Najnowsze wpisy”

<h2>Najnowsze wpisy</h2>
[latest_posts number="5" show_excerpt="yes" class="featured-posts"]

Sekcja “Inne wpisy autora”

<h2>Inne wpisy tego autora</h2>
[author_posts number="5" exclude_current="yes"]

Kalendarz wydarzeń (zaplanowane wpisy)

<h2>Nadchodzące wydarzenia</h2>
[future_posts number="10" orderby="date" order="ASC"]

Shortcode’y w WordPress to potężne narzędzie, które w 2026 roku wciąż ma zastosowanie. Pamiętaj o bezpieczeństwie, wydajności i stopniowej migracji do Gutenberg Bloków.