Passed
Pull Request — master (#377)
by Brian
05:01
created

GetPaid_Invoice_Data_Store::update()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 55
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 32
c 1
b 0
f 0
dl 0
loc 55
rs 8.7857
cc 6
nc 12
nop 1

How to fix   Long Method   

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_taxes',
27
		'_wpinv_fees',
28
		'_wpinv_discounts',
29
		'_wpinv_submission_id',
30
		'_wpinv_payment_form',
31
	);
32
33
	/**
34
	 * A map of meta keys to data props.
35
	 *
36
	 * @since 1.0.19
37
	 *
38
	 * @var array
39
	 */
40
	protected $meta_key_to_props = array(
41
		'_wpinv_subscr_profile_id' => 'subscription_id',
42
		'_wpinv_taxes'             => 'taxes',
43
		'_wpinv_fees'              => 'fees',
44
		'_wpinv_discounts'         => 'discounts',
45
		'_wpinv_submission_id'     => 'submission_id',
46
		'_wpinv_payment_form'      => 'payment_form',
47
	);
48
49
	/*
50
	|--------------------------------------------------------------------------
51
	| CRUD Methods
52
	|--------------------------------------------------------------------------
53
	*/
54
	/**
55
	 * Method to create a new invoice in the database.
56
	 *
57
	 * @param WPInv_Invoice $invoice Invoice object.
58
	 */
59
	public function create( &$invoice ) {
60
		$invoice->set_version( WPINV_VERSION );
61
		$invoice->set_date_created( current_time('mysql') );
62
63
		// Create a new post.
64
		$id = wp_insert_post(
65
			apply_filters(
66
				'getpaid_new_invoice_data',
67
				array(
68
					'post_date'     => $invoice->get_date_created( 'edit' ),
69
					'post_type'     => $invoice->get_post_type( 'edit' ),
70
					'post_status'   => $this->get_post_status( $invoice ),
71
					'ping_status'   => 'closed',
72
					'post_author'   => $invoice->get_user_id( 'edit' ),
73
					'post_title'    => $invoice->get_number( 'edit' ),
74
					'post_excerpt'  => $invoice->get_description( 'edit' ),
75
					'post_parent'   => $invoice->get_parent_id( 'edit' ),
76
					'post_name'     => $invoice->get_path( 'edit' ),
77
				)
78
			),
79
			true
80
		);
81
82
		if ( $id && ! is_wp_error( $id ) ) {
83
			$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

83
			$invoice->set_id( /** @scrutinizer ignore-type */ $id );
Loading history...
84
			$this->save_special_fields( $invoice );
85
			$this->update_post_meta( $invoice );
86
			$invoice->save_meta_data();
87
			$invoice->apply_changes();
88
			$this->clear_caches( $invoice );
89
			do_action( 'getpaid_new_' . $invoice->get_type(), $invoice->get_id(), $invoice );
90
			return true;
91
		}
92
93
		if ( is_wp_error( $id ) ) {
94
			$invoice->last_error = $id->get_error_message();
95
		}
96
97
		return false;
98
	}
99
100
	/**
101
	 * Method to read an invoice from the database.
102
	 *
103
	 * @param WPInv_Invoice $invoice Invoice object.
104
	 *
105
	 */
106
	public function read( &$invoice ) {
107
108
		$invoice->set_defaults();
109
		$invoice_object = get_post( $invoice->get_id() );
110
111
		if ( ! $invoice->get_id() || ! $invoice_object || getpaid_is_invoice_post_type( $invoice_object->post_type ) ) {
112
			$invoice->last_error = __( 'Invalid invoice.', 'invoicing' );
113
			return false;
114
		}
115
116
		$invoice->set_props(
117
			array(
118
				'date_created'  => 0 < $invoice_object->post_date ? $invoice_object->post_date : null,
119
				'date_modified' => 0 < $invoice_object->post_modified ? $invoice_object->post_modified : null,
120
				'status'        => $invoice_object->post_status,
121
				'author'        => $invoice_object->post_author,
122
				'description'   => $invoice_object->post_excerpt,
123
				'parent_id'     => $invoice_object->post_parent,
124
				'name'          => $invoice_object->post_title,
125
				'path'          => $invoice_object->post_name,
126
				'post_type'     => $invoice_object->post_type,
127
			)
128
		);
129
130
		$this->read_object_data( $invoice, $invoice_object );
131
		$this->add_special_fields( $invoice );
132
		$invoice->read_meta_data();
133
		$invoice->set_object_read( true );
134
		do_action( 'getpaid_read_' . $invoice->get_type(), $invoice->get_id(), $invoice );
135
136
	}
137
138
	/**
139
	 * Method to update an invoice in the database.
140
	 *
141
	 * @param WPInv_Invoice $invoice Invoice object.
142
	 */
143
	public function update( &$invoice ) {
144
		$invoice->save_meta_data();
145
		$invoice->set_version( WPINV_VERSION );
146
147
		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...
148
			$invoice->set_date_created(  current_time('mysql') );
149
		}
150
151
		// Grab the current status so we can compare.
152
		$previous_status = get_post_status( $invoice->get_id() );
153
154
		$changes = $invoice->get_changes();
155
156
		// Only update the post when the post data changes.
157
		if ( array_intersect( array( 'date_created', 'date_modified', 'status', 'name', 'author', 'description', 'parent_id', 'post_excerpt', 'path' ), array_keys( $changes ) ) ) {
158
			$post_data = array(
159
				'post_date'         => $invoice->get_date_created( 'edit' ),
160
				'post_status'       => $invoice->get_status( 'edit' ),
161
				'post_title'        => $invoice->get_name( 'edit' ),
162
				'post_author'       => $invoice->get_user_id( 'edit' ),
163
				'post_modified'     => $invoice->get_date_modified( 'edit' ),
164
				'post_excerpt'      => $invoice->get_description( 'edit' ),
165
				'post_parent'       => $invoice->get_parent_id( 'edit' ),
166
				'post_name'         => $invoice->get_path( 'edit' ),
167
				'post_type'         => $invoice->get_post_type( 'edit' ),
168
			);
169
170
			/**
171
			 * When updating this object, to prevent infinite loops, use $wpdb
172
			 * to update data, since wp_update_post spawns more calls to the
173
			 * save_post action.
174
			 *
175
			 * This ensures hooks are fired by either WP itself (admin screen save),
176
			 * or an update purely from CRUD.
177
			 */
178
			if ( doing_action( 'save_post' ) ) {
179
				$GLOBALS['wpdb']->update( $GLOBALS['wpdb']->posts, $post_data, array( 'ID' => $invoice->get_id() ) );
180
				clean_post_cache( $invoice->get_id() );
181
			} else {
182
				wp_update_post( array_merge( array( 'ID' => $invoice->get_id() ), $post_data ) );
183
			}
184
			$invoice->read_meta_data( true ); // Refresh internal meta data, in case things were hooked into `save_post` or another WP hook.
185
		}
186
		$this->update_post_meta( $invoice );
187
		$this->save_special_fields( $invoice );
188
		$invoice->apply_changes();
189
		$this->clear_caches( $invoice );
190
191
		// Fire a hook depending on the status - this should be considered a creation if it was previously draft status.
192
		$new_status = $invoice->get_status( 'edit' );
193
194
		if ( $new_status !== $previous_status && in_array( $previous_status, array( 'new', 'auto-draft', 'draft' ), true ) ) {
195
			do_action( 'getpaid_new_' . $invoice->get_type(), $invoice->get_id(), $invoice );
196
		} else {
197
			do_action( 'getpaid_update_' . $invoice->get_type(), $invoice->get_id(), $invoice );
198
		}
199
200
	}
201
202
	/*
203
	|--------------------------------------------------------------------------
204
	| Additional Methods
205
	|--------------------------------------------------------------------------
206
	*/
207
208
	/**
209
     * Retrieves special fields and adds to the invoice.
210
	 *
211
	 * @param WPInv_Invoice $invoice Invoice object.
212
     */
213
    public function add_special_fields( &$invoice ) {
214
		global $wpdb;
215
216
		$table =  $wpdb->prefix . 'getpaid_invoices';
217
        $data  = $wpdb->get_row(
218
			$wpdb->prepare( "SELECT * FROM $table WHERE `post_id`=%d LIMIT 1", $this->ID ),
0 ignored issues
show
Bug Best Practice introduced by
The property ID does not exist on GetPaid_Invoice_Data_Store. Did you maybe forget to declare it?
Loading history...
219
			ARRAY_A
220
		);
221
222
		// Abort if the data does not exist.
223
		if ( empty( $data ) ) {
224
			return;
225
		}
226
227
		$invoice->set_props( $data );
228
229
	}
230
231
	/**
232
	 * Gets a list of special fields that need updated based on change state
233
	 * or if they are present in the database or not.
234
	 *
235
	 * @param  WPInv_Invoice $invoice       The Invoice object.
236
	 * @param  array   $meta_key_to_props   A mapping of prop => value.
237
	 * @return array                        A mapping of field keys => prop names, filtered by ones that should be updated.
238
	 */
239
	protected function get_special_fields_to_update( $invoice, $special_fields ) {
240
		$props_to_update = array();
241
		$changed_props   = $invoice->get_changes();
242
243
		// Props should be updated if they are a part of the $changed array or don't exist yet.
244
		foreach ( $special_fields as $prop => $value ) {
245
			if ( array_key_exists( $prop, $changed_props ) ) {
246
				$props_to_update[ $prop ] = $value;
247
			}
248
		}
249
250
		return $props_to_update;
251
	}
252
253
	/**
254
     * Saves all special fields.
255
	 *
256
	 * @param WPInv_Invoice $invoice Invoice object.
257
     */
258
    public function save_special_fields( $invoice ) {
259
		global $wpdb;
260
261
		// Fields to update.
262
		$fields = array (
263
            'post_id'        => $invoice->get_id(),
264
            'number'         => $invoice->get_number( 'edit' ),
265
            'key'            => $invoice->get_key( 'edit' ),
266
            'type'           => $invoice->get_type( 'edit' ),
267
            'mode'           => $invoice->get_mode( 'edit' ),
268
            'user_ip'        => $invoice->get_user_ip( 'edit' ),
269
            'first_name'     => $invoice->get_first_name( 'edit' ),
270
            'last_name'      => $invoice->get_last_name( 'edit' ),
271
            'address'        => $invoice->get_address( 'edit' ),
272
            'city'           => $invoice->get_city( 'edit' ),
273
            'state'          => $invoice->get_state( 'edit' ),
274
            'country'        => $invoice->get_country( 'edit' ),
275
            'zip'            => $invoice->get_zip( 'edit' ),
276
            'address_confirmed' => (int) $invoice->get_address_confirmed( 'edit' ),
277
            'gateway'        => $invoice->get_gateway( 'edit' ),
278
            'transaction_id' => $invoice->get_transaction_id( 'edit' ),
279
            'currency'       => $invoice->get_currency( 'edit' ),
280
            'subtotal'       => $invoice->get_subtotal( 'edit' ),
281
            'total_tax'      => $invoice->get_total_tax( 'edit' ),
282
            'total_fees'     => $invoice->get_total_fees( 'edit' ),
283
            'total_discount' => $invoice->get_total_discount( 'edit' ),
284
            'discount_code'  => $invoice->get_discount_code( 'edit' ),
285
            'disable_taxes'  => (int) $invoice->get_disable_taxes( 'edit' ),
286
            'due_date'       => $invoice->get_due_date( 'edit' ),
287
            'completed_date' => $invoice->get_completed_date( 'edit' ),
288
            'company'        => $invoice->get_company( 'edit' ),
289
            'vat_number'     => $invoice->get_vat_number( 'edit' ),
290
            'vat_rate'       => $invoice->get_vat_rate( 'edit' ),
291
		);
292
293
		// The invoices table.
294
		$table = $wpdb->prefix . 'getpaid_invoices';
295
		$id    = (int) $invoice->get_id();
296
		if ( $wpdb->get_var( "SELECT `post_id` FROM $table WHERE `post_id`= $id" ) ) {
297
298
			$to_update = $this->get_special_fields_to_update( $invoice, $fields );
299
300
			if ( empty( $to_update ) ) {
301
				return;
302
			}
303
304
			$changes = array(
305
				'tax'                => 'total_tax',
306
				'fees_total'         => 'total_fees',
307
				'discount'           => 'total_discount',
308
				'adddress_confirmed' => 'address_confirmed',
309
			);
310
311
			foreach ( $changes as $to => $from ) {
312
				if ( isset( $changes[ $from ] ) ) {
313
					$changes[ $to ] = $changes[ $from ];
314
					unset( $changes[ $from ] );
315
				}
316
			}
317
318
			$changes['total'] = $invoice->get_total( 'edit' );
0 ignored issues
show
Unused Code introduced by
The call to WPInv_Invoice::get_total() has too many arguments starting with 'edit'. ( Ignorable by Annotation )

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

318
			/** @scrutinizer ignore-call */ 
319
   $changes['total'] = $invoice->get_total( 'edit' );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
319
            $wpdb->update( $table, $fields, array( 'post_id' => $id ) );
320
321
        } else {
322
323
			$fields['tax'] = $fields['total_tax'];
324
			unset( $fields['total_tax'] );
325
326
			$fields['fees_total'] = $fields['total_fees'];
327
			unset( $fields['total_fees'] );
328
329
			$fields['discount'] = $fields['total_discount'];
330
			unset( $fields['total_discount'] );
331
332
			$fields['adddress_confirmed'] = $fields['address_confirmed'];
333
			unset( $fields['address_confirmed'] );
334
			
335
			$fields['total']   = $invoice->get_total( 'edit' );
336
			$fields['post_id'] = $id;
337
            $wpdb->insert( $table, $fields );
338
339
		}
340
341
	}
342
343
	/**
344
     * Set's up cart details.
345
	 *
346
	 * @param WPInv_Invoice $invoice Invoice object.
347
     */
348
    public function add_items( &$invoice ) {
349
		global $wpdb;
350
351
		$table =  $wpdb->prefix . 'getpaid_invoice_items';
352
        $items = $wpdb->get_results(
353
            $wpdb->prepare( "SELECT * FROM $table WHERE `post_id`=%d", $this->ID )
0 ignored issues
show
Bug Best Practice introduced by
The property ID does not exist on GetPaid_Invoice_Data_Store. Did you maybe forget to declare it?
Loading history...
354
        );
355
356
        if ( empty( $items ) ) {
357
            return;
358
		}
359
360
		foreach ( $items as $item_data ) {
361
			$item = new GetPaid_Form_Item( $item_data->item_id );
362
363
			// Set item data.
364
			$item->item_tax      = wpinv_sanitize_amount( $item_data->tax );
0 ignored issues
show
Documentation Bug introduced by
It seems like wpinv_sanitize_amount($item_data->tax) can also be of type string. However, the property $item_tax is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
365
			$item->item_discount = wpinv_sanitize_amount( $item_data->tax );
0 ignored issues
show
Documentation Bug introduced by
It seems like wpinv_sanitize_amount($item_data->tax) can also be of type string. However, the property $item_discount is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
366
			$item->set_name( $item_data->item_name );
367
			$item->set_description( $item_data->item_description );
368
			$item->set_price( $item_data->item_price );
369
			$item->set_quantity( $item_data->quantity );
370
371
			$invoice->add_item( $item );
372
		}
373
374
	}
375
376
	/**
377
     * Saves cart details.
378
	 *
379
	 * @param WPInv_Invoice $invoice Invoice object.
380
     */
381
    public function save_items( $invoice ) {
382
383
		// Delete previously existing items.
384
		$this->delete_items( $invoice );
385
386
		$table   =  $GLOBALS['wpdb']->prefix . 'getpaid_invoice_items';
387
        $to_save = $invoice->get_cart_details();
388
389
		foreach ( $to_save as $item_data ) {
390
			$GLOBALS['wpdb']->insert( $table, $item_data );
391
		}
392
393
	}
394
395
	/**
396
     * Deletes an invoice's cart details from the database.
397
	 *
398
	 * @param WPInv_Invoice $invoice Invoice object.
399
     */
400
    public function delete_items( $invoice ) {
401
		$table =  $GLOBALS['wpdb']->prefix . 'getpaid_invoice_items';
402
		return $GLOBALS['wpdb']->delete( $table, array( 'post_id' => $invoice->ID ) );
403
    }
404
405
}
406