/** * REST API: WP_REST_Post_Types_Controller class * * @package WordPress * @subpackage REST_API * @since 4.7.0 */ /** * Core class to access post types via the REST API. * * @since 4.7.0 * * @see WP_REST_Controller */ class WP_REST_Post_Types_Controller extends WP_REST_Controller { /** * Constructor. * * @since 4.7.0 */ public function __construct() { $this->namespace = 'wp/v2'; $this->rest_base = 'types'; } /** * Registers the routes for post types. * * @since 4.7.0 * * @see register_rest_route() */ public function register_routes() { register_rest_route( $this->namespace, '/' . $this->rest_base, array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), 'args' => $this->get_collection_params(), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( 'args' => array( 'type' => array( 'description' => __( 'An alphanumeric identifier for the post type.' ), 'type' => 'string', ), ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => '__return_true', 'args' => array( 'context' => $this->get_context_param( array( 'default' => 'view' ) ), ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Checks whether a given request has permission to read types. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has read access, WP_Error object otherwise. */ public function get_items_permissions_check( $request ) { if ( 'edit' === $request['context'] ) { $types = get_post_types( array( 'show_in_rest' => true ), 'objects' ); foreach ( $types as $type ) { if ( current_user_can( $type->cap->edit_posts ) ) { return true; } } return new WP_Error( 'rest_cannot_view', __( 'Sorry, you are not allowed to edit posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Retrieves all public post types. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function get_items( $request ) { if ( $request->is_method( 'HEAD' ) ) { // Return early as this handler doesn't add any response headers. return new WP_REST_Response( array() ); } $data = array(); $types = get_post_types( array( 'show_in_rest' => true ), 'objects' ); foreach ( $types as $type ) { if ( 'edit' === $request['context'] && ! current_user_can( $type->cap->edit_posts ) ) { continue; } $post_type = $this->prepare_item_for_response( $type, $request ); $data[ $type->name ] = $this->prepare_response_for_collection( $post_type ); } return rest_ensure_response( $data ); } /** * Retrieves a specific post type. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function get_item( $request ) { $obj = get_post_type_object( $request['type'] ); if ( empty( $obj ) ) { return new WP_Error( 'rest_type_invalid', __( 'Invalid post type.' ), array( 'status' => 404 ) ); } if ( empty( $obj->show_in_rest ) ) { return new WP_Error( 'rest_cannot_read_type', __( 'Cannot view post type.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( 'edit' === $request['context'] && ! current_user_can( $obj->cap->edit_posts ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) ); } $data = $this->prepare_item_for_response( $obj, $request ); return rest_ensure_response( $data ); } /** * Prepares a post type object for serialization. * * @since 4.7.0 * @since 5.9.0 Renamed `$post_type` to `$item` to match parent class for PHP 8 named parameter support. * * @param WP_Post_Type $item Post type object. * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response Response object. */ public function prepare_item_for_response( $item, $request ) { // Restores the more descriptive, specific name for use within this method. $post_type = $item; // Don't prepare the response body for HEAD requests. if ( $request->is_method( 'HEAD' ) ) { /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php */ return apply_filters( 'rest_prepare_post_type', new WP_REST_Response( array() ), $post_type, $request ); } $taxonomies = wp_list_filter( get_object_taxonomies( $post_type->name, 'objects' ), array( 'show_in_rest' => true ) ); $taxonomies = wp_list_pluck( $taxonomies, 'name' ); $base = ! empty( $post_type->rest_base ) ? $post_type->rest_base : $post_type->name; $namespace = ! empty( $post_type->rest_namespace ) ? $post_type->rest_namespace : 'wp/v2'; $supports = get_all_post_type_supports( $post_type->name ); $fields = $this->get_fields_for_response( $request ); $data = array(); if ( rest_is_field_included( 'capabilities', $fields ) ) { $data['capabilities'] = $post_type->cap; } if ( rest_is_field_included( 'description', $fields ) ) { $data['description'] = $post_type->description; } if ( rest_is_field_included( 'hierarchical', $fields ) ) { $data['hierarchical'] = $post_type->hierarchical; } if ( rest_is_field_included( 'has_archive', $fields ) ) { $data['has_archive'] = $post_type->has_archive; } if ( rest_is_field_included( 'visibility', $fields ) ) { $data['visibility'] = array( 'show_in_nav_menus' => (bool) $post_type->show_in_nav_menus, 'show_ui' => (bool) $post_type->show_ui, ); } if ( rest_is_field_included( 'viewable', $fields ) ) { $data['viewable'] = is_post_type_viewable( $post_type ); } if ( rest_is_field_included( 'labels', $fields ) ) { $data['labels'] = $post_type->labels; } if ( rest_is_field_included( 'name', $fields ) ) { $data['name'] = $post_type->label; } if ( rest_is_field_included( 'slug', $fields ) ) { $data['slug'] = $post_type->name; } if ( rest_is_field_included( 'icon', $fields ) ) { $data['icon'] = $post_type->menu_icon; } if ( rest_is_field_included( 'supports', $fields ) ) { $data['supports'] = $supports; } if ( rest_is_field_included( 'taxonomies', $fields ) ) { $data['taxonomies'] = array_values( $taxonomies ); } if ( rest_is_field_included( 'rest_base', $fields ) ) { $data['rest_base'] = $base; } if ( rest_is_field_included( 'rest_namespace', $fields ) ) { $data['rest_namespace'] = $namespace; } if ( rest_is_field_included( 'template', $fields ) ) { $data['template'] = $post_type->template ?? array(); } if ( rest_is_field_included( 'template_lock', $fields ) ) { $data['template_lock'] = ! empty( $post_type->template_lock ) ? $post_type->template_lock : false; } $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); // Wrap the data in a response object. $response = rest_ensure_response( $data ); if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { $response->add_links( $this->prepare_links( $post_type ) ); } /** * Filters a post type returned from the REST API. * * Allows modification of the post type data right before it is returned. * * @since 4.7.0 * * @param WP_REST_Response $response The response object. * @param WP_Post_Type $post_type The original post type object. * @param WP_REST_Request $request Request used to generate the response. */ return apply_filters( 'rest_prepare_post_type', $response, $post_type, $request ); } /** * Prepares links for the request. * * @since 6.1.0 * * @param WP_Post_Type $post_type The post type. * @return array Links for the given post type. */ protected function prepare_links( $post_type ) { return array( 'collection' => array( 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), ), 'https://api.w.org/items' => array( 'href' => rest_url( rest_get_route_for_post_type_items( $post_type->name ) ), ), ); } /** * Retrieves the post type's schema, conforming to JSON Schema. * * @since 4.7.0 * @since 4.8.0 The `supports` property was added. * @since 5.9.0 The `visibility` and `rest_namespace` properties were added. * @since 6.1.0 The `icon` property was added. * * @return array Item schema data. */ public function get_item_schema() { if ( $this->schema ) { return $this->add_additional_fields_schema( $this->schema ); } $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'type', 'type' => 'object', 'properties' => array( 'capabilities' => array( 'description' => __( 'All capabilities used by the post type.' ), 'type' => 'object', 'context' => array( 'edit' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'A human-readable description of the post type.' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'hierarchical' => array( 'description' => __( 'Whether or not the post type should have children.' ), 'type' => 'boolean', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'viewable' => array( 'description' => __( 'Whether or not the post type can be viewed.' ), 'type' => 'boolean', 'context' => array( 'edit' ), 'readonly' => true, ), 'labels' => array( 'description' => __( 'Human-readable labels for the post type for various contexts.' ), 'type' => 'object', 'context' => array( 'edit' ), 'readonly' => true, ), 'name' => array( 'description' => __( 'The title for the post type.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'slug' => array( 'description' => __( 'An alphanumeric identifier for the post type.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'supports' => array( 'description' => __( 'All features, supported by the post type.' ), 'type' => 'object', 'context' => array( 'edit' ), 'readonly' => true, ), 'has_archive' => array( 'description' => __( 'If the value is a string, the value will be used as the archive slug. If the value is false the post type has no archive.' ), 'type' => array( 'string', 'boolean' ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'taxonomies' => array( 'description' => __( 'Taxonomies associated with post type.' ), 'type' => 'array', 'items' => array( 'type' => 'string', ), 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'rest_base' => array( 'description' => __( 'REST base route for the post type.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'rest_namespace' => array( 'description' => __( 'REST route\'s namespace for the post type.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'visibility' => array( 'description' => __( 'The visibility settings for the post type.' ), 'type' => 'object', 'context' => array( 'edit' ), 'readonly' => true, 'properties' => array( 'show_ui' => array( 'description' => __( 'Whether to generate a default UI for managing this post type.' ), 'type' => 'boolean', ), 'show_in_nav_menus' => array( 'description' => __( 'Whether to make the post type available for selection in navigation menus.' ), 'type' => 'boolean', ), ), ), 'icon' => array( 'description' => __( 'The icon for the post type.' ), 'type' => array( 'string', 'null' ), 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'template' => array( 'type' => array( 'array' ), 'description' => __( 'The block template associated with the post type.' ), 'readonly' => true, 'context' => array( 'view', 'edit', 'embed' ), ), 'template_lock' => array( 'type' => array( 'string', 'boolean' ), 'enum' => array( 'all', 'insert', 'contentOnly', false ), 'description' => __( 'The template_lock associated with the post type, or false if none.' ), 'readonly' => true, 'context' => array( 'view', 'edit', 'embed' ), ), ), ); $this->schema = $schema; return $this->add_additional_fields_schema( $this->schema ); } /** * Retrieves the query params for collections. * * @since 4.7.0 * * @return array Collection parameters. */ public function get_collection_params() { return array( 'context' => $this->get_context_param( array( 'default' => 'view' ) ), ); } } Zaawansowana optymalizacja techniczna kampanii Facebook pod kątem lokalnego SEO: krok po kroku dla ekspertów – Chambers Of Vikramaditya

Zaawansowana optymalizacja techniczna kampanii Facebook pod kątem lokalnego SEO: krok po kroku dla ekspertów

W kontekście coraz bardziej konkurencyjnego rynku lokalnego, skuteczna optymalizacja techniczna kampanii reklamowych na Facebooku stanowi kluczowy element strategii marketingowej małych firm. W niniejszym artykule zagłębimy się w szczegóły, które umożliwią Pan(i) osiągnięcie maksymalnej precyzji targetowania, optymalne ustawienia geolokalizacyjne oraz skuteczne śledzenie konwersji. Wykorzystując wiedzę z zakresu zaawansowanych technik, przejdziemy przez konkretne kroki, które pozwolą na pełne wykorzystanie potencjału Facebook Ads w lokalnym SEO.

Spis treści

1. Metodologia dokładnego targetowania reklam Facebook w kontekście lokalnego SEO dla małych firm

a) Jak identyfikować i segmentować grupy docelowe na podstawie danych geolokalizacyjnych i demograficznych

Pierwszym krokiem na drodze do skutecznego targetowania jest precyzyjne określenie segmentów odbiorców. W praktyce oznacza to wykorzystanie danych z systemów CRM, Google Analytics oraz bezpośredniego badania rynku lokalnego. Ważne jest, aby tworzyć szczegółowe profile demograficzne, obejmujące wiek, płeć, stan cywilny oraz poziom wykształcenia, co pozwala na zawężenie grupy do najbardziej zainteresowanych.
Kolejnym etapem jest analiza danych geolokalizacyjnych – nie ograniczaj się wyłącznie do miasta, ale uwzględnij dzielnice, osiedla, a nawet konkretne ulice, jeśli to konieczne. Użyj narzędzi takich jak Facebook Audience Insights, aby zweryfikować, które obszary przynoszą największą aktywność i konwersje.

b) Jakie narzędzia i funkcje Facebooka wykorzystać do precyzyjnego ustawiania targetowania

Facebook oferuje rozbudowane możliwości targetowania, które można w pełni dostosować do potrzeb lokalnego SEO. Kluczem jest korzystanie z funkcji takich jak warunki (np. zainteresowania, zachowania), wykluczenia (np. odrzucenie odbiorców spoza określonego obszaru), a także tworzenie niestandardowych grup odbiorców (Custom Audiences) na podstawie list CRM, ruchu z witryny, czy interakcji z treściami na Facebooku.
Warto również korzystać z funkcji lokalizacji na poziomie dokładnych współrzędnych GPS, co umożliwia ustawienie targetowania na bardzo wąskie obszary – od ulicy do osiedla. Przy tym, ustawianie wykluczeń pozwala uniknąć niepotrzebnych wyświetleń w miejscach nieistotnych, co jest kluczowe dla efektywności kampanii.

c) Jak tworzyć profile odbiorców (buyer personas) z uwzględnieniem lokalnych preferencji i zachowań konsumenckich

Profil odbiorcy w kontekście lokalnego SEO wymaga głębokiego zrozumienia specyfiki rynku. Analiza zachowań konsumenckich polega na identyfikacji najczęstszych motywacji, motywacji zakupu, a także nawyków komunikacyjnych. Tworząc buyer personas, należy uwzględnić lokalne motywy, takie jak popularność określonych wydarzeń, sezonowość, czy lokalne święta.
Przy tym, warto korzystać z danych ankietowych, recenzji klientów i obserwacji konkurencji. Dla przykładu, dla sklepu spożywczego w Warszawie warto uwzględnić preferencje mieszkańców konkretnych dzielnic, ich zwyczaje zakupowe i oczekiwania wobec promocji.

d) Jak optymalizować targetowanie na podstawie zachowań offline i danych CRM

Kluczowe jest integracja danych offline z Facebookiem poprzez funkcję Offline Conversions. Proces obejmuje:

  • Krok 1: Zbieranie danych z systemu CRM – np. rejestracje, wizyty w sklepie, telefoniczne zapytania.
  • Krok 2: Konfiguracja zdarzeń offline w Menedżerze zdarzeń Facebooka, co wymaga przesłania danych za pomocą API, plików CSV lub integracji z systemami POS.
  • Krok 3: Mapowanie danych na profile użytkowników i tworzenie niestandardowych grup odbiorców.
  • Krok 4: Ustawienie kampanii targetujących te grupy, co pozwala na precyzyjne dotarcie do klientów, którzy odwiedzili sklep lub wykonali określoną akcję offline.

Uwzględnienie tych danych umożliwia tworzenie bardzo precyzyjnych segmentów, które zwiększają skuteczność działań reklamowych.

2. Techniczne aspekty ustawień kampanii reklamowych pod kątem lokalnego SEO

a) Jak poprawnie skonfigurować ustawienia lokalizacji w panelu Ads Manager

Podczas tworzenia kampanii w Menedżerze Reklam kluczowe jest precyzyjne ustawienie lokalizacji. Postępuj według poniższej procedury:

  1. Wybierz cel kampanii, np. „Zasięg” lub „Ruch”.
  2. W sekcji „Grupa odbiorców” kliknij „Lokalizacja”.
  3. Wprowadź dokładne współrzędne geograficzne (np. w formacie „lat, lon”) lub wybierz miasta, dzielnice, a następnie dostosuj promień (np. 1 km, 5 km).
  4. Użyj funkcji „Dostosuj lokalizację”, aby wykluczyć obszary, które nie są istotne (np. odrzucenie sąsiednich dzielnic).
  5. Ustawienia zapisz i przejdź do konfiguracji kreacji.

Ważne jest, aby testować różne konfiguracje, korzystając z funkcji podglądu, aby upewnić się, że reklama wyświetla się w właściwym obszarze.

b) Jak korzystać z funkcji geotargetowania na poziomie promień i geozón

Funkcja promienia (radius targeting) pozwala na ustawienie obszaru wokół wybranego punktu (np. sklepu) z dokładnością do kilku metrów, co jest idealne dla małych, lokalnych biznesów. Strategia obejmuje:

  • Ustalenie głównego punktu (np. geokod sklepu).
  • Dobór promienia – od 500 m do kilku kilometrów, w zależności od zasięgu działalności.
  • Testowanie różnych promieni, aby znaleźć optymalną wartość – np. 1 km często jest najbardziej efektywne w przypadku sklepów osiedlowych.

Geozón (geofencing) natomiast umożliwia tworzenie wirtualnych granic w określonych lokalizacjach (np. parkingi, centra handlowe). Używaj jej, gdy chcesz kierować reklamy do użytkowników w konkretnej placówce lub wydarzeniu, co wymaga ustawienia precyzyjnych współrzędnych GPS i odpowiednich warunków wyzwalających wyświetlenie reklamy.

c) Jak wprowadzać parametry wykluczeń, aby uniknąć nieefektywnych wyświetleń

Kluczowe jest korzystanie z funkcji wykluczeń, które pozwalają eliminować odbiorców spoza grupy docelowej. Proces obejmuje:

  • Dodanie wykluczeń geolokalizacyjnych – np. odrzucenie obszarów o niskim potencjale konwersji, takich jak odległe miejscowości lub rejony o niskim natężeniu ruchu.
  • Wykluczanie określonych zainteresowań lub zachowań – np. jeśli reklama jest skierowana do klientów zainteresowanych zdrowym stylem życia, wyklucz osoby nieinteresujące się sportem lub dietą.
  • Używanie niestandardowych grup odbiorców do wykluczeń, np. wykluczenie klientów już korzystających z usług, aby uniknąć powtórzeń.

Ważne, aby regularnie analizować raporty i wykluczać odbiorców, którzy nie generują konwersji, co pozwala na optymalizację kosztów i zwiększenie skuteczności.

d) Jak zarządzać budżetem i harmonogramem, aby maksymalizować lokalny zasięg w kluczowych godzinach

Optymalizacja budżetu wymaga precyzyjnego planowania. Zalecamy:

  • Ustawienie dziennego lub łącznego budżetu na poziomie, który pozwoli na testy A/B i optymalizację – np. początkowo 50-100 zł dziennie.
  • Korzystanie z funkcji automatycznego budżetu, gdy kampania osiąga stabilne wyniki, co pozwala na dynamiczne dostosowania i maksymalizację zasięgu.
  • Harmonogram wyświetleń – ustawienie wyświetleń głównie w godzinach szczytu (np. od 10:00 do 16:00), gdy użytkownicy są najbardziej aktywni lokalnie. Warto to zweryfikować na podstawie danych historycznych.

Przeprowadzanie testów w różnych porach dnia i monitorowanie kosztów konwersji umożliwiają wybranie najbardziej efektywnych godzin.

3. Kreacja i optymalizacja treści reklam pod lokalne SEO na Facebooku

a) Jak tworzyć przekazy, które rezonują z lokalnym odbiorcą

Kluczem do skutecznej komunikacji jest użycie języka, motywów i CTA (wezwania do działania), które są naturalne i zrozumiałe dla lokalnej społeczności. Zalecamy:

  • Wykorzystanie lokalnego dialektu lub popularnych zwrotów, które budują więź z odbiorcą.

Leave a Comment

Your email address will not be published. Required fields are marked *