PluginProbe
Property Hive / 2.3.0
Property Hive v2.3.0
2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 1.4.61 1.4.62 All 260 releases
propertyhive / includes / class-ph-aioseo.php

class-ph-aioseo.php in Property Hive 2.3.0, at includes/class-ph-aioseo.php

213 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit; // Exit if accessed directly
5 }
6
7 /**
8 * AIOSEO Compatibility
9 *
10 * @class PH_AIOSEO
11 * @version 1.0.0
12 * @package PropertyHive/Classes/
13 * @category Class
14 * @author PropertyHive
15 */
16 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Preserve the existing public PH_AIOSEO class name for plugin and extension compatibility.
17 class PH_AIOSEO {
18
19 /** @var PH_AIOSEO The single instance of the class */
20 protected static $_instance = null;
21
22 /**
23 * Hook in methods
24 */
25 public static function init()
26 {
27 add_filter( 'aioseo_post_metabox_priority', array( __CLASS__, 'meta_box_to_bottom') );
28 add_filter( 'aioseo_sitemap_exclude_posts', array( __CLASS__, 'sitemap_exclude_off_market'), 10, 2 );
29
30 add_filter( 'aioseo_schema_output', [ __CLASS__, 'add_real_estate_listing_schema' ] );
31 }
32
33 public static function meta_box_to_bottom( $priority )
34 {
35 return 'low';
36 }
37
38 public static function sitemap_exclude_off_market( $ids, $type )
39 {
40 $ids = is_array( $ids ) ? $ids : [];
41
42 $off_market = get_posts(array(
43 'fields' => 'ids',
44 'posts_per_page' => -1,
45 'post_type' => 'property',
46 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Sitemap exclusions require all property IDs explicitly marked off-market in the existing metadata schema; preserve the complete exclusion list and status semantics.
47 'meta_query' => array(
48 array(
49 'key' => '_on_market',
50 'value' => '',
51 )
52 )
53 ));
54
55 if ( $off_market ) {
56 $ids = array_merge( $ids, $off_market );
57 }
58 return array_values( array_unique( array_map( 'intval', $ids ) ) );
59 }
60
61 public static function add_real_estate_listing_schema( $graphs )
62 {
63 if ( ! is_singular( 'property' ) ) {
64 return $graphs;
65 }
66
67 $post_id = get_queried_object_id();
68 if ( ! $post_id ) {
69 return $graphs;
70 }
71
72 $property = new PH_Property($post_id);
73
74 // Common fields (adjust to your meta keys as needed).
75 $title = get_the_title( $post_id );
76 $url = get_permalink( $post_id );
77 $description = wp_strip_all_tags( get_the_excerpt( $post_id ) ?: '', true );
78 $datePosted = gmdate("Y-m-d\TH:i:s", strtotime($property->_on_market_change_date)) . "+00:00";
79
80 $department = $property->_department;
81
82 $street = $property->_address_street;
83 $locality = $property->_address_two;
84 $region = $property->_address_three;
85 $postcode = $property->_address_postcode;
86 $country = $property->_address_country;
87 $lat = $property->_latitude;
88 $lng = $property->_longitude;
89
90 // Address bits (adjust to your meta keys).
91 $address = array_filter( [
92 '@type' => 'PostalAddress',
93 'streetAddress' => $street,
94 'addressLocality' => $locality,
95 'addressRegion' => $region,
96 'postalCode' => $postcode,
97 'addressCountry' => $country,
98 ] );
99
100 $bedrooms = (int)$property->_bedrooms;
101 $bathrooms = (int)$property->_bathrooms;
102
103 $images = array();
104 if ( get_option('propertyhive_images_stored_as', '') == 'urls' )
105 {
106 $photo_urls = $property->_photo_urls;
107 if ( !is_array($photo_urls) ) { $photo_urls = array(); }
108
109 if ( !empty($num_images) )
110 {
111 $photo_urls = array_slice($photo_urls, 0, $num_images);
112 }
113
114 foreach ( $photo_urls as $photo )
115 {
116 $images[] = isset($photo['url']) ? $photo['url'] : '';
117 }
118 }
119 else
120 {
121 $gallery_attachments = $property->get_gallery_attachment_ids();
122
123 if ( !empty($gallery_attachments) )
124 {
125 if ( !empty($num_images) )
126 {
127 $gallery_attachments = array_slice($gallery_attachments, 0, $num_images);
128 }
129
130 foreach ($gallery_attachments as $gallery_attachment)
131 {
132 $images[] = wp_get_attachment_url( $gallery_attachment );
133 }
134 }
135 }
136 $images = array_values( array_unique( array_filter( $images ) ) );
137
138 $types = array( 'Residence' );
139 if ( $department != 'commercial' && ph_get_custom_department_based_on($department) != 'commercial' )
140 {
141 $types[] = 'SingleFamilyResidence';
142 }
143 $item_offered = array_filter( [
144 '@type' => $types,
145 'address' => ! empty( $address ) ? $address : null,
146 ] );
147 if ( $lat && $lng ) {
148 $item_offered['geo'] = [
149 '@type' => 'GeoCoordinates',
150 'latitude' => (float)$lat,
151 'longitude' => (float)$lng,
152 ];
153 }
154 if ( $bedrooms && $department != 'commercial' && ph_get_custom_department_based_on($department) != 'commercial' ) { $item_offered['numberOfBedrooms'] = $bedrooms; }
155 if ( $bathrooms && $department != 'commercial' && ph_get_custom_department_based_on($department) != 'commercial' ) { $item_offered['numberOfBathroomsTotal'] = $bathrooms; }
156
157 // Attach an Offer (price, currency, availability) that points at the Residence
158 if ( $department == 'residential-sales' || ph_get_custom_department_based_on($department) == 'residential-sales' )
159 {
160 $offer = [
161 '@type' => 'Offer',
162 'price' => $property->_poa != 'yes' ? (float)$property->_price : '',
163 'priceCurrency' => $property->_currency,
164 // Use a schema.org ItemAvailability URL if you have one; default to InStock
165 //'availability' => $availability ?: 'https://schema.org/InStock',
166 'businessFunction' => 'https://purl.org/goodrelations/v1#Sell',
167 'itemOffered' => $item_offered,
168 'url' => $url
169 ];
170 }
171 elseif ( $department == 'residential-lettings' || ph_get_custom_department_based_on($department) == 'residential-lettings' )
172 {
173 $offer = [
174 '@type' => 'Offer',
175 'price' => $property->_poa != 'yes' ? (float)$property->_rent : '',
176 'priceCurrency' => $property->_currency,
177 'priceSpecification' => [
178 '@type' => 'UnitPriceSpecification',
179 'price' => $property->_poa != 'yes' ? (float)$property->_rent : '',
180 'priceCurrency' => $property->_currency,
181 'unitText' => $property->_rent_frequency,
182 ],
183 'businessFunction' => 'https://purl.org/goodrelations/v1#LeaseOut',
184 // Use a schema.org ItemAvailability URL if you have one; default to InStock
185 //'availability' => $availability ?: 'https://schema.org/InStock',
186 'itemOffered' => $item_offered,
187 'url' => $url
188 ];
189 }
190
191 $realEstateListing = array_filter( [
192 '@type' => 'RealEstateListing',
193 '@id' => trailingslashit( $url ) . '#realEstateListing',
194 'url' => $url,
195 'name' => $title,
196 'description' => $description,
197 'datePosted' => $datePosted,
198 'image' => ! empty( $images ) ? $images : null,
199 'mainEntityOfPage' => $url,
200 'offers' => $offer,
201 ] );
202
203 // Append (don’t replace) our graph.
204 if ( ! is_array( $graphs ) ) {
205 $graphs = [];
206 }
207 $graphs[] = $realEstateListing;
208
209 return $graphs;
210 }
211 }
212
213 PH_AIOSEO::init();