Elementor – jak stworzyć własny plugin dodający custom post
W tym poradniku stworzymy widget do naszego plugin, który dodawałby custom post, określany w widget’cie oraz pola, które również wybieralibyśmy z poziomu Elementor. W poprzednim poradniku: Własny plugin dodający widget do wtyczki Elementor stworzyliśmy plugin, dodający własne widget’y do wtyczki Elementor. Będzie to takie samo rozwiązanie jak w artykule REDO JSComposer Additional – plugin dodający widget do WPBakery tylko zamiast wtyczki WPBakery docelową wtyczką będzie Elementor.
Zacznijmy pracę nad naszym pluginem dla Elementora dodającego custom post
Tworzymy kolejny widget w folderze widgets i nazywamy go customPostRedo.php, który będzie przechowywał klasę CustomPostRedo.
Musimy wrócić do folderu głównego naszego pluginu i edytować plik main.php. Musimy wskazać ścieżkę do naszego widgetu i jego klasy w klasie głównej E_REDO. Żeby to zrobić dopisujemy analogicznie jak przy widgetcie projekty.php – ścieżkę w require_once() oraz dodajemy ścieżkę do klasy CustomPostRedo w funkcji register_widgets().
1 2 3 4 5 6 7 8 9 10 11 12 | private function include_widgets_files() { require_once(__DIR__ . '/widgets/projekty.php'); require_once(__DIR__ . '/widgets/customPostRedo.php'); } public function register_widgets() { $this->include_widgets_files(); \Elementor\Plugin::instance()->widgets_manager->register_widget_type(new Widgets\Projekty()); \Elementor\Plugin::instance()->widgets_manager->register_widget_type(new Widgets\CustomPostRedo()); } |
Edycja pliku customPostRedo.php naszego pluginu
Gotowe. Przechodzimy do edycji naszego pliku customPostRedo.php. Nadajemy odpowiednią nazwę dla namespace oraz dodajemy ścieżki do odpowiednich klas, których będziemy używać w naszym kodzie. Dodajemy sprawdzenie czy jest zdefiniowana stała ABSPATH i następnie definiujemy nazwę, naszej klasy CustomPostRedo dziedziczącej po klasie Widget_Base – klasa wtyczki Elementor.
1 2 3 4 5 6 7 | namespace REDO\Widgets; use Elementor\Widget_Base; use Elementor\Controls_Manager; use REDO\E_REDO; if (!defined('ABSPATH')) exit; class CustomPostRedo extends Widget_Base { } |
Rejestracja widget’u w pluginie Elementor
Przechodzimy do tworzenia klasy obsługującej nasz widget – CustomPostRedo. Definiujemy kilka funkcji wbudowanych Elementora, nazywającego nasz widget.
1 2 3 4 5 6 7 8 9 10 11 12 | public function get_name(){ return 'customPostRedo'; } public function get_title(){ return __('Custom Post REDO'); } public function get_icon() { return 'fa fa-eye'; } public function get_categories(){ return ['general']; } |
Przechodzimy do głównej funkcji _register_controls(), która pobierać będzie jaki typ custom post’a chcemy wyświetlić oraz jakie pola posta chcemy otrzymać w widoku. Żeby to otrzymać potrzebujemy pobrać do widgetu Elementora listę custom postów oraz ich pól a także pól własnych. Będziemy tutaj bazować na wbudowanej opcji get_option(’cptui_post_types’) oraz stworzonej w pliku main.php funkcji prepareCustomFields(). Pobranie listy typów postów jest analogiczne do tego jak tworzyliśmy to w artykule: REDO JSComposer Additional – dodawanie własnych widget’ów do WPBakery – część druga – tam wytłumaczone są funkcje oraz listy na których będziemy pracować.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | protected function _register_controls() { $customPostList = get_option('cptui_post_types'); $_custom_posts = array(); $_custom_posts_params = array(); if ($customPostList) { foreach ($customPostList as $postList) { $_custom_posts_params[$postList["name"]] = $postList["label"]; $_custom_posts[$postList["name"]]["type"] = $postList["name"]; $_custom_posts[$postList["name"]]["label"] = $postList["label"]; $supportsArray = array_flip($postList["supports"]); foreach ($supportsArray as $key => $value) { $supportsArray[$key] = $key; } $_custom_posts[$postList["name"]]["supports"] = $supportsArray; } } $fields = E_REDO::prepareCustomFields(); $customFieldsList = array(); if ($fields) { foreach ($fields as $field) { $customFieldsList[$field["type"]]["customFields"] = $field["fields"]; } } } |
Mamy już przygotowaną listę z typami postów i ich pól. Tworzymy sekcję w naszym widgetcie i nadajemy odpowiedni label informujący o tym że będzie to sekcja do wyboru typu posta.
1 2 3 4 5 6 | $this->start_controls_section( 'custom-post', [ 'label' => 'Custom Post Type', 'Choose Post Type which you want to display', ] ); |
Wyświetlanie typów custom post w Elementor
Teraz możemy wyświetlić możliwe typy postów do wyboru. Trzeba pamiętać aby każda nazwa parametru była indywidualna i niepowtarzalna. Jeśli nazwy jakichkolwiek parametrów będą się powtarzać – nasz widget nie zadziała. Ważne jest aby odpowiednio nazwać nasz parametr, aby później można było operować na nich (zgodna musi być identyfikacja każdego parametru, żebyśmy wiedzieli za co on odpowiada). Żeby wyświetlić typy postów posługujemy się listą $_custom_posts_params dla, której w pętli foreach dodajemy osobny radio-button, za pomocą funkcji elementor add_control(). Definiujemy nazwę parametru – ’post-type’.$key – do tablicy przekaże nam – ’post-type-typ custom posta’ – co będzie zawsze indywidualną nazwą dla każdego typu posta. W typie posta korzystamy z typu CHOOSE dziedziczonego z klasy Controls_Manager – typ ten jest jednoznaczny z radio-button. W tablicy options dodajemy wartość $key z naszej listy.
1 2 3 4 5 6 7 8 9 10 11 12 13 | foreach ($_custom_posts_params as $key => $value) { $this->add_control( 'post_type-'.$key, [ 'label' => $value, 'type' => \Elementor\Controls_Manager::CHOOSE, 'default' => '', 'options' => [ $key => __($key), ], ] ); } |
Teraz należy zamknąć sekcję dla typu custom postów.
1 | $this->end_controls_section(); |
Kolejne sekcje naszego widgetu będą tworzone w pętli foreach dla wartości tablicy $_custom_posts przechowującej typ posta oraz dla każdego typu posta typy pól możliwych do wyświetlenia. Wewnątrz pętli definiujemy sekcję dla każdego typu posta, przechowującą typy pól. Typy pól będziemy przechowyweać w liście $custom_params dla której za pomocą pętli foreach przypisujemy odpowiednie radio-button’y w stworzonej sekcji.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | $this->start_controls_section( $key .'-fields', [ 'label' => $value["label"] . ' Fields', 'Chose additional fields', ] ); $custom_params = $value["supports"]; foreach ($custom_params as $pkey => $pvalue) { $this->add_control( 'post_field-'.$key .'-' . $pvalue, [ 'label' => $pvalue, 'type' => \Elementor\Controls_Manager::CHOOSE, 'default' => '', 'options' => [ $pkey => __($pvalue), ], ] ); } $this->end_controls_section(); |
W tej samej pętli dla list $_custom_posts tworzymy kolejne sekcje dla pól własnych – indywidualnie dla każdego typu posta.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | foreach ($customFieldsList as $fkey => $fvalue) { if ($fkey == $value["type"]) { $customList = $fvalue["customFields"]; foreach ($customList as $ck => $cv) { $customList[$ck] = $ck; } $this->start_controls_section( $key . 'custom-fields', [ 'label' => $value["label"] . ' Custom Fields', 'Chose additional fields', ] ); foreach ($customList as $ckey) { $this->add_control( 'customfield-'. $key . '-' . $ckey, [ 'label' => $ckey, 'type' => \Elementor\Controls_Manager::CHOOSE, 'default' => '', 'options' => [ $ckey => __($ckey), ], ] ); } $this->end_controls_section(); } } |
Całość kodu funkcji _register_controls() :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | protected function _register_controls() { $customPostList = get_option('cptui_post_types'); $_custom_posts = array(); $_custom_posts_params = array(); if ($customPostList) { foreach ($customPostList as $postList) { $_custom_posts_params[$postList["name"]] = $postList["label"]; $_custom_posts[$postList["name"]]["type"] = $postList["name"]; $_custom_posts[$postList["name"]]["label"] = $postList["label"]; $supportsArray = array_flip($postList["supports"]); foreach ($supportsArray as $key => $value) { $supportsArray[$key] = $key; } $_custom_posts[$postList["name"]]["supports"] = $supportsArray; } } $fields = E_REDO::prepareCustomFields(); $customFieldsList = array(); if ($fields) { foreach ($fields as $field) { $customFieldsList[$field["type"]]["customFields"] = $field["fields"]; } } $this->start_controls_section( 'custom-post', [ 'label' => 'Custom Post Type', 'Choose Post Type which you want to display', ] ); foreach ($_custom_posts_params as $key => $value) { $this->add_control( 'post_type-'.$key, [ 'label' => $value, 'type' => \Elementor\Controls_Manager::CHOOSE, 'default' => '', 'options' => [ $key => __($key), ], ] ); } $this->end_controls_section(); foreach ($_custom_posts as $key => $value) { $this->start_controls_section( $key .'-fields', [ 'label' => $value["label"] . ' Fields', 'Chose additional fields', ] ); $custom_params = $value["supports"]; foreach ($custom_params as $pkey => $pvalue) { $this->add_control( 'post_field-'.$key .'-' . $pvalue, [ 'label' => $pvalue, 'type' => \Elementor\Controls_Manager::CHOOSE, 'default' => '', 'options' => [ $pkey => __($pvalue), ], ] ); } $this->end_controls_section(); foreach ($customFieldsList as $fkey => $fvalue) { if ($fkey == $value["type"]) { $customList = $fvalue["customFields"]; foreach ($customList as $ck => $cv) { $customList[$ck] = $ck; } $this->start_controls_section( $key . 'custom-fields', [ 'label' => $value["label"] . ' Custom Fields', 'Chose additional fields', ] ); foreach ($customList as $ckey) { $this->add_control( 'customfield-'. $key . '-' . $ckey, [ 'label' => $ckey, 'type' => \Elementor\Controls_Manager::CHOOSE, 'default' => '', 'options' => [ $ckey => __($ckey), ], ] ); } $this->end_controls_section(); } } } } |
Otrzymaliśmy widok w elementorze zawierający wszystkie sekcje przechowujące odpowiednie wartości:
Teraz chcielibyśmy aby wybrane w widgecie wartości wyświetlić w widoku. Wyświetlanie będzie bardzo podobne jak w przypadku widgetu dla WPBakery. Różnicą będzie operowanie nie na tablicy $atts jak w przypadku WPBakery a na tablicy $settings, którą uzupełniamy za pomocą funkcji get_settings_for_display() – funkcji Elementora, zwracającej parametry widgetu oraz ich wartości. Widok przekazywany będzie w funkcji render().
1 2 3 | protected function render() { $settings = $this->get_settings_for_display(); } |
Pobieramy listę typów postów wybranych w widgetcie. Tworzymy pustą tablicę $postTypes do której przekażemy typy postów. Za pomocą pętli foreach przeszukujemy tablicę $settings. Jeśli klucz tablicy będzie zawierał w części nazwy parametru ’post_type-’ i wartość dla danego klucza nie będzie pusta – przekazujemy wartość do tablicy $postTypes.
1 2 3 4 5 6 7 | foreach($settings as $key => $value) { if (strpos($key, 'post_type-') !== false) { if(!empty($value)) { $postTypes[$value] = $value; } } } |
Tworzenie widoku końcowego listy custom post w Elementor
Pobraliśmy już listę typów postów pobranych z widgetu. Teraz przechodzimy do wyświetlenia dla nich widoku. Pierwszym krokiem jest sprawdzenie czy tablica $postTypes zawiera typy postów. Jeśli nie jest pusta dla każdego typu postu w tablicy wyświetlamy container oraz nazwę typu posta.
1 2 3 4 5 6 | <?php if (!empty($postTypes)) : ?> <?php foreach ($postTypes as $postType) : ?> <div class="post-type-container <?php echo $postType ?>"> <h2><?php echo $postType ?></h2> </div> <?php endforeach; endif; ?> |
Dla każdego typu posta pobieramy listę postów. W tablicy $args podajemy dla jakiego typu posta chcemy pobrać listę oraz ilość postów. W tablicy $query wykonujemy zapytanie WP_Query na tablicy $args. Listę postów zawartą w $query przekazujemy do tablicy $postList, która będzie przechowywać pobrane post’y.
1 2 3 4 5 6 7 | $args = array( 'post_type' => $postType, 'posts_per_page' => -1, 'orderby' => 'date' ); $query = new \WP_Query($args); $postList = ($query->posts); |
Teraz sprawdzamy czy $postList nie jest pusta. Jeśli nie to wyswietlamy dla każdego posta container oraz jego tytuł.
1 2 3 4 5 | if (!empty($postList)) : ?> <?php foreach ($postList as $myPost) : ?> <div class="post-container <?php echo $postType ?>"> <h2><?php echo $myPost->post_title; ?></h2> </div><?php endforeach; endif; |
Teraz należy przeszukać tablicę $settings i wyświetlić wybrane pola. Podobnie jak w przypadku typów postów za pomocą funkcji strpos() sprawdzamy czy klucz zawiera w nazwie ’post-field’ lub ’customfield’ i odpowiednio wyświetlamy te pola. Wyświetlanie przebiega analogicznie jak w przypadku WPBakery – odsyłam do tego artykułu – link, jeśli nie rozumiesz poniższego kodu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | foreach($settings as $key => $value) { if (strpos($key, 'post_field-'.$postType) !== false) { if(!empty($value)) { ?> <div class="'post-field '<?php echo $postType . ' ' . $value ?>"> <?php if($value=="thumbnail") { echo get_the_post_thumbnail($myPost->ID, 'medium'); } else { echo get_post_field("post_".$value, $myPost->ID); }?> </div> <?php } } if (strpos($key, 'customfield-'.$postType) !== false) { ?> <div class="'post-field '<?php echo $postType . ' ' . $value ?>"> <?php $customField = get_field($value,$myPost->ID); if(is_array($customField)) { $size = 'thumbnail'; echo '<img src="' . esc_url( $customField['sizes'][$size] ) . '"/>'; } else { echo $customField; } ?> </div> <?php } |
Gotowe! Nasz plugin posiada widget dodający custom post w pluginie Elementor
Całość kodu pliku customPostRedo.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | <?php namespace REDO\Widgets; use Elementor\Widget_Base; use Elementor\Controls_Manager; use REDO\E_REDO; if (!defined('ABSPATH')) exit; class CustomPostRedo extends Widget_Base { public function get_name() { return 'customPostRedo'; } public function get_title() { return __('Custom Post REDO'); } public function get_icon() { return 'fa fa-eye'; } public function get_categories() { return ['general']; } protected function _register_controls() { $customPostList = get_option('cptui_post_types'); $_custom_posts = array(); $_custom_posts_params = array(); if ($customPostList) { foreach ($customPostList as $postList) { $_custom_posts_params[$postList["name"]] = $postList["label"]; $_custom_posts[$postList["name"]]["type"] = $postList["name"]; $_custom_posts[$postList["name"]]["label"] = $postList["label"]; $supportsArray = array_flip($postList["supports"]); foreach ($supportsArray as $key => $value) { $supportsArray[$key] = $key; } $_custom_posts[$postList["name"]]["supports"] = $supportsArray; } } $fields = E_REDO::prepareCustomFields(); $customFieldsList = array(); if ($fields) { foreach ($fields as $field) { $customFieldsList[$field["type"]]["customFields"] = $field["fields"]; } } $this->start_controls_section( 'custom-post', [ 'label' => 'Custom Post Type', 'Choose Post Type which you want to display', ] ); foreach ($_custom_posts_params as $key => $value) { $this->add_control( 'post_type-'.$key, [ 'label' => $value, 'type' => \Elementor\Controls_Manager::CHOOSE, 'default' => '', 'options' => [ $key => __($key), ], ] ); } $this->end_controls_section(); foreach ($_custom_posts as $key => $value) { $this->start_controls_section( $key .'-fields', [ 'label' => $value["label"] . ' Fields', 'Chose additional fields', ] ); $custom_params = $value["supports"]; foreach ($custom_params as $pkey => $pvalue) { $this->add_control( 'post_field-'.$key .'-' . $pvalue, [ 'label' => $pvalue, 'type' => \Elementor\Controls_Manager::CHOOSE, 'default' => '', 'options' => [ $pkey => __($pvalue), ], ] ); } $this->end_controls_section(); foreach ($customFieldsList as $fkey => $fvalue) { if ($fkey == $value["type"]) { $customList = $fvalue["customFields"]; foreach ($customList as $ck => $cv) { $customList[$ck] = $ck; } $this->start_controls_section( $key . 'custom-fields', [ 'label' => $value["label"] . ' Custom Fields', 'Chose additional fields', ] ); foreach ($customList as $ckey) { $this->add_control( 'customfield-'. $key . '-' . $ckey, [ 'label' => $ckey, 'type' => \Elementor\Controls_Manager::CHOOSE, 'default' => '', 'options' => [ $ckey => __($ckey), ], ] ); } $this->end_controls_section(); } } } } protected function render() { $settings = $this->get_settings_for_display(); $postTypes = array(); foreach($settings as $key => $value) { if (strpos($key, 'post_type-') !== false) { if(!empty($value)) { $postTypes[$value] = $value; } } } ?> <!-- HTML DESIGN HERE --> <?php if (!empty($postTypes)) : ?> <?php foreach ($postTypes as $postType) : ?> <div class="post-type-container <?php echo $postType ?>"> <h2><?php echo $postType ?></h2> <?php $args = array( 'post_type' => $postType, 'posts_per_page' => -1, 'orderby' => 'date' ); $query = new \WP_Query($args); $postList = ($query->posts); if (!empty($postList)) : ?> <?php foreach ($postList as $myPost) : ?> <div class="post-container <?php echo $postType ?>"> <h2><?php echo $myPost->post_title; ?></h2> <?php foreach($settings as $key => $value) { if (strpos($key, 'post_field-'.$postType) !== false) { if(!empty($value)) { ?> <div class="'post-field '<?php echo $postType . ' ' . $value ?>"> <?php if($value=="thumbnail") { echo get_the_post_thumbnail($myPost->ID, 'medium'); } else { echo get_post_field("post_".$value, $myPost->ID); }?> </div> <?php } } if (strpos($key, 'customfield-'.$postType) !== false) { ?> <div class="'post-field '<?php echo $postType . ' ' . $value ?>"> <?php $customField = get_field($value,$myPost->ID); if(is_array($customField)) { $size = 'thumbnail'; echo '<img src="' . esc_url( $customField['sizes'][$size] ) . '"/>'; } else { echo $customField; } ?> </div> <?php } } ?> </div> <?php endforeach; ?> <?php endif; ?> </div> <?php endforeach; ?> <?php endif; ?> <!-- HTML END DESIGN HERE --> <?php } } |
Podsumowanie
Otrzymaliśmy widok wyświetlający posty wybranego typu custom post oraz ich pola, które również wybieramy w widgetcie. Taki widok możemy łatwo przekształcać za pomocą CSS, ponieważ każdy div danego pola jest indywidualnie nazwany nazwą typu posta oraz nazwą typu pola.
Ważne jest aby pamiętać przy tworzeniu podobnych widgetów dla Elementora, że nazwy parametrów muszą mieć unikalne nazwy – jeśli jakaś nazwa parametru będzie się powtarzać – widget nie zadziała prawidłowo. Warto także używać debugowania zmiennych, aby sprawdzać zawartości naszych list na których operujemy.