PluginProbe
Pixelavo – Server Side Tracking & Pixel + AI Ads Tools / trunk
Pixelavo – Server Side Tracking & Pixel + AI Ads Tools vtrunk
1.5.5 1.5.4 trunk 1.0.0 1.0.1 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.2 All 47 releases
pixelavo / includes / pixel-feed.php

pixel-feed.php in Pixelavo – Server Side Tracking & Pixel + AI Ads Tools trunk, at includes/pixel-feed.php

304 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Pixelavo;
4
5 /* Exit if accessed directly */
6 if (!defined('ABSPATH')) {
7 exit;
8 }
9
10 class PixelFeed{
11
12 private static $_instance = null;
13
14 /**
15 * Instance.
16 * Initializes a singleton instance.
17 * @return self class
18 */
19 static function instance() {
20 if ( is_null( self::$_instance ) ) {
21 self::$_instance = new self();
22 }
23 return self::$_instance;
24 }
25
26 function __construct() {
27 add_action('init', [$this, 'init_feed']);
28 add_filter('feed_content_type', [$this, 'feed_content_type'], 10, 2);
29 }
30
31 function feed_content_type($content_type, $type) {
32 if ('pixelavo' === $type) {
33 return feed_content_type( 'rss-http' );
34 }
35 return $content_type;
36 }
37
38 /**
39 * Feed.
40 * Run when user search for any product and go to `search` page.
41 * @return void
42 */
43 function init_feed() {
44 add_feed( 'pixelavo', [$this, 'pixel_feed'] );
45 }
46
47 function pixel_feed() {
48
49 /**
50 * Settings
51 */
52 $settings = get_option('pixelavo_settings');
53 $exclude_categories = array_key_exists('exclude_categories', $settings) ? $settings['exclude_categories'] : [];
54 $exclude_tags = array_key_exists('exclude_tags', $settings) ? $settings['exclude_tags'] : [];
55
56 /**
57 * Products
58 */
59 $args = [
60 'status' => 'publish',
61 'limit' => -1,
62 'orderby' => 'ID',
63 'order' => 'ASC',
64 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
65 'tax_query' => [
66 'relation' => 'AND',
67 [
68 'taxonomy' => 'product_cat',
69 'field' => 'term_id',
70 'terms' => $exclude_categories,
71 'operator' => 'NOT IN',
72 ],
73 [
74 'taxonomy' => 'product_tag',
75 'field' => 'term_id',
76 'terms' => $exclude_tags,
77 'operator' => 'NOT IN',
78 ]
79 ],
80 ];
81 $products = wc_get_products($args);
82
83 /**
84 * Feed Namespace
85 */
86 $namespace = 'http://base.google.com/ns/1.0';
87
88 /**
89 * Feed RSS
90 */
91 $rss = new \SimpleXMLElement("<?xml version='1.0' encoding='UTF-8'?><rss xmlns:g='{$namespace}'></rss>");
92 $rss->addAttribute('version', '2.0');
93
94 /**
95 * Feed Channel
96 */
97 $channel = $rss->addChild('channel');
98 $channel->addChild('title', get_bloginfo('name'));
99 if(!empty(get_bloginfo('description'))) {
100 $channel->addChild('description', get_bloginfo('description'));
101 }
102 $channel->addChild('link', get_bloginfo('url'));
103
104 /**
105 * Feed Item
106 */
107 foreach ($products as $product) {
108 $pro['id'] = $settings['product_identifier'] == 'post_id' ? $product->get_id() : $product->get_sku();
109 $pro['group_id'] = $pro['id'];
110 $pro['title'] = $product->get_title();
111 $pro['desc'] = $settings['description_field'] == 'description' ? $product->get_description() : $product->get_short_description();
112 $pro['link'] = $product->get_permalink();
113 $pro['image_link'] = $this->get_image_link($product->get_id());
114 $pro['brand'] = $this->get_brand($product->get_id());
115 $pro['condition'] = $this->get_condition($product->get_id());
116 $pro['availability'] = $product->is_in_stock() ? 'in stock' : 'out of stock';
117 $pro['price'] = $this->get_price($product);
118 $pro['extra_fields'] = $this->get_extra_fields($product);
119 $pro['additional_images'] = $this->get_additional_images($product);
120 $pro['google_product_category'] = empty( $settings['product_feed_gpc'] ) ? $product->get_attribute( 'google_product_category' ) : $settings['product_feed_gpc'];
121
122 if($product->is_type('variable') && pixelavo_get_option('include_variations', 'pixelavo_settings') == 'on') {
123 foreach ($product->get_available_variations() as $variation) {
124 $pro['image_link'] = $this->get_image_link($product->get_id(), $variation);
125 $pro['price'] = $this->get_price($product, $variation);
126 $pro['availability'] = $variation['is_in_stock'] ? 'in stock' : 'out of stock';
127 $pro['link'] = !empty($variation['url']) ? $variation['url'] : $product->get_permalink();
128 $pro['additional_images'] = $this->get_additional_images($product, $variation);
129 $this->feed_item($channel, $pro, $namespace, $variation);
130 }
131 } else {
132 $this->feed_item($channel, $pro, $namespace);
133 }
134 }
135
136 echo $rss->asXML(); // phpcs:ignore
137 }
138
139 /**
140 * Feed item function
141 */
142 function feed_item($channel, $product, $namespace, $variation = false) {
143
144 if($variation) {
145 $product['id'] = $product['id'] . '-' . $variation['variation_id'];
146 $product['title'] = $product['title'] . ' - ' . $this->get_variation_title($variation['attributes']);
147 $product['extra_fields'] = array_merge($product['extra_fields'], $this->get_extra_fields(null,$variation['attributes']));
148 }
149
150 $item = $channel->addChild('item');
151 $item->addChild("g:id", $product['id'], $namespace);
152 $item->addChild("g:item_group_id", $product['group_id'], $namespace);
153 $item->addChild("g:title", $product['title'], $namespace);
154 $item->addChild("g:description", $product['desc'], $namespace);
155 $item->addChild("g:availability", $product['availability'], $namespace);
156 $item->addChild("g:condition", $product['condition'], $namespace);
157 if($product['price']) {
158 $item->addChild("g:price", $product['price'], $namespace);
159 }
160 $item->addChild("g:link", $product['link'], $namespace);
161 if($product['image_link']) {
162 $item->addChild("g:image_link", $product['image_link'], $namespace);
163 }
164 if(isset($product['google_product_category']) && !empty($product['google_product_category'])) {
165 $item->addChild("g:google_product_category", $product['google_product_category'], $namespace);
166 }
167 if(!empty($product['brand'])) {
168 $item->addChild("g:brand", $product['brand'], $namespace);
169 }
170 foreach ($product['extra_fields'] as $key => $value) {
171 $item->addChild("g:{$key}", $value, $namespace);
172 }
173 foreach ($product['additional_images'] as $image) {
174 $item->addChild("g:additional_image_link", $image, $namespace);
175 }
176 }
177
178 /**
179 * Get variation product title
180 */
181 function get_variation_title($variations) {
182 if (is_array($variations)) {
183 $variation_values = [];
184 foreach ($variations as $variation) {
185 if(!empty($variation)) {
186 $variation_values[] = $variation;
187 }
188 }
189 return '(' . implode( '-', $variation_values ) . ')';
190 }
191 return '';
192 }
193
194 /**
195 * Get product thumbnail image link
196 */
197 function get_image_link($product_id, $variation = null) {
198 // Try variation image first if provided and has an image
199 if (is_array($variation) && !empty($variation['image_id'])) {
200 $image = wp_get_attachment_image_src($variation['image_id'], 'single-post-thumbnail');
201 if ($image) {
202 return $image[0];
203 }
204 }
205 // Fall back to parent product thumbnail
206 $image = wp_get_attachment_image_src(get_post_thumbnail_id($product_id), 'single-post-thumbnail');
207 if ($image) {
208 return $image[0];
209 }
210 return false;
211 }
212
213 /**
214 * Get brand name
215 */
216 function get_brand($product_id) {
217 $brand_terms = wp_get_post_terms( $product_id, 'product_brand' );
218 if (!empty($brand_terms) && !is_wp_error($brand_terms)) {
219 return $brand_terms[0]->name;
220 }
221 $settings = get_option('pixelavo_settings');
222 return isset($settings['product_feed_brand']) ? $settings['product_feed_brand'] : '';
223 }
224
225 /**
226 * Get product condition
227 */
228 function get_condition($product_id) {
229 $condition = get_post_meta($product_id, '_product_condition', true);
230 if (empty($condition)) {
231 return 'new';
232 }
233 return $condition;
234 }
235
236 /**
237 * Get product price
238 */
239 function get_price($product, $variation = null) {
240 $the_price = is_array($variation) ? $variation['display_price'] : wc_get_price_to_display( $product );
241 if ($the_price) {
242 return $the_price . ' ' . get_woocommerce_currency();
243 }
244 return false;
245 }
246
247 /**
248 * Get product extra fields
249 */
250 function get_extra_fields($product, $variation = null) {
251 $fields = ['color', 'gender', 'pattern', 'material', 'size'];
252 $extra_fields = [];
253 forEach ( $fields as $field ) {
254 if ( isSet($variation[ 'attribute_pa_' . $field ]) && !empty($variation[ 'attribute_pa_' . $field ])) {
255 $extra_fields[$field] = $variation[ 'attribute_pa_' . $field ];
256 } else if (isSet($product) && $product->get_attribute($field) && !empty($product->get_attribute($field))) {
257 $extra_fields[$field] = $product->get_attribute($field);
258 }
259 }
260 return $extra_fields;
261 }
262
263 /**
264 * Get product categories
265 */
266 function get_categories($product) {
267 $categories = get_the_terms($product->get_id(), 'product_cat');
268 $category_string = '';
269 if (!empty($categories)) {
270 $categories = wp_list_sort($categories, ['parent' => 'ASC', 'name' => 'ASC'], 'ASC');
271 foreach ($categories as $category) {
272 $category_string = empty($category_string) ? $category_string . $category->name : $category_string .' > '. $category->name;
273 }
274 }
275 return $category_string;
276 }
277
278 /**
279 * Get product additional images
280 */
281 function get_additional_images( $product, $variation = null ) {
282 $images = [];
283 /* Variation Image */
284 $variation_image = is_array($variation) ? wp_get_attachment_image_src($variation['image_id'], 'single-post-thumbnail') : false;
285 if ($variation_image) {
286 $images[] = $variation_image[0];
287 }
288 /* Gallery Images */
289 $gallery_ids = $product->get_gallery_image_ids();
290 if (!empty($gallery_ids)) {
291 foreach($gallery_ids as $gallery_id) {
292 $images[] = wp_get_attachment_url($gallery_id);
293 }
294 }
295 return array_unique($images);
296 }
297
298 }
299
300 /**
301 * Initialize PixelFeed Class
302 */
303 PixelFeed::instance();
304