Collegare due diversi tipi di post personalizzati
Spero che qualcuno possa aiutarmi poiché è molto frustrante...
Sto cercando di costruire un sito immobiliare in WordPress e vorrei collegare due tipi di post personalizzati -
Uno sarebbe per le aree di sviluppo e l'altro per le singole proprietà all'interno di quell'area di sviluppo
Capisco che posso creare un CPT chiamato 'Developments' e un altro chiamato 'Properties', ma come potrei collegarli insieme?.. per esempio, se creassi una proprietà e cercassi di collegarla a uno sviluppo, come funzionerebbe se sono tipi di post personalizzati separati?
Ho letto una domanda simile - Come collegare diversi CPT insieme? e la risposta di Scuba Kay era quasi quello di cui avevo bisogno, ma non so come si potrebbe interrogare una determinata proprietà che appartiene a un determinato Sviluppo
Grazie in anticipo!

Se ho capito bene, stai cercando di impostare una relazione uno-a-molti tra aree di sviluppo e proprietà.
Ecco una classe che, se caricata e poi istanziata, farà:
- creare una meta box per un post type chiamato development_area che contiene una lista di checkbox di tutte le proprietà disponibili, così da poter aggiornare facilmente la relazione tra i due.
- creare una meta box per un post type chiamato property che contiene una select box per scegliere l'area di sviluppo correlata a una proprietà.
- garantire che ci sia una relazione 1 a molti tra aree di sviluppo e proprietà, il che significa che un'area di sviluppo può essere correlata a molte proprietà, ma ogni proprietà avrà solo un'area di sviluppo correlata.
Nota:
Questo codice illustra come fare ciò. Mentre ho controllato gli errori stupidi e ricontrollato la logica, potresti dover modificare qualcosa poiché non l'ho testato in runtime. Lo faccio sempre e funziona molto bene.
Dopo la classe c'è un esempio di come farla funzionare e poi un paio di esempi su come usare le relazioni che la classe imposta.
class One_To_Many_Linker {
protected $_already_saved = false; # Usato per evitare il salvataggio due volte
public function __construct() {
$this->do_initialize();
}
Impostazione delle meta box e della funzionalità di salvataggio
protected function do_initialize() {
add_action(
'save_post',
array( $this, 'save_meta_box_data' ),
10,
2
);
add_action(
"add_meta_boxes_for_development_area",
array( $this, 'setup_development_area_boxes' )
);
add_action(
"add_meta_boxes_for_property",
array( $this, 'setup_property_boxes' )
);
}
Creazione della meta box necessaria Altre meta box possono essere facilmente impostate qui.
# Post type area di sviluppo
# - questo post type può avere più post di proprietà correlati
public function setup_development_area_boxes( \WP_Post $post ) {
add_meta_box(
'area_related_properties_box',
__('Proprietà Correlate', 'language'),
array( $this, 'draw_area_related_properties_box' ),
$post->post_type,
'advanced',
'default'
);
}
Disegno delle Proprietà Correlate Questo codice disegna le proprietà correlate come una serie di checkbox.
public function draw_area_related_properties_box( \WP_Post $post ) {
$all_properties = $this->get_all_of_post_type( 'property' );
$linked_property_ids = $this->get_linked_property_ids( $post->ID );
if ( 0 == count($all_properties) ) {
$choice_block = '<p>Nessuna proprietà attualmente nel sistema.</p>';
} else {
$choices = array();
foreach ( $all_properties as $property ) {
$checked = ( in_array( $property->ID, $linked_property_ids ) ) ? ' checked="checked"' : '';
$display_name = esc_attr( $property->post_title );
$choices[] = <<<HTML
<label><input type="checkbox" name="property_ids[]" value="{$property->ID}" {$checked}/> {$display_name}</label>
HTML;
}
$choice_block = implode("\r\n", $choices);
}
# Assicurati che l'utente intendesse fare questo.
wp_nonce_field(
"updating_{$post->post_type}_meta_fields",
$post->post_type . '_meta_nonce'
);
echo $choice_block;
}
Ottenere liste di post Questo recupera tutti i post di un tipo specifico. Se usi post sticky, potresti voler rimuovere il flag sticky negli argomenti args.
# Recupera tutti i post del tipo specificato
# Restituisce un array di oggetti post
protected function get_all_of_post_type( $type_name = '') {
$items = array();
if ( !empty( $type_name ) ) {
$args = array(
'post_type' => "{$type_name}",
'posts_per_page' => -1,
'order' => 'ASC',
'orderby' => 'title'
);
$results = new \WP_Query( $args );
if ( $results->have_posts() ) {
while ( $results->have_posts() ) {
$items[] = $results->next_post();
}
}
}
return $items;
}
Ottenere gli ID delle proprietà collegate per un'area di sviluppo
Dato un ID area di sviluppo, questo restituirà un array di tutti gli ID post delle proprietà collegate.
protected function get_linked_property_ids( $area_id = 0 ) {
$ids = array();
if ( 0 < $area_id ) {
$args = array(
'post_type' => 'property',
'posts_per_page' => -1,
'order' => 'ASC',
'orderby' => 'title',
'meta_query' => array(
array(
'key' => '_development_area_id',
'value' => (int)$area_id,
'type' => 'NUMERIC',
'compare' => '='
)
)
);
$results = new \WP_Query( $args );
if ( $results->have_posts() ) {
while ( $results->have_posts() ) {
$item = $results->next_post();
$ids[] = $item->ID;
}
}
}
return $ids;
}
Impostazione delle meta box per le Proprietà
Puoi facilmente aggiungere altre meta box qui se vuoi.
# Impostazione meta box per il post type
public function setup_property_boxes( \WP_Post $post ) {
add_meta_box(
'property_linked_area_box',
__('Area di Sviluppo Correlata', 'language'),
array( $this, 'draw_property_linked_area_box' ),
$post->post_type,
'advanced',
'default'
);
}
Disegno della meta box dell'editor delle Proprietà
Questo disegna un elemento select (menu a discesa) che permette all'utente di scegliere a quale area di sviluppo la proprietà è correlata. Usiamo una select box per assicurarci che solo una proprietà possa essere specificata, ma una lista di radio box funzionerebbe anche.
public function draw_property_linked_area_box( \WP_Post $post ) {
$all_areas = $this->get_all_of_post_type('development_area');
$related_area_id = $this->get_property_linked_area_id( $post->ID );
if ( 0 == $all_areas ) {
$choice_block = '<p>Nessuna area di sviluppo da scegliere ancora.</p>';
} else {
$choices = array();
$selected = ( 0 == $related_area_id )? ' selected="selected"':'';
$choices[] = '<option value=""' . $selected . '> -- Nessuna -- </option>';
foreach ( $all_areas as $area ) {
$selected = ( $area->ID == (int)$related_area_id ) ? ' selected="selected"' : '';
$display_name = esc_attr( $area->post_title );
$choices[] = <<<HTML
<option value="{$area->ID}" {$selected}>{$display_name}</option>
HTML;
}
$choice_list = implode("\r\n" . $choices);
$choice_block = <<<HTML
<select name="development_area_id">
{$choice_list}
</select>
HTML;
}
wp_nonce_field(
"updating_{$post->post_type}_meta_fields",
$post->post_type . '_meta_nonce'
);
echo $choice_block;
}
Il metodo di collegamento
Nota che stabiliamo i nostri collegamenti impostando una chiave _development_area_id
su ogni proprietà.
- le aree di sviluppo possono interrogare le proprietà con questa chiave per visualizzarle
le proprietà possono recuperare i loro meta o avere la loro query filtrata per recuperare i dati di sviluppo
protected function get_property_linked_area_id( $property_id = 0 ) { $area_id = 0; if ( 0 < $property_id ) { $area_id = (int) get_post_meta( $property_id, '_development_area_id', true ); } return $area_id; }
Salvataggio dei meta dati
Facciamo attenzione a salvare solo quando necessario e corretto. Vedi i commenti nel codice.
public function save_meta_box_data( $post_id = 0, \WP_Post $post = null ) {
$do_save = true;
$allowed_post_types = array(
'development_area',
'property'
);
# Non salvare se abbiamo già salvato i nostri aggiornamenti
if ( $this->_already_saved ) {
$do_save = false;
}
# Non salvare se non c'è un ID post o un post
if ( empty($post_id) || empty( $post ) ) {
$do_save = false;
} else if ( ! in_array( $post->post_type, $allowed_post_types ) ) {
$do_save = false;
}
# Non salvare per revisioni o autosalvataggi
if (
defined('DOING_AUTOSAVE')
&& (
is_int( wp_is_post_revision( $post ) )
|| is_int( wp_is_post_autosave( $post ) )
)
) {
$do_save = false;
}
# Assicurati che si stia lavorando sul post corretto
if ( !array_key_exists('post_ID', $_POST) || $post_id != $_POST['post_ID'] ) {
$do_save = false;
}
# Assicurati di avere i permessi necessari per salvare [ presuppone che entrambi i tipi usino edit_post ]
if ( ! current_user_can( 'edit_post', $post_id ) ) {
$do_save = false;
}
# Assicurati che il nonce e il referrer siano corretti.
$nonce_field_name = $post->post_type . '_meta_nonce';
if ( ! array_key_exists( $nonce_field_name, $_POST) ) {
$do_save = false;
} else if ( ! wp_verify_nonce( $_POST["{$nonce_field_name}"], "updating_{$post->post_type}_meta_fields" ) ) {
$do_save = false;
} else if ( ! check_admin_referer( "updating_{$post->post_type}_meta_fields", $nonce_field_name ) ) {
$do_save = false;
}
if ( $do_save ) {
switch ( $post->post_type ) {
case "development_area":
$this->handle_development_area_meta_changes( $post_id, $_POST );
break;
case "property":
$this->handle_property_meta_changes( $post_id, $_POST );
break;
default:
# Non facciamo nulla per altri tipi di post
break;
}
# Nota che abbiamo salvato i nostri dati
$this->_already_saved = true;
}
return;
}
Aggiornamento delle Proprietà di Sviluppo
Leggiamo la lista dei post type delle proprietà selezionate, recuperiamo la lista dei post type delle proprietà attualmente correlate, e poi usiamo quelle liste per determinare cosa aggiornare.
Nota: Qui stiamo aggiornando i meta dati sui post type delle proprietà, non sulla nostra area di sviluppo modificata.
# Le aree di sviluppo possono collegarsi a più proprietà
# ma ogni proprietà può collegarsi solo a una singola area di sviluppo
protected function handle_development_area_meta_changes( $post_id = 0, $data = array() ) {
# Ottieni gli ID delle proprietà attualmente collegate a quest'area di sviluppo
$linked_property_ids = $this->get_linked_property_ids( $post_id );
# Ottieni la lista degli ID delle proprietà selezionate quando l'utente ha salvato le modifiche
if ( array_key_exists('property_ids', $data) && is_array( $data['property_ids'] ) ) {
$chosen_property_ids = $data['property_ids'];
} else {
$chosen_property_ids = array();
}
# Costruisci una lista di proprietà da collegare o scollegare da quest'area
$to_remove = array();
$to_add = array();
if ( 0 < count( $chosen_property_ids ) ) {
# L'utente ha scelto almeno una proprietà da collegare
if ( 0 < count( $linked_property_ids ) ) {
# Avevamo già almeno una proprietà collegata
# Scorri le esistenti e nota quelle che l'utente non ha selezionato
foreach ( $linked_property_ids as $property_id ) {
if ( ! in_array( $property_id, $chosen_property_ids ) ) {
# Attualmente collegata, ma non selezionata. Rimuovila.
$to_remove[] = $property_id;
}
}
# Scorri le selezionate e nota quelle che non sono attualmente collegate
foreach ( $chosen_property_ids as $property_id ) {
if ( ! in_array( $property_id, $linked_property_ids ) ) {
# Selezionata ma non tra quelle attualmente collegate. Aggiungila.
$to_add[] = $property_id;
}
}
} else {
# Nessun ID precedentemente selezionato, aggiungili tutti
$to_add = $chosen_property_ids;
}
} else if ( 0 < count( $linked_property_ids ) ) {
# Nessuna proprietà scelta per essere collegata. Rimuovi tutte quelle attualmente collegate.
$to_remove = $linked_property_ids;
}
if ( 0 < count($to_add) ) {
foreach ( $to_add as $property_id ) {
# Questo sovrascriverà qualsiasi valore esistente per la chiave di collegamento
# per assicurarci di mantenere solo un'area di sviluppo collegata per ogni proprietà.
update_post_meta( $property_id, '_development_area_id', $post_id );
}
}
if ( 0 < count( $to_remove ) ) {
foreach ( $to_remove as $property_id ) {
# Questo cancellerà tutte le aree collegate esistenti per la proprietà
# per assicurarci di avere solo un'area collegata per proprietà
delete_post_meta( $property_id, '_development_area_id' );
}
}
}
Salvataggio delle Modifiche alle Proprietà
Poiché la nostra meta key è su ogni proprietà, semplicemente aggiorniamo i nostri meta dati se necessario. Poiché la lettura è quasi sempre più veloce in mysql della scrittura, aggiorniamo solo se assolutamente necessario.
# Le proprietà si riferiscono solo a una singola area di sviluppo
protected function handle_property_meta_changes( $post_id = 0, $data = array() ) {
# Ottieni qualsiasi area di sviluppo attualmente collegata
$linked_area_id = $this->get_property_linked_area_id( $post_id );
if ( empty($linked_area_id) ) {
$linked_area_id = 0;
}
if ( array_key_exists( 'development_area_id', $data ) && !empty($data['development_area_id'] ) ) {
$received_area_id = (int)$data['development_area_id'];
} else {
$received_area_id = 0;
}
if ( $received_area_id != $linked_area_id ) {
# Questo sovrascriverà tutte le copie esistenti della nostra meta key
# così possiamo assicurarci di avere solo un'area collegata per proprietà
update_post_meta( $post_id, '_development_area_id', $received_area_id );
}
}
}
Come Usare La Classe
Purché tu carichi la classe nel tuo file functions del tema o in un plugin, puoi usare il seguente codice per farla funzionare:
if ( is_admin() ) {
new One_To_Many_Linker();
}
Alcuni Casi d'Uso Seguono Di seguito, ho fornito un paio di casi d'uso front end.
- Visualizzazione di tutte le proprietà per l'area di sviluppo corrente
- Visualizzazione dell'area di sviluppo per una proprietà in un archivio o singola proprietà
Mostrare tutte le proprietà correlate all'area di sviluppo attualmente visualizzata
global $wp_query;
$area_id = $wp_query->get_queried_object_id();
$args = array(
'post_type' => 'property',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => '_development_area_id',
'value' => $area_id,
'compare' => '=',
'type' => 'NUMERIC'
)
)
);
$properties = new \WP_Query( $args );
if ( $properties->have_posts() ) {
while( $properties->have_posts() ) {
$property = $properties->next_post();
# fai qualcosa con la proprietà
$property_link = get_permalink( $property->ID );
$property_name = esc_attr( $property->post_title );
echo '<a href="' . $property_link . '">' . $property_name . '</a>';
}
}
Mostrare Aree di Sviluppo Collegate
Metodo 1: Recuperare i post meta, caricare l'area e usare i dati
- funziona su pagine dove is_singular('property') è vero
- funziona su pagine dove is_post_type_archive('property') è vero
global $post;
while ( have_posts() ) {
the_post();
$post_id = get_the_ID(); # si potrebbe usare $post->ID
$dev_area_id = get_post_meta( $post_id, '_development_area_id', true);
if ( !empty( $dev_area_id ) ) {
$development_area = get_post( $dev_area_id );
# fai qualcosa...
$dev_area_link = get_permalink ( $development_area->ID );
$dev_area_title = $development_area->post_title;
$dev_area_content = $development_area->post_content;
echo '<a href="' . $dev_area_link . '">' . $dev_area_title . '</a><br />' . $dev_area_content;
}
}
Metodo 2: Usare un filtro sulla query
- funziona su pagine dove is_singular('property') è vero
- funziona su pagine dove is_post_type_archive('property') è vero
Nota che questo evita di dover recuperare i meta del post type e fare una query extra per i dati dell'area di sviluppo. Man mano che mostri più proprietà su una singola pagina, questo ti fa risparmiare sempre più potenza di elaborazione.
Dove inserire il seguente codice:
- In un nuovo plugin che crei consigliato
- Nel tuo file functions.php del tema
add_filter('posts_clauses', 'do_my_maybe_modify_queries', 10, 2);
function do_my_maybe_modify_queries( $pieces, \WP_Query $wp_query ) {
if ( !is_admin() && $wp_query->is_main_query() ) {
# Non siamo nei pannelli di amministrazione e siamo la query principale per il template
if ( array_key_exists('post_type', $wp_query->query_vars) ) {
# È stato interrogato un post type di qualche tipo.
# Recuperalo come array dei tipi specificati
$value = $wp_query->query_vars['post_type'];
if ( !is_array( $value ) ) {
if ( empty( $value ) ) {
$post_types = array();
} else {
$post_types = array( $value );
}
} else {
$post_types = $value;
}
if ( in_array('property', $post_types) ) {
# È stata richiesta una proprietà
if ( $wp_query->is_post_type_archive || $wp_query->is_singular ) {
# Mostra l'archivio del post type della proprietà o una singola proprietà.
# Vogliamo aggiungere l'ID, il titolo e il contenuto dell'area di sviluppo ai campi restituiti
global $wpdb;
# Collega il post di sviluppo a ogni proprietà attraverso la sua meta key
# Poiché c'è solo 1 area di sviluppo per proprietà, questo funziona bene
$pieces['join'] .= <<<SQL
LEFT JOIN {$wpdb->prefix}postmeta AS dev_pm ON {$wpdb->prefix}posts.ID = dev_pm.post_id AND dev_pm.meta_key = '_development_area_id'
LEFT JOIN {$wpdb->prefix}posts AS dev_post ON dev_post.ID = dev_pm.meta_value
SQL;
# Aggiungi i campi del post di sviluppo che vogliamo a quelli restituiti dalla query
$pieces['fields'] .= ", IFNULL( dev_pm.meta_value, 0 ) as development_area_id";
$pieces['fields'] .= ", IFNULL( dev_post.post_title, '') as development_area_title";
$pieces['fields'] .= ", IFNULL( dev_post.post_content, '') as development_area_content";
}
}
}
}
return $pieces;
}
Con il codice sopra in posizione, puoi ora accedere ai dati come segue su una pagina singola di proprietà o archivio di proprietà. Lascio come esercizio al lettore fare questo per le tassonomie correlate alle proprietà.
if ( have_posts() ) {
global $post;
while ( have_posts() ) {
the_post();
if ( property_exists( $post, 'development_area_id' ) ) {
$dev_area_id = $post->development_area_id;
$dev_area_title = $post->development_area_title;
$dev_area_content = $post->development_area_content;
$dev_area_link = get_permalink( $dev_area_id );
echo '<a href="' . $dev_area_link . '">' . $dev_area_title . '</a><br />' . $dev_area_content;
}
}
}
Metodo 3: Filtrare WP_Query
Come il metodo di filtro sopra, ma utilizzabile per query personalizzate usando WP_Query. Ottimo se vuoi scrivere uno shortcode o un widget che mostra un gruppo di proprietà.
Prima, creiamo il nostro filtro (molto simile a quello mostrato nel metodo 2)
function add_dev_data_to_wp_query( $pieces, \WP_Query $wp_query ) {
global $wpdb;
if ( !is_admin() && !$wp_query->is_main_query() ) {
# Collega il post di sviluppo a ogni proprietà attraverso la sua meta key
# Poiché c'è solo 1 area di sviluppo per proprietà, questo funziona bene
$pieces['join'] .= <<<SQL
LEFT JOIN {$wpdb->prefix}postmeta AS dev_pm ON {$wpdb->prefix}posts.ID = dev_pm.post_id AND dev_pm.meta_key = '_development_area_id'
LEFT JOIN {$wpdb->prefix}posts AS dev_post ON dev_post.ID = dev_pm.meta_value
SQL;
# Aggiungi i campi del post di sviluppo che vogliamo a quelli restituiti dalla query
$pieces['fields'] .= ", IFNULL( dev_pm.meta_value, 0 ) as development_area_id";
$pieces['fields'] .= ", IFNULL( dev_post.post_title, '') as development_area_title";
$pieces['fields'] .= ", IFNULL( dev_post.post_content, '') as development_area_content";
}
return $pieces;
}
Poi, applichiamo il nostro filtro prima di creare la query e lo rimuoviamo subito dopo per assicurarci di non alterare altre query.
$args = array(
'post_type' => 'property',
'posts_per_page' => -1
);
# Applica il nostro filtro
add_filter('posts_clauses', 'add_dev_data_to_wp_query', 10, 2);
# Esegui la query
$properties = new \WP_Query( $args );
# Rimuovi il nostro filtro
remove_filter('posts_clauses', 'add_dev_data_to_wp_query', 10);
if ( $properties->have_posts() ) {
while( $properties->have_posts() ) {
$property = $properties->next_post();
# Fai qualcosa con le tue proprietà
echo '<p>Il nome della proprietà è ' . $property->post_title . '</p>';
# Fai qualcosa con le aree di sviluppo correlate se i dati sono disponibili
if ( property_exists( $property, 'development_area_id' ) ) {
echo '<p>Parte dell'area di sviluppo chiamata ' . $property->development_area_title . '</p>';
}
}
}
Anche se richiede un po' più di lavoro iniziale per fare cose come queste, fornisce un paio di bei vantaggi
- Stai facendo solo 1 query invece di 2 o 3 query per mostrare informazioni correlate. Questo si somma velocemente.
- Una volta che ti sei abituato, puoi usarlo con relazioni a più oggetti che ti faranno risparmiare più query extra ciascuna
- Usando una variazione su questo, puoi fare lo stesso per aggiungere titoli (e link di visualizzazione e modifica) alle colonne dell'elenco degli editor. Per esempio, con lo scenario sopra potresti elencare lo sviluppo per ogni proprietà nella lista delle proprietà nell'amministrazione.
Naturalmente, questo è già abbastanza lungo.

Puoi utilizzare il fantastico e gratuito plugin Advanced Custom Fields, che ti permette di creare un campo personalizzato di tipo Relazione, che collega un articolo a un altro articolo.
Ad ogni modo, prima di tutto dovresti assicurarti di aver realmente bisogno di due tipi di contenuto personalizzati, e che non puoi utilizzare tassonomie o semplicemente campi personalizzati. Ad esempio, una singola Proprietà potrebbe essere un tipo di contenuto, mentre l'Area di Sviluppo a cui appartiene potrebbe essere una Tassonomia o un Campo Personalizzato contenente il nome di quell'Area di Sviluppo... Fondamentalmente dipende da quante informazioni hai bisogno di conservare sia sulle Aree di Sviluppo che sulle Proprietà...

Preferirei mantenerlo semplice e fare in modo che le proprietà all'interno di un complesso immobiliare siano articoli figli dell'articolo genitore del complesso. Potresti anche utilizzare la tassonomia standard delle categorie e contrassegnare ogni proprietà "secondaria" come parte del complesso più grande. Dovresti semplicemente inserire questi complessi come categorie nella tassonomia. In questo modo mantieni tutte le proprietà in un unico elenco lungo e facile da gestire. Puoi includere o escludere ciò che ti serve quando richiami questo elenco utilizzando il parametro di profondità o la tassonomia.

Grazie per il tuo aiuto, come potrei fare per rendere le proprietà trovate all'interno di uno sviluppo come post figli del post genitore dello sviluppo?

Prima crea la pagina/post genitore (sviluppo). Poi aggiungi una nuova pagina e sul lato destro dell'editor dovresti vedere un box degli attributi della pagina con uno dei campi chiamato Genitore con un menu a discesa. Usalo per selezionare lo sviluppo. Questo dovrebbe bastare!
