Passed
Pull Request — master (#785)
by
unknown
04:48
created

GetPaid_Invoice_Data_Store::add_items()   C

Complexity

Conditions 14
Paths 22

Size

Total Lines 74
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 42
nc 22
nop 1
dl 0
loc 74
rs 6.2666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * GetPaid_Invoice_Data_Store class file.
5
 *
6
 */
7
if ( ! defined( 'ABSPATH' ) ) {
8
	exit;
9
}
10
11
/**
12
 * Invoice Data Store: Stored in CPT.
13
 *
14
 * @version  1.0.19
15
 */
16
class GetPaid_Invoice_Data_Store extends GetPaid_Data_Store_WP {
17
18
	/**
19
	 * Data stored in meta keys, but not considered "meta" for a discount.
20
	 *
21
	 * @since 1.0.19
22
	 * @var array
23
	 */
24
	protected $internal_meta_keys = array(
25
		'_wpinv_subscr_profile_id',
26
		'_wpinv_subscription_id',
27
		'_wpinv_taxes',
28
		'_wpinv_fees',
29
		'_wpinv_discounts',
30
		'_wpinv_submission_id',
31
		'_wpinv_payment_form',
32
		'_wpinv_is_viewed',
33
		'_wpinv_phone',
34
		'_wpinv_company_id',
35
		'wpinv_shipping',
36
		'wpinv_email_cc',
37
		'wpinv_template',
38
		'wpinv_created_via',
39
	);
40
41
	/**
42
	 * A map of meta keys to data props.
43
	 *
44
	 * @since 1.0.19
45
	 *
46
	 * @var array
47
	 */
48
	protected $meta_key_to_props = array(
49
		'_wpinv_subscr_profile_id' => 'remote_subscription_id',
50
		'_wpinv_subscription_id'   => 'subscription_id',
51
		'_wpinv_taxes'             => 'taxes',
52
		'_wpinv_fees'              => 'fees',
53
		'_wpinv_discounts'         => 'discounts',
54
		'_wpinv_submission_id'     => 'submission_id',
55
		'_wpinv_payment_form'      => 'payment_form',
56
		'_wpinv_is_viewed'         => 'is_viewed',
57
		'wpinv_email_cc'           => 'email_cc',
58
		'wpinv_template'           => 'template',
59
		'wpinv_created_via'        => 'created_via',
60
		'_wpinv_phone'             => 'phone',
61
		'_wpinv_company_id'        => 'company_id',
62
		'wpinv_shipping'           => 'shipping',
63
	);
64
65
	/**
66
	 * A map of database fields to data props.
67
	 *
68
	 * @since 1.0.19
69
	 *
70
	 * @var array
71
	 */
72
	protected $database_fields_to_props = array(
73
		'post_id'            => 'id',
74
		'number'             => 'number',
75
		'currency'           => 'currency',
76
		'invoice_key'        => 'key',
77
		'type'               => 'type',
78
		'mode'               => 'mode',
79
		'user_ip'            => 'user_ip',
80
		'first_name'         => 'first_name',
81
		'last_name'          => 'last_name',
82
		'address'            => 'address',
83
		'city'               => 'city',
84
		'state'              => 'state',
85
		'country'            => 'country',
86
		'zip'                => 'zip',
87
		'zip'                => 'zip',
88
		'adddress_confirmed' => 'address_confirmed',
89
		'gateway'            => 'gateway',
90
		'transaction_id'     => 'transaction_id',
91
		'currency'           => 'currency',
92
		'subtotal'           => 'subtotal',
93
		'tax'                => 'total_tax',
94
		'fees_total'         => 'total_fees',
95
		'discount'           => 'total_discount',
96
		'total'              => 'total',
97
		'discount_code'      => 'discount_code',
98
		'disable_taxes'      => 'disable_taxes',
99
		'due_date'           => 'due_date',
100
		'completed_date'     => 'completed_date',
101
		'company'            => 'company',
102
		'vat_number'         => 'vat_number',
103
		'vat_rate'           => 'vat_rate',
104
		'customer_id'        => 'customer_id',
105
	);
106
107
	/*
108
	|--------------------------------------------------------------------------
109
	| CRUD Methods
110
	|--------------------------------------------------------------------------
111
	*/
112
113
	/**
114
	 * Method to create a new invoice in the database.
115
	 *
116
	 * @param WPInv_Invoice $invoice Invoice object.
117
	 */
118
	public function create( &$invoice ) {
119
		$invoice->set_version( WPINV_VERSION );
120
		$invoice->set_date_created( current_time( 'mysql' ) );
121
122
		// Create a new post.
123
		$id = wp_insert_post(
124
			apply_filters(
125
				'getpaid_new_invoice_data',
126
				array(
127
					'post_date'    => $invoice->get_date_created( 'edit' ),
128
					'post_type'    => $invoice->get_post_type( 'edit' ),
129
					'post_status'  => $this->get_post_status( $invoice ),
130
					'ping_status'  => 'closed',
131
					'post_author'  => $invoice->get_user_id( 'edit' ),
132
					'post_title'   => $invoice->get_title( 'edit' ),
133
					'post_excerpt' => $invoice->get_description( 'edit' ),
134
					'post_parent'  => $invoice->get_parent_id( 'edit' ),
135
				)
136
			),
137
			true
138
		);
139
140
		if ( $id && ! is_wp_error( $id ) ) {
141
142
			// Update the new id and regenerate a title.
143
			$invoice->set_id( $id );
0 ignored issues
show
Bug introduced by
It seems like $id can also be of type WP_Error; however, parameter $id of GetPaid_Data::set_id() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
			$invoice->set_id( /** @scrutinizer ignore-type */ $id );
Loading history...
144
145
			$invoice->maybe_set_number();
146
147
			wp_update_post(
148
				array(
149
					'ID'         => $invoice->get_id(),
150
					'post_title' => $invoice->get_number( 'edit' ),
151
					'post_name'  => $invoice->get_path( 'edit' ),
152
				)
153
			);
154
155
			// Save special fields and items.
156
			$this->save_special_fields( $invoice );
157
			$this->save_items( $invoice );
158
159
			// Update meta data.
160
			$this->update_post_meta( $invoice );
161
			$invoice->save_meta_data();
162
163
			// Apply changes.
164
			$invoice->apply_changes();
165
			$this->clear_caches( $invoice );
166
167
			// Fires after a new invoice is created.
168
			do_action( 'getpaid_new_invoice', $invoice );
169
			return true;
170
		}
171
172
		if ( is_wp_error( $id ) ) {
173
			$invoice->last_error = $id->get_error_message();
174
		}
175
176
		return false;
177
	}
178
179
	/**
180
	 * Method to read an invoice from the database.
181
	 *
182
	 * @param WPInv_Invoice $invoice Invoice object.
183
	 *
184
	 */
185
	public function read( &$invoice ) {
186
187
		$invoice->set_defaults();
188
		$invoice_object = get_post( $invoice->get_id() );
189
190
		if ( ! $invoice->get_id() || ! $invoice_object || ! getpaid_is_invoice_post_type( $invoice_object->post_type ) ) {
191
			$invoice->last_error = __( 'Invalid invoice.', 'invoicing' );
192
			$invoice->set_id( 0 );
193
			return false;
194
		}
195
196
		$invoice->set_props(
197
			array(
198
				'date_created'  => 0 < $invoice_object->post_date ? $invoice_object->post_date : null,
199
				'date_modified' => 0 < $invoice_object->post_modified ? $invoice_object->post_modified : null,
200
				'status'        => $invoice_object->post_status,
201
				'author'        => $invoice_object->post_author,
202
				'description'   => $invoice_object->post_excerpt,
203
				'parent_id'     => $invoice_object->post_parent,
204
				'name'          => $invoice_object->post_title,
205
				'path'          => $invoice_object->post_name,
206
				'post_type'     => $invoice_object->post_type,
207
			)
208
		);
209
210
		$invoice->set_type( $invoice_object->post_type );
211
212
		$this->read_object_data( $invoice, $invoice_object );
0 ignored issues
show
Bug introduced by
It seems like $invoice_object can also be of type array; however, parameter $post_object of GetPaid_Data_Store_WP::read_object_data() does only seem to accept WP_Post, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

212
		$this->read_object_data( $invoice, /** @scrutinizer ignore-type */ $invoice_object );
Loading history...
213
		$this->add_special_fields( $invoice );
214
		$this->add_items( $invoice );
215
		$invoice->read_meta_data();
216
		$invoice->set_object_read( true );
217
		do_action( 'getpaid_read_invoice', $invoice );
218
	}
219
220
	/**
221
	 * Method to update an invoice in the database.
222
	 *
223
	 * @param WPInv_Invoice $invoice Invoice object.
224
	 */
225
	public function update( &$invoice ) {
226
		$invoice->save_meta_data();
227
		$invoice->set_version( WPINV_VERSION );
228
229
		if ( null === $invoice->get_date_created( 'edit' ) ) {
0 ignored issues
show
introduced by
The condition null === $invoice->get_date_created('edit') is always false.
Loading history...
230
			$invoice->set_date_created( current_time( 'mysql' ) );
231
		}
232
233
		// Ensure both the key and number are set.
234
		$invoice->get_path();
235
236
		// Grab the current status so we can compare.
237
		$previous_status = get_post_status( $invoice->get_id() );
238
239
		$changes = $invoice->get_changes();
240
241
		// Only update the post when the post data changes.
242
		if ( array_intersect( array( 'date_created', 'date_modified', 'status', 'name', 'author', 'description', 'parent_id', 'post_excerpt', 'path' ), array_keys( $changes ) ) ) {
243
			$post_data = array(
244
				'post_date'     => $invoice->get_date_created( 'edit' ),
245
				'post_date_gmt' => $invoice->get_date_created_gmt( 'edit' ),
246
				'post_status'   => $invoice->get_status( 'edit' ),
247
				'post_title'    => $invoice->get_name( 'edit' ),
248
				'post_author'   => $invoice->get_user_id( 'edit' ),
249
				'post_modified' => $invoice->get_date_modified( 'edit' ),
250
				'post_excerpt'  => $invoice->get_description( 'edit' ),
251
				'post_parent'   => $invoice->get_parent_id( 'edit' ),
252
				'post_name'     => $invoice->get_path( 'edit' ),
253
				'post_type'     => $invoice->get_post_type( 'edit' ),
254
			);
255
256
			/**
257
			 * When updating this object, to prevent infinite loops, use $wpdb
258
			 * to update data, since wp_update_post spawns more calls to the
259
			 * save_post action.
260
			 *
261
			 * This ensures hooks are fired by either WP itself (admin screen save),
262
			 * or an update purely from CRUD.
263
			 */
264
			if ( doing_action( 'save_post' ) ) {
265
				$GLOBALS['wpdb']->update( $GLOBALS['wpdb']->posts, $post_data, array( 'ID' => $invoice->get_id() ) );
266
				clean_post_cache( $invoice->get_id() );
267
			} else {
268
				wp_update_post( array_merge( array( 'ID' => $invoice->get_id() ), $post_data ) );
269
			}
270
			$invoice->read_meta_data( true ); // Refresh internal meta data, in case things were hooked into `save_post` or another WP hook.
271
		}
272
273
		// Update meta data.
274
		$this->update_post_meta( $invoice );
275
276
		// Save special fields and items.
277
		$this->save_special_fields( $invoice );
278
		$this->save_items( $invoice );
279
280
		// Apply the changes.
281
		$invoice->apply_changes();
282
283
		// Clear caches.
284
		$this->clear_caches( $invoice );
285
286
		// Fire a hook depending on the status - this should be considered a creation if it was previously draft status.
287
		$new_status = $invoice->get_status( 'edit' );
288
289
		if ( $new_status !== $previous_status && in_array( $previous_status, array( 'new', 'auto-draft', 'draft' ), true ) ) {
290
			do_action( 'getpaid_new_invoice', $invoice );
291
		} else {
292
			do_action( 'getpaid_update_invoice', $invoice );
293
		}
294
	}
295
296
	/*
297
	|--------------------------------------------------------------------------
298
	| Additional Methods
299
	|--------------------------------------------------------------------------
300
	*/
301
302
	/**
303
     * Retrieves special fields and adds to the invoice.
304
	 *
305
	 * @param WPInv_Invoice $invoice Invoice object.
306
     */
307
    public function add_special_fields( &$invoice ) {
308
		global $wpdb;
309
310
		// Maybe retrieve from the cache.
311
		$data   = wp_cache_get( $invoice->get_id(), 'getpaid_invoice_special_fields' );
312
313
		// If not found, retrieve from the db.
314
		if ( false === $data ) {
315
			$table = $wpdb->prefix . 'getpaid_invoices';
316
317
			$data  = $wpdb->get_row(
318
				$wpdb->prepare( "SELECT * FROM $table WHERE `post_id`=%d LIMIT 1", $invoice->get_id() ),
319
				ARRAY_A
320
			);
321
322
			// Update the cache with our data
323
			wp_cache_set( $invoice->get_id(), $data, 'getpaid_invoice_special_fields' );
324
325
		}
326
327
		// Abort if the data does not exist.
328
		if ( empty( $data ) ) {
329
			$invoice->set_object_read( true );
330
			$invoice->set_props( wpinv_get_user_address( $invoice->get_user_id() ) );
331
			return;
332
		}
333
334
		$props = array();
335
336
		foreach ( $this->database_fields_to_props as $db_field => $prop ) {
337
338
			if ( $db_field == 'post_id' ) {
339
				continue;
340
			}
341
342
			$props[ $prop ] = $data[ $db_field ];
343
		}
344
345
		$invoice->set_props( $props );
346
	}
347
348
	/**
349
	 * Gets a list of special fields that need updated based on change state
350
	 * or if they are present in the database or not.
351
	 *
352
	 * @param  WPInv_Invoice $invoice       The Invoice object.
353
	 * @return array                        A mapping of field keys => prop names, filtered by ones that should be updated.
354
	 */
355
	protected function get_special_fields_to_update( $invoice ) {
356
		$fields_to_update = array();
357
		$changed_props   = $invoice->get_changes();
358
359
		// Props should be updated if they are a part of the $changed array or don't exist yet.
360
		foreach ( $this->database_fields_to_props as $database_field => $prop ) {
361
			if ( array_key_exists( $prop, $changed_props ) ) {
362
				$fields_to_update[ $database_field ] = $prop;
363
			}
364
		}
365
366
		return $fields_to_update;
367
	}
368
369
	/**
370
	 * Helper method that updates all the database fields for an invoice based on it's settings in the WPInv_Invoice class.
371
	 *
372
	 * @param WPInv_Invoice $invoice WPInv_Invoice object.
373
	 * @since 1.0.19
374
	 */
375
	protected function update_special_fields( &$invoice ) {
376
		global $wpdb;
377
378
		$updated_props    = array();
379
		$fields_to_update = $this->get_special_fields_to_update( $invoice );
380
381
		foreach ( $fields_to_update as $database_field => $prop ) {
382
			$value = $invoice->{"get_$prop"}( 'edit' );
383
			$value = is_string( $value ) ? wp_slash( $value ) : $value;
384
			$value = is_bool( $value ) ? (int) $value : $value;
385
			$updated_props[ $database_field ] = maybe_serialize( $value );
386
		}
387
388
		if ( ! empty( $updated_props ) ) {
389
390
			$table = $wpdb->prefix . 'getpaid_invoices';
391
			$wpdb->update( $table, $updated_props, array( 'post_id' => $invoice->get_id() ) );
392
			wp_cache_delete( $invoice->get_id(), 'getpaid_invoice_special_fields' );
393
			do_action( 'getpaid_invoice_update_database_fields', $invoice, $updated_props );
394
395
		}
396
	}
397
398
	/**
399
	 * Helper method that inserts special fields to the database.
400
	 *
401
	 * @param WPInv_Invoice $invoice WPInv_Invoice object.
402
	 * @since 1.0.19
403
	 */
404
	protected function insert_special_fields( &$invoice ) {
405
		global $wpdb;
406
407
		$updated_props   = array();
408
409
		foreach ( $this->database_fields_to_props as $database_field => $prop ) {
410
			$value = $invoice->{"get_$prop"}( 'edit' );
411
			$value = is_string( $value ) ? wp_slash( $value ) : $value;
412
			$value = is_bool( $value ) ? (int) $value : $value;
413
			$updated_props[ $database_field ] = maybe_serialize( $value );
414
		}
415
416
		$table = $wpdb->prefix . 'getpaid_invoices';
417
		$wpdb->insert( $table, $updated_props );
418
		wp_cache_delete( $invoice->get_id(), 'getpaid_invoice_special_fields' );
419
		do_action( 'getpaid_invoice_insert_database_fields', $invoice, $updated_props );
420
	}
421
422
	/**
423
     * Saves all special fields.
424
	 *
425
	 * @param WPInv_Invoice $invoice Invoice object.
426
     */
427
    public function save_special_fields( &$invoice ) {
428
		global $wpdb;
429
430
		// The invoices table.
431
		$table = $wpdb->prefix . 'getpaid_invoices';
432
		$id    = (int) $invoice->get_id();
433
		$invoice->maybe_set_key();
434
435
		if ( $wpdb->get_var( "SELECT `post_id` FROM $table WHERE `post_id`= $id" ) ) {
436
437
			$this->update_special_fields( $invoice );
438
439
		} else {
440
441
			$this->insert_special_fields( $invoice );
442
443
		}
444
	}
445
446
	/**
447
     * Set's up cart details.
448
	 *
449
	 * @param WPInv_Invoice $invoice Invoice object.
450
     */
451
    public function add_items( &$invoice ) {
452
		global $wpdb;
453
454
		// Maybe retrieve from the cache.
455
		$items = wp_cache_get( $invoice->get_id(), 'getpaid_invoice_cart_details' );
456
457
		// If not found, retrieve from the db.
458
		if ( false === $items ) {
459
			$table = $wpdb->prefix . 'getpaid_invoice_items';
460
461
			$items = $wpdb->get_results(
462
				$wpdb->prepare( "SELECT * FROM $table WHERE `post_id`=%d", $invoice->get_id() )
463
			);
464
465
			// Update the cache with our data
466
			wp_cache_set( $invoice->get_id(), $items, 'getpaid_invoice_cart_details' );
467
468
		}
469
470
		// Abort if no items found.
471
        if ( empty( $items ) ) {
472
            return;
473
		}
474
475
		$_items = array();
476
		foreach ( $items as $item_data ) {
477
			$item = new GetPaid_Form_Item( $item_data->item_id );
478
479
			// Set item data.
480
			$item->item_tax      = wpinv_sanitize_amount( $item_data->tax );
481
			$item->item_discount = wpinv_sanitize_amount( $item_data->discount );
482
			$item->set_name( $item_data->item_name );
483
			$item->set_description( $item_data->item_description );
484
            $item->set_price( $item_data->item_price );
485
			$item->set_quantity( $item_data->quantity );
486
			$item->set_price_id( $item_data->price_id );
487
			$item->set_item_meta( $item_data->meta );
488
489
            if ( $item->has_variable_pricing() ) {
490
                $price_options = $item->get_variable_prices();
491
492
                if ( ! empty( $price_options ) && isset( $price_options[ $item_data->price_id ] ) ) {
493
                    $price = $price_options[ $item_data->price_id ];
494
495
                    $item->set_price( (float) $price['amount'] );
496
497
                    if ( isset( $price['is-recurring'] ) && 'yes' === $price['is-recurring'] ) {
498
                        if ( isset( $price['trial-interval'], $price['trial-period'] ) && $price['trial-interval'] > 0 ) {
499
                            $trial_interval = (int) $price['trial-interval'];
500
                            $trial_period = $price['trial-period'];
501
502
                            $item->set_is_free_trial( 1 );
503
                            $item->set_trial_interval( $trial_interval );
504
                            $item->set_trial_period( $trial_period );
505
                        }
506
507
                        if ( isset( $price['recurring-interval'], $price['recurring-period'] ) && $price['recurring-interval'] > 0 ) {
508
                            $recurring_interval = (int) $price['recurring-interval'];
509
                            $recurring_period = $price['recurring-period'];
510
                            $recurring_limit = isset( $price['recurring-limit'] ) ? (int) $price['recurring-limit'] : 0;
511
512
                            $item->set_is_recurring( 1 );
513
                            $item->set_recurring_interval( $recurring_interval );
514
                            $item->set_recurring_period( $recurring_period );
515
                            $item->set_recurring_limit( $recurring_limit );
516
                        }
517
                    }
518
                }
519
            }
520
521
			$_items[] = $item;
522
		}
523
524
		$invoice->set_items( $_items );
525
	}
526
527
	/**
528
     * Saves cart details.
529
	 *
530
	 * @param WPInv_Invoice $invoice Invoice object.
531
     */
532
    public function save_items( $invoice ) {
533
534
		// Delete previously existing items.
535
		$this->delete_items( $invoice );
536
537
		$table   = $GLOBALS['wpdb']->prefix . 'getpaid_invoice_items';
538
539
		foreach ( $invoice->get_cart_details() as $item_data ) {
540
			$item_data = array_map( 'maybe_serialize', $item_data );
541
			$GLOBALS['wpdb']->insert( $table, $item_data );
542
		}
543
544
		wp_cache_delete( $invoice->get_id(), 'getpaid_invoice_cart_details' );
545
		do_action( 'getpaid_invoice_save_items', $invoice );
546
	}
547
548
	/**
549
     * Deletes an invoice's cart details from the database.
550
	 *
551
	 * @param WPInv_Invoice $invoice Invoice object.
552
     */
553
    public function delete_items( $invoice ) {
554
		$table = $GLOBALS['wpdb']->prefix . 'getpaid_invoice_items';
555
		return $GLOBALS['wpdb']->delete( $table, array( 'post_id' => $invoice->get_id() ) );
556
	}
557
558
	/**
559
     * Deletes an invoice's special fields from the database.
560
	 *
561
	 * @param WPInv_Invoice $invoice Invoice object.
562
     */
563
    public function delete_special_fields( $invoice ) {
564
		$table = $GLOBALS['wpdb']->prefix . 'getpaid_invoices';
565
		return $GLOBALS['wpdb']->delete( $table, array( 'post_id' => $invoice->get_id() ) );
566
	}
567
568
	/**
569
	 * Get the status to save to the post object.
570
	 *
571
	 *
572
	 * @since 1.0.19
573
	 * @param  WPInv_Invoice $object GetPaid_Data object.
574
	 * @return string
575
	 */
576
	protected function get_post_status( $object ) {
577
		$object_status = $object->get_status( 'edit' );
578
579
		if ( ! $object_status ) {
580
			$object_status = $object->get_default_status();
581
		}
582
583
		return $object_status;
584
	}
585
}
586