PluginProbe
Elementor Website Builder – more than just a page builder / 4.0.0-dev3
Elementor Website Builder – more than just a page builder v4.0.0-dev3
4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 All 454 releases
← All changes | modules/global-classes/global-classes-repository.php +47 -343 4.1.0-beta3 → 4.0.0-dev3 View file →
@@ -1,12 +1,9 @@
1 1 <?php
2 2 namespace Elementor\Modules\GlobalClasses;
3 3
4 -use Elementor\Core\Kits\Documents\Kit;
5 -use Elementor\Modules\DesignSystemSync\Classes\Global_Classes_Sync_Map;
6 -use Elementor\Modules\GlobalClasses\Concerns\Has_Kit_Dependency;
7 -use Elementor\Modules\GlobalClasses\Concerns\Has_Preview_Context;
8 -use Elementor\Modules\GlobalClasses\Utils\Global_Class_Data_Normalizer;
4 +use Elementor\Modules\AtomicWidgets\PropTypeMigrations\Migrations_Orchestrator;
5 +use Elementor\Plugin;
9 6
10 7 if ( ! defined( 'ABSPATH' ) ) {
11 8 exit; // Exit if accessed directly.
12 9 }
@@ -11,10 +8,8 @@
11 8 exit; // Exit if accessed directly.
12 9 }
13 10
14 11 class Global_Classes_Repository {
15 - use Has_Kit_Dependency;
16 - use Has_Preview_Context;
17 12
18 13 const META_KEY_FRONTEND = '_elementor_global_classes';
19 14 const META_KEY_PREVIEW = '_elementor_global_classes_preview';
20 15
@@ -20,381 +15,90 @@
20 15
21 16 const CONTEXT_FRONTEND = 'frontend';
22 17 const CONTEXT_PREVIEW = 'preview';
23 18
24 - const READ_BATCH_SIZE = 100;
25 - const PERSIST_BATCH_SIZE = 100;
19 + private string $context = self::CONTEXT_FRONTEND;
26 20
27 - protected array $context_keys = [
28 - 'event' => [
29 - 'frontend' => self::CONTEXT_FRONTEND,
30 - 'preview' => self::CONTEXT_PREVIEW,
31 - ],
32 - 'meta_key' => [
33 - 'frontend' => self::META_KEY_FRONTEND,
34 - 'preview' => self::META_KEY_PREVIEW,
35 - ],
36 - ];
37 -
38 21 private ?Global_Classes $cache = null;
39 22
40 - public function __construct( ?Kit $kit = null ) {
41 - if ( null !== $kit ) {
42 - $this->set_kit( $kit );
43 - }
23 + public static function make(): Global_Classes_Repository {
24 + return new self();
44 25 }
45 26
46 - public static function make( ?Kit $kit = null ): Global_Classes_Repository {
47 - return new self( $kit );
48 - }
27 + public function context( string $context ): self {
28 + $this->context = $context;
29 + $this->cache = null;
49 30
50 - protected function on_preview_change(): void {
51 - $this->cache = null;
31 + return $this;
52 32 }
53 33
54 - /**
55 - * This method may be too heavy to use
56 - * Be mindful as this call would cause the server to freeze for as much time as needed until it fetches
57 - * all global classes
58 - */
59 34 public function all( bool $force = false ): Global_Classes {
60 35 if ( ! $force && null !== $this->cache ) {
61 36 return $this->cache;
62 37 }
63 38
64 - $this->cache = $this->all_from_posts();
39 + $meta_key = $this->get_meta_key();
40 + $kit = $this->get_kit();
41 + $all = $kit->get_json_meta( $meta_key );
65 42
66 - return $this->cache;
67 - }
43 + $is_preview = static::META_KEY_PREVIEW === $meta_key;
44 + $is_empty = empty( $all );
68 45
69 - public function all_labels(): array {
70 - return $this->labels()->get_ordered_labels();
71 - }
72 -
73 - public function get_order(): array {
74 - return Global_Classes_Order::make( $this->get_kit() )->get_order();
75 - }
76 -
77 - public function update_order_and_labels( array $order, array $new_labels ): void {
78 - Global_Classes_Order::make( $this->get_kit() )->set_order( $order );
79 -
80 - $labels = $this->labels();
81 - $existing_labels = $labels->get_labels();
82 -
83 - foreach ( $new_labels as $id => $label ) {
84 - $existing_labels[ $id ] = $label;
46 + if ( $is_preview && $is_empty ) {
47 + $all = $kit->get_json_meta( static::META_KEY_FRONTEND );
85 48 }
86 49
87 - $labels->set_labels( $existing_labels );
88 -
89 - $this->cache = null;
90 - }
91 -
92 - private function labels(): Global_Classes_Labels {
93 - return Global_Classes_Labels::make( $this->get_kit() )->set_preview( $this->is_preview() );
94 - }
95 -
96 - public function get( string $class_id ): ?array {
97 - $post = Global_Class_Post::find_by_class_id( $class_id, $this->is_preview() );
98 -
99 - return $post ? $post->to_array() : null;
100 - }
101 -
102 - public function get_by_ids( array $class_ids ): array {
103 - if ( empty( $class_ids ) ) {
104 - return [];
105 - }
106 -
107 - $items = [];
108 -
109 - foreach ( $this->iterate_class_posts_for_ids( $class_ids ) as $class_post ) {
110 - $class_data = $class_post->to_array();
111 - $items[ $class_data['id'] ] = $class_data;
112 - }
113 -
114 - return $items;
115 - }
116 -
117 - public function apply_changes( array $touched_items, array $changes, array $order ): void {
118 - $labels = $this->labels();
119 - $before = $labels->get_labels();
120 - $is_preview = $this->is_preview();
121 - $to_delete = $changes['deleted'] ?? [];
122 - $to_create = $changes['added'] ?? [];
123 - $to_update = $changes['modified'] ?? [];
124 - $order_changed = isset( $changes['order'] ) && $changes['order'];
125 -
126 - $final_label_map = [];
127 - foreach ( $order as $id ) {
128 - if ( isset( $touched_items[ $id ] ) ) {
129 - $final_label_map[ $id ] = $touched_items[ $id ]['label'];
130 - } elseif ( isset( $before[ $id ] ) ) {
131 - $final_label_map[ $id ] = $before[ $id ];
50 + Migrations_Orchestrator::make()->migrate(
51 + $all,
52 + $kit->get_id(),
53 + $meta_key,
54 + function( $migrated_data ) use ( $kit, $meta_key ) {
55 + $kit->update_json_meta( $meta_key, $migrated_data );
132 56 }
133 - }
57 + );
134 58
135 - $this->persist_class_batch_mutations( $to_delete, $to_create, $to_update, $touched_items, $is_preview );
59 + $this->cache = Global_Classes::make( $all['items'] ?? [], $all['order'] ?? [] );
136 60
137 - $classes_order = Global_Classes_Order::make( $this->get_kit() );
138 - $classes_order->set_order( $order );
139 - $labels->set_labels( $final_label_map );
140 -
141 - if ( ! $is_preview ) {
142 - Global_Classes_Sync_Map::make( $this->get_kit() )->apply_changes( $touched_items, $to_delete );
143 -
144 - $this->each_class_id_batch( array_values( $to_update ), function ( string $class_id ) {
145 - $post = Global_Class_Post::find_by_class_id( $class_id, false );
146 - if ( $post ) {
147 - delete_post_meta( $post->get_post_id(), Global_Class_Post::META_KEY_DATA_PREVIEW );
148 - clean_post_cache( $post->get_post_id() );
149 - }
150 - } );
151 - }
152 -
153 - $this->cache = null;
154 - $this->flush_runtime_cache();
155 -
156 - do_action( 'elementor/global_classes/update', $this->get_context_key( 'event' ), [
157 - 'added' => $to_create,
158 - 'deleted' => $to_delete,
159 - 'modified' => $to_update,
160 - 'order' => $order_changed,
161 - ] );
61 + return $this->cache;
162 62 }
163 63
164 - public function each_item( callable $cb, bool $skip_migration = false, int $batch_size = self::READ_BATCH_SIZE ): void {
165 - $order = Global_Classes_Order::make( $this->get_kit() )->get_order();
166 -
167 - if ( empty( $order ) ) {
168 - return;
169 - }
170 -
171 - foreach ( array_chunk( $order, $batch_size ) as $chunk ) {
172 - foreach ( $this->iterate_class_posts_for_ids( $chunk ) as $class_post ) {
173 - $cb( $class_post->to_array( $skip_migration ) );
174 - }
175 - }
176 - }
177 -
178 64 public function put( array $items, array $order ) {
179 - $current_ids = Global_Classes_Order::make( $this->get_kit() )->get_order();
65 + $current_value = $this->all()->get();
180 66
181 - $new_ids = array_keys( $items );
182 -
183 - $current_order_string = implode( ';', $current_ids );
184 -
185 - $changes = [
186 - 'added' => array_values( array_diff( $new_ids, $current_ids ) ),
187 - 'deleted' => array_values( array_diff( $current_ids, $new_ids ) ),
188 - 'modified' => array_values( array_intersect( $new_ids, $current_ids ) ),
189 - 'order' => implode( ';', $order ) !== $current_order_string,
67 + $updated_value = [
68 + 'items' => $items,
69 + 'order' => $order,
190 70 ];
191 71
192 - $this->put_to_posts( $items, $order, $current_ids );
193 -
194 - $this->cache = null;
195 -
196 - do_action( 'elementor/global_classes/update', $this->get_context_key( 'event' ), $changes );
197 - }
198 -
199 - private function all_from_posts(): Global_Classes {
200 - $classes_order = Global_Classes_Order::make( $this->get_kit() );
201 - $order = $classes_order->get_order();
202 -
203 - if ( empty( $order ) ) {
204 - return Global_Classes::make( [], [] );
72 + // `update_metadata` fails for identical data, so we skip it.
73 + if ( $current_value === $updated_value ) {
74 + return;
205 75 }
206 76
207 - $items = [];
77 + $meta_key = $this->get_meta_key();
78 + $value = $this->get_kit()->update_json_meta( $meta_key, $updated_value );
208 79
209 - foreach ( $this->iterate_class_posts_for_ids( $order ) as $class_post ) {
210 - $class_data = $class_post->to_array();
211 - $items[ $class_data['id'] ] = $class_data;
212 - }
80 + $should_delete_preview = static::META_KEY_FRONTEND === $meta_key;
213 81
214 - $order = Global_Classes_Parser::sanitize_order( $items, $order );
215 -
216 - return Global_Classes::make( $items, $order );
217 - }
218 -
219 - private function put_to_posts( array $items, array $order, array $current_ids ): void {
220 - $is_preview = $this->is_preview();
221 - $new_ids = array_keys( $items );
222 -
223 - $to_delete = array_diff( $current_ids, $new_ids );
224 - $to_create = array_diff( $new_ids, $current_ids );
225 - $to_update = array_intersect( $new_ids, $current_ids );
226 -
227 - $this->persist_class_batch_mutations( $to_delete, $to_create, $to_update, $items, $is_preview );
228 -
229 - $classes_order = Global_Classes_Order::make( $this->get_kit() );
230 - $classes_order->set_order( $order );
231 -
232 - $label_map = [];
233 - foreach ( $order as $id ) {
234 - if ( isset( $items[ $id ]['label'] ) ) {
235 - $label_map[ $id ] = $items[ $id ]['label'];
236 - }
82 + if ( $should_delete_preview ) {
83 + $this->get_kit()->delete_meta( static::META_KEY_PREVIEW );
237 84 }
238 - $this->labels()->set_labels( $label_map );
239 85
240 - if ( ! $is_preview ) {
241 - $touched_ids = array_merge( array_values( $to_create ), array_values( $to_update ) );
242 - $touched_items = array_intersect_key(
243 - $items,
244 - array_flip( $touched_ids )
245 - );
246 - Global_Classes_Sync_Map::make( $this->get_kit() )->apply_changes( $touched_items, array_values( $to_delete ) );
247 -
248 - $this->each_class_id_batch( array_values( $to_update ), function ( string $class_id ) {
249 - $post = Global_Class_Post::find_by_class_id( $class_id, false );
250 - if ( $post ) {
251 - delete_post_meta( $post->get_post_id(), Global_Class_Post::META_KEY_DATA_PREVIEW );
252 - clean_post_cache( $post->get_post_id() );
253 - }
254 - } );
86 + if ( ! $value ) {
87 + throw new \Exception( 'Failed to update global classes' );
255 88 }
256 - }
257 89
258 - private function iterate_class_posts_for_ids( array $class_ids ): \Generator {
259 - foreach ( array_chunk( $class_ids, self::READ_BATCH_SIZE ) as $chunk ) {
260 - $posts = get_posts( [
261 - 'post_type' => Global_Class_Post_Type::CPT,
262 - 'post_status' => 'publish',
263 - 'posts_per_page' => -1,
264 - 'meta_query' => [
265 - [
266 - 'key' => Global_Class_Post::META_KEY_ID,
267 - 'value' => $chunk,
268 - 'compare' => 'IN',
269 - ],
270 - ],
271 - ] );
90 + $this->cache = null;
272 91
273 - $seen_class_ids = [];
274 -
275 - foreach ( $posts as $post ) {
276 - $class_id = get_post_meta( $post->ID, Global_Class_Post::META_KEY_ID, true );
277 -
278 - if ( $class_id && isset( $seen_class_ids[ $class_id ] ) ) {
279 - // if for some reason there's already a post meta to that class - delete any other ones
280 - // otherwise we may end up updating a stale post meta
281 - wp_delete_post( $post->ID, true );
282 - continue;
283 - }
284 -
285 - if ( $class_id ) {
286 - $seen_class_ids[ $class_id ] = true;
287 - }
288 -
289 - yield Global_Class_Post::from_post( $post, $this->is_preview() );
290 - clean_post_cache( $post->ID );
291 - }
292 - unset( $posts, $seen_class_ids );
293 - $this->flush_runtime_cache();
294 - }
92 + do_action( 'elementor/global_classes/update', $this->context, $updated_value, $current_value );
295 93 }
296 94
297 - private function persist_class_batch_mutations(
298 - array $to_delete,
299 - array $to_create,
300 - array $to_update,
301 - array $items_by_id,
302 - bool $is_preview
303 - ): void {
304 - $relations = new Global_Classes_Relations();
305 -
306 - $this->each_class_id_batch( array_values( $to_delete ), function ( string $class_id ) use ( $is_preview, $relations ) {
307 - $post = Global_Class_Post::find_by_class_id( $class_id, false );
308 -
309 - if ( $post ) {
310 - if ( $is_preview ) {
311 - $post->set_preview( true );
312 - $post->update_data( [] );
313 - clean_post_cache( $post->get_post_id() );
314 - } else {
315 - $relations->clear_class_relations( $class_id );
316 - $post->delete();
317 - }
318 - }
319 - } );
320 -
321 - $this->each_class_id_batch( $to_create, function ( string $class_id ) use ( $items_by_id, $is_preview ) {
322 - if ( ! isset( $items_by_id[ $class_id ] ) ) {
323 - return;
324 - }
325 -
326 - $item = $items_by_id[ $class_id ];
327 - $data = Global_Class_Data_Normalizer::normalize_style_fields( $item );
328 -
329 - if ( $is_preview ) {
330 - $post = Global_Class_Post::find_by_class_id( $class_id, true );
331 -
332 - if ( $post ) {
333 - $post->set_preview( true );
334 - $post->update_data( $data );
335 - $post->update_label( $item['label'] );
336 - clean_post_cache( $post->get_post_id() );
337 - } else {
338 - $created = Global_Class_Post::create( $class_id, $item['label'], $data );
339 - if ( $created ) {
340 - clean_post_cache( $created->get_post_id() );
341 - }
342 - }
343 - } else {
344 - $created = Global_Class_Post::create( $class_id, $item['label'], $data );
345 - if ( $created ) {
346 - clean_post_cache( $created->get_post_id() );
347 - }
348 - }
349 - } );
350 -
351 - $this->each_class_id_batch( $to_update, function ( string $class_id ) use ( $items_by_id, $is_preview ) {
352 - if ( ! isset( $items_by_id[ $class_id ] ) ) {
353 - return;
354 - }
355 -
356 - $item = $items_by_id[ $class_id ];
357 - $post = Global_Class_Post::find_by_class_id( $class_id, $is_preview );
358 -
359 - if ( ! $post ) {
360 - return;
361 - }
362 -
363 - $data = Global_Class_Data_Normalizer::normalize_style_fields( $item );
364 - $post->update_data( $data );
365 - $post->update_label( $item['label'] );
366 - clean_post_cache( $post->get_post_id() );
367 - } );
95 + private function get_meta_key(): string {
96 + return static::CONTEXT_FRONTEND === $this->context
97 + ? static::META_KEY_FRONTEND
98 + : static::META_KEY_PREVIEW;
368 99 }
369 100
370 - public function delete_all(): void {
371 - $order = $this->get_order();
372 -
373 - $this->each_class_id_batch( $order, function ( string $class_id ) {
374 - $post = Global_Class_Post::find_by_class_id( $class_id );
375 -
376 - if ( $post ) {
377 - $post->delete();
378 - }
379 - } );
380 -
381 - Global_Classes_Order::make( $this->get_kit() )->set_order( [] );
382 - $this->labels()->set_labels( [] );
383 - }
384 -
385 - private function each_class_id_batch( $class_ids, callable $callback, int $batch_size = self::PERSIST_BATCH_SIZE ): void {
386 - $class_ids = is_array( $class_ids ) ? $class_ids : iterator_to_array( $class_ids, false );
387 - foreach ( array_chunk( array_values( $class_ids ), $batch_size ) as $batch ) {
388 - foreach ( $batch as $class_id ) {
389 - $callback( $class_id );
390 - }
391 - $this->flush_runtime_cache();
392 - }
393 - }
394 -
395 - private function flush_runtime_cache(): void {
396 - if ( function_exists( 'wp_cache_flush_runtime' ) ) {
397 - wp_cache_flush_runtime();
398 - }
101 + private function get_kit() {
102 + return Plugin::$instance->kits_manager->get_active_kit();
399 103 }
400 104 }