PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 4.5.0
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v4.5.0
4.5.1 4.5.0 4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 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.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.11.1 1.11.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.10.0 2.10.1 2.11.0 2.11.1 2.11.2 2.11.3 2.11.4 2.12.0 2.13.0 2.14.0 2.14.1 2.15.0 2.15.1 2.16.0 2.16.1 2.16.2 2.16.3 2.17.0 2.17.1 2.17.2 2.18.0 2.19.0 2.19.2 2.19.3 2.19.4 2.2.0 2.2.1 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.22.0 2.22.1 2.23.0 2.24.0 2.25.0 2.25.1 2.25.2 2.26.0 2.27.0 2.27.1 2.28.0 2.29.0 2.29.1 2.29.2 2.29.3 2.29.4 2.3.0 2.3.1 2.30.0 2.31.0 2.31.1 2.31.2 2.31.3 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.40.0 2.40.1 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.9.0 3.0.0 3.0.0-RC1 3.0.0-RC2 3.0.0-beta1 3.0.0-beta2 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.10.0 3.10.1 3.11.0 3.12.0 3.13.0 3.13.1 3.13.2 3.13.3 3.13.4 3.14.0 3.15.0 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.16.6 3.16.7 3.16.8 3.17.0 3.17.1 3.17.2 3.17.3 3.17.4 3.17.5 3.17.6 3.18.0 3.19.0 3.19.1 3.19.2 3.2.0 3.2.1 3.2.2 3.20.0 3.20.1 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 4.0.0 4.0.1 4.0.2 4.0.3 trunk 4.1.0 0.2.19.1 4.1.1 1.0.0 4.2.0 1.0.1 1.0.2
surecart / app / src / Models / Blocks / AbstractProductListBlock.php
surecart / app / src / Models / Blocks Last commit date
AbstractProductListBlock.php 2 months ago ProductListBlock.php 2 months ago ProductPageBlock.php 2 months ago ProductReviewBlock.php 4 months ago ProductReviewListBlock.php 4 months ago RelatedProductsBlock.php 2 months ago
AbstractProductListBlock.php
217 lines
1 <?php
2
3 namespace SureCart\Models\Blocks;
4
5 /**
6 * Abstract product list block.
7 */
8 abstract class AbstractProductListBlock {
9 /**
10 * The block.
11 *
12 * @var \WP_Block
13 */
14 protected $block;
15
16 /**
17 * The URL.
18 *
19 * @var object
20 */
21 protected $url;
22
23 /**
24 * The query.
25 *
26 * @var \WP_Query
27 */
28 protected $query;
29
30 /**
31 * Constructor.
32 *
33 * @param \WP_Block $block The block.
34 */
35 public function __construct( \WP_Block $block ) {
36 $this->block = $block;
37 $this->url = \SureCart::block()->urlParams( 'products' );
38 }
39
40 /**
41 * Get the URL.
42 *
43 * @return object|null
44 */
45 public function urlParams() {
46 return $this->url;
47 }
48
49 /**
50 * Get a query attribute with fallback to context and default.
51 *
52 * @param string $key The attribute key to fetch.
53 * @param mixed $default_value The default value if not found.
54 *
55 * @return mixed
56 */
57 protected function getQueryAttribute( $key, $default_value = null ) {
58 return $this->block->parsed_block['attrs']['query'][ $key ] ?? $this->block->context['query'][ $key ] ?? $default_value;
59 }
60
61 /**
62 * Get the query attribute.
63 *
64 * @param string $key The key.
65 * @return mixed
66 */
67 public function __get( $key ) {
68 if ( 'has_pagination' === $key ) {
69 $max_page = $this->getQueryAttribute( 'pages', 0 );
70
71 // if the max page is 1, we don't have a next page.
72 if ( 1 === $max_page ) {
73 return false;
74 }
75
76 return $this->max_num_pages > 1;
77 }
78
79 // handle pagination.
80 if ( 'next_page_link' === $key ) {
81 $max_page = $this->getQueryAttribute( 'pages', 0 );
82
83 // if the max page is 1, we don't have a next page.
84 if ( 1 === $max_page ) {
85 return '';
86 }
87
88 // if the max page is greater than or equal to the query max num pages, we don't have a next page.
89 if ( $max_page && $max_page === $this->paged ) {
90 return '';
91 }
92
93 return $this->max_num_pages && $this->max_num_pages !== $this->paged ? $this->url->addPageArg( $this->paged + 1 )->url() : '';
94 }
95
96 if ( 'previous_page_link' === $key ) {
97 $max_page = $this->getQueryAttribute( 'pages', 0 );
98
99 // if the max page is 1, we don't have a next page.
100 if ( 1 === $max_page ) {
101 return '';
102 }
103
104 return $this->paged > 1 ? $this->url->addPageArg( $this->paged - 1 )->url() : '';
105 }
106
107 if ( 'pagination_links' === $key ) {
108 $max_page = $this->getQueryAttribute( 'pages', 0 );
109
110 // if the max page is 1, we don't have a next page.
111 if ( 1 === $max_page ) {
112 return [];
113 }
114
115 return array_map(
116 function ( $i ) {
117 return array(
118 'href' => $this->url->addPageArg( $i )->url(),
119 'name' => $i,
120 'current' => (int) $i === (int) $this->paged,
121 );
122 },
123 range( 1, $max_page ? min( $this->max_num_pages, $max_page ) : $this->max_num_pages )
124 );
125 }
126
127 if ( 'products' === $key ) {
128 // Check if we have a query object (for WP_Query based lists).
129 if ( ! empty( $this->query ) && ! empty( $this->query->posts ) ) {
130 return array_map(
131 function ( $post ) {
132 return sc_get_product( $post );
133 },
134 $this->query->posts
135 );
136 }
137 return [];
138 }
139
140 // Try to get from query object first.
141 if ( ! empty( $this->query ) ) {
142 return $this->query->$key ?? $this->query->query[ $key ] ?? $this->query->query_vars[ $key ] ?? null;
143 }
144
145 return null;
146 }
147
148 /**
149 * Batch-prime gallery attachment caches for all products in the query.
150 *
151 * Collects all numeric attachment IDs from each product's gallery_ids
152 * metadata and loads them in a single query via WordPress core,
153 * eliminating N+1 get_post() calls in GalleryItemAttachment::create().
154 */
155 protected function primeAttachmentCaches() {
156 if ( empty( $this->query->posts ) ) {
157 return;
158 }
159
160 $ids = $this->collectGalleryAttachmentIds( $this->query->posts );
161
162 if ( ! empty( $ids ) ) {
163 _prime_post_caches( array_unique( $ids ), false, true );
164 }
165 }
166
167 /**
168 * Collect all numeric WP attachment IDs from product gallery metadata.
169 *
170 * @param \WP_Post[] $posts The product posts.
171 *
172 * @return int[]
173 */
174 protected function collectGalleryAttachmentIds( array $posts ) {
175 $ids = [];
176
177 foreach ( $posts as $post ) {
178 $product = sc_get_product( $post );
179 if ( empty( $product ) ) {
180 continue;
181 }
182
183 $gallery_ids = $product->gallery_ids ?? [];
184 if ( ! is_array( $gallery_ids ) ) {
185 continue;
186 }
187
188 foreach ( $gallery_ids as $item ) {
189 if ( is_numeric( $item ) ) {
190 $ids[] = (int) $item;
191 } elseif ( is_object( $item ) && isset( $item->id ) && is_numeric( $item->id ) ) {
192 $ids[] = (int) $item->id;
193 } elseif ( is_array( $item ) && isset( $item['id'] ) && is_numeric( $item['id'] ) ) {
194 $ids[] = (int) $item['id'];
195 }
196 }
197 }
198
199 return $ids;
200 }
201
202 /**
203 * Call the query method.
204 *
205 * @param string $method The method.
206 * @param array $args The arguments.
207 *
208 * @return mixed
209 */
210 public function __call( $method, $args ) {
211 if ( ! empty( $this->query ) ) {
212 return $this->query->$method( ...$args );
213 }
214 return null;
215 }
216 }
217