WordPress Custom Post Types – czyli jak stworzyć Portfolio

To jest kolejny wpis o popularnym systemie WordPress. Tym razem zaprezentuję na przykładzie tej strony jak w prosty sposób zmodyfikować szablon, tak aby była możliwość ustawienia Portfolio, czyli w tym wypadku podstrony z projektami.

Post Types

W WordPressie domyślnie występującymi typami postów są przykładowo:

  • page
  • post

Typy te posiadają własne sekcje w panelu administatora oraz w zasadzie wykazują wiele podobieństw. Jednak istnieją pewne różnice, na przykład we wpisach mamy możliwość dodawania tagów oraz kategorii, natomiast w przypadku stron takich opcji nie ma.

Custom Post Types

Istnieje również możliwość stworzenia własnych typów tzw. Custom Post Types z odpowiednią konfiguracją, która pozwala określić chociażby możliwość tagowania czy kategoryzowania wpisów należących do stworzonego typu. Przykładowo bardzo popularna wtyczka Woocommerce, która przekształca WordPressa w sklep, również wykorzystuje własne typy do produktów, zamówień etc. Co doskonale pokazuje jak bogate ta funkcjonalność ma zastosowania. Poniższy kod jest wyciągnięty prosto z Woocommerce, prezentuje funkcję odpowiedzialną za zarejestrowanie typu product.

register_post_type( 'product',
      apply_filters( 'woocommerce_register_post_type_product',
        array(
          'labels'              => array(
              'name'                  => __( 'Products', 'woocommerce' ),
              'singular_name'         => __( 'Product', 'woocommerce' ),
              'menu_name'             => _x( 'Products', 'Admin menu name', 'woocommerce' ),
              'add_new'               => __( 'Add product', 'woocommerce' ),
              'add_new_item'          => __( 'Add new product', 'woocommerce' ),
              'edit'                  => __( 'Edit', 'woocommerce' ),
              'edit_item'             => __( 'Edit product', 'woocommerce' ),
              'new_item'              => __( 'New product', 'woocommerce' ),
              'view'                  => __( 'View product', 'woocommerce' ),
              'view_item'             => __( 'View product', 'woocommerce' ),
              'search_items'          => __( 'Search products', 'woocommerce' ),
              'not_found'             => __( 'No products found', 'woocommerce' ),
              'not_found_in_trash'    => __( 'No products found in trash', 'woocommerce' ),
              'parent'                => __( 'Parent product', 'woocommerce' ),
              'featured_image'        => __( 'Product image', 'woocommerce' ),
              'set_featured_image'    => __( 'Set product image', 'woocommerce' ),
              'remove_featured_image' => __( 'Remove product image', 'woocommerce' ),
              'use_featured_image'    => __( 'Use as product image', 'woocommerce' ),
              'insert_into_item'      => __( 'Insert into product', 'woocommerce' ),
              'uploaded_to_this_item' => __( 'Uploaded to this product', 'woocommerce' ),
              'filter_items_list'     => __( 'Filter products', 'woocommerce' ),
              'items_list_navigation' => __( 'Products navigation', 'woocommerce' ),
              'items_list'            => __( 'Products list', 'woocommerce' ),
            ),
          'description'         => __( 'This is where you can add new products to your store.', 'woocommerce' ),
          'public'              => true,
          'show_ui'             => true,
          'capability_type'     => 'product',
          'map_meta_cap'        => true,
          'publicly_queryable'  => true,
          'exclude_from_search' => false,
          'hierarchical'        => false, // Hierarchical causes memory issues - WP loads all records!
          'rewrite'             => $permalinks['product_rewrite_slug'] ? array( 'slug' => $permalinks['product_rewrite_slug'], 'with_front' => false, 'feeds' => true ) : false,
          'query_var'           => true,
          'supports'            => array( 'title', 'editor', 'excerpt', 'thumbnail', 'comments', 'custom-fields', 'publicize', 'wpcom-markdown' ),
          'has_archive'         => ( $shop_page_id = wc_get_page_id( 'shop' ) ) && get_post( $shop_page_id ) ? get_page_uri( $shop_page_id ) : 'shop',
          'show_in_nav_menus'   => true,
          'show_in_rest'        => true,
        )
      )
    );

Tworzenie portfolio

Przechodzimy do modyfikacji szablonu, ta strona korzysta z lekko przerobionego motywu Vito, więc co prawda powinienem robić to na Child Theme, aby zostawić sobie możliwość późniejszej aktualizacji bez utraty wprowadzonych zmian. Jednak z racji, że na aktualizacji mi nie zależy to robię to bezpośrednio na bazowym motywie. Z resztą nawet na aktualizację nie ma co liczyć, jest to darmowy motyw, a jego ostatni update był w 2015 roku.

Zaczynamy od edycji pliku functions.php. Autor motywu zamieścił tutaj komentarz:

//You can start adding your code below
//==================================================================

Więc posłuchamy jego rady i kod dotyczący zarejestrowania własnego typu dodamy poniżej:

function createProjectPostType() {
    register_post_type('Project',
        array(
            'labels' => array(
                'name' => esc_attr__('Projects', 'vito'),
                'singular_name' => esc_attr__('Project', 'vito')
            ),
            'public' => true,
            'has_archive' => false,
            'rewrite' => array('slug' => 'project'),
            'supports' => array('title', 'editor', 'thumbnail', 'revisions'),
        )
    );
}

add_action('init', 'createProjectPostType');

Pole supports przyjmuje wartości, które zostały opisane tutaj https://codex.wordpress.org/Function_Reference/post_type_supports. Te, które tutaj zostały użyte to odpowiednio:

  • title – wiadomo tytuł
  • editor – treść
  • thumbnail – możliwość dodania „obrazka wyróżniającego”
  • revisions – wersje

Po dodaniu tego kodu, w panelu administratora powinniśmy mieć taką zakładkę. 

Jeśli chciałbyś dodać do tego typu kategorię to potrzebne informacje znajdują się tutaj https://codex.wordpress.org/Function_Reference/register_taxonomy. W razie problemów możesz śmiało do mnie napisać.

Kolejną rzeczą, która musimy wykonać to przygotowanie odpowiedniego szablonu do wyświetlenia tych postów. W tym celu w  katalogu głównym motywu tworzymy plik page-projects.php oraz dodajemy na samej górze komentarz:

<?php
/**
 * Template Name: Page with projects
 */

W ten sposób stworzyliśmy szablon, który następnie możemy wybrać edytując daną stronę:

Teraz musimy pobrać posty typu project. Aby zbytnio nie śmiecić w szablonie, przygotujemy funkcję getProjects() w pliku functions.php i następnie z niej skorzystamy.

function getProjects() {
    $args = array(
        'post_type' => 'project',
        'post_status' => 'publish',
        'posts_per_page' => -1
    );

    return get_posts($args);
}

W ten sposób pobieramy wszystkie posty o typie project oraz ze statusem publish.

Poniżej znajduje się całość pliku page-projects.php. Kod HTML odpowiedzialny za wyświetlanie projektu to po prostu przeróbka kodu wyświetlającego zwykły post na blogu.

<?php
/**
 * Template Name: Page with projects
 */

get_header(); ?>

    <div id="content">
        <?php
            $posts = getProjects();

            foreach($posts as $post): setup_postdata($post);
        ?>

        <article id="post-<?php the_ID(); ?>" <?php post_class(); ?> >

            <?php
            if(!get_post_format()) {
                //Display the Post Image by default
                get_template_part( "post_image", "index" );
            } else {
                get_template_part('format', get_post_format());
            }
            ?>

            <div class="post-inside container-fluid">
                <div class="row">
                    <div class="post-content col-md-9 col-lg-10">
                        <?php the_title('<h2 class="post_title">', '</h2>'); ?>

                        <div class="entry">
                            <?php the_content(); ?>
                            <div class="clearfix"></div>
                        </div>
                        <div class="clearfix"></div>
                    </div><!-- /post_content -->

                </div><!-- /row -->
            </div><!-- /post-inside -->

            <div class="clearfix"></div>
        </article>

        <?php
            endforeach;
            wp_reset_postdata();
        ?>


<?php get_template_part("/templates/afterloop", "page") ?>

<?php get_footer(); ?>

WordPress i zmienne globalne

WordPress dużo korzysta ze zmiennych globalnych, co jest oczywiście złą praktyką, w zasadzie to wordpress jest pełen złych praktyk, dlatego jest przez wiele osób tak bardzo krytykowany, jednak popularność i jego mnogość zastosowań zmusza nas do pracy z nim. Nie ma co tutaj dużo mówić, ma on po prostu swoje zalety i dużo wad 😀

W Wordpressie mamy do czynienia ze zmienną globalną $post, która w przypadku powyższego kodu odnosi się do danej strony, czyli postu o typie page, gdyż jest to szablon strony. Więc funkcje takie jak: the_post(), the_content(), odnoszą się do tej strony. Jednak dzięki użyciu funkcji takich jak:

  • setup_postdata($post)
  • wp_reset_postdata();

Odpowiednio najpierw przypisujemy do zmiennej globalnej $post nasz $post z pętli foreach przetwarzającej wszystkie projekty, co pozwala na używanie funkcji the_post()the_content() odnośnie tych przetwarzanych postów, a następnie funkcja wp_reset_postdata() przywraca poprzednią wartość $post.

Advanced Custom Fields

Chciałbym mieć możliwość dodania linku do projektu oraz do kodu źródłowego osobno, żeby tego za każdym razem nie wstawiać w treści. Można coś takiego napisać wykorzystując Meta Boxes (add_meta_box()), ale ja tutaj preferuję pewne ułatwienie jakim jest wtyczka Advanced Custom Fields. Pozwala ona utworzyć dodatkowe pola różnego typu (tekst, lista, treść, zdjęcie etc.), w wersji płatnej dostępna jest większa ilość pól jak na przykład pole umożliwiające dodanie wielu zdjęć (idealne do tworzenia własnych galerii, sliderów). Dodatkowo możemy te pola przypisać do określonych typów postów.

Utworzyłem sobie dwa pola na link do kodu źródłowego oraz projektu, następnie dodałem regułę, która pokaże te pola tylko jeśli typ postu to project. Poniżej przedstawiony został kod, odpowiedzialny za wyświetlenie tych wartości. W przypadku, gdy pobieramy wartość pola dla zmiennej globalnej wystarczy wywołanie funkcji get_field(). Jeśli chcemy pobrać wartość dla innego postu, funkcja ta jako drugi parametr przyjmuje ID postu.

<?php
    $projectLink = get_field('link');
    $codeLink = get_field('code');
?>
<?php if(isset($projectLink) && !empty($projectLink)): ?>
    <a target="_blank" class="project-icon" href="<?php echo $projectLink; ?>">
        <i class="fa fa-link" aria-hidden="true"></i>
    </a>
<?php endif; ?>
<?php if(isset($codeLink) && !empty($codeLink)): ?>
    <a target="_blank" class="project-icon" href="<?php echo $codeLink; ?>">
        <i class="fa fa-code" aria-hidden="true"></i>
    </a>
<?php endif; ?>

Finalny efekt można sprawdzić tutaj. Póki co dodałem tam tylko jeden projekt, na razie nie udostępniłem kodu, więc jest tylko jedna ikona.

 

 

Udostępnij: