Completed
Branch BETA-4.9-message-activity (c2a8e0)
by
unknown
18:28 queued 10s
created

EE_Transaction::_get_lock()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php if ( ! defined( 'EVENT_ESPRESSO_VERSION' ) ) {
2
	exit( 'No direct script access allowed' );
3
}
4
/**
5
 * EE_Transaction class
6
 *
7
 * @package     Event Espresso
8
 * @subpackage 	includes/classes/EE_Transaction.class.php
9
 * @author      Brent Christensen
10
 */
11
class EE_Transaction extends EE_Base_Class implements EEI_Transaction {
12
13
	/**
14
	 * The length of time in seconds that a lock is applied before being considered expired.
15
	 * It is not long because a transaction should only be locked for the duration of the request that locked it
16
	 */
17
	const LOCK_EXPIRATION = 2;
18
19
	/**
20
	 * @param array  $props_n_values          incoming values
21
	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
22
	 *                                        used.)
23
	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
24
	 *                                        date_format and the second value is the time format
25
	 * @return EE_Transaction
26
	 * @throws \EE_Error
27
	 */
28
	public static function new_instance( $props_n_values = array(), $timezone = null, $date_formats = array() ) {
29
		$has_object = parent::_check_for_object( $props_n_values, __CLASS__, $timezone, $date_formats );
0 ignored issues
show
Bug introduced by
It seems like $timezone defined by parameter $timezone on line 28 can also be of type string; however, EE_Base_Class::_check_for_object() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (_check_for_object() instead of new_instance()). Are you sure this is correct? If so, you might want to change this to $this->_check_for_object().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
30
		return $has_object
31
			? $has_object
32
			: new self( $props_n_values, false, $timezone, $date_formats );
33
	}
34
35
36
37
	/**
38
	 * @param array  $props_n_values  incoming values from the database
39
	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
40
	 *                                the website will be used.
41
	 * @return EE_Transaction
42
	 * @throws \EE_Error
43
	 */
44
	public static function new_instance_from_db( $props_n_values = array(), $timezone = null ) {
45
		return new self( $props_n_values, TRUE, $timezone );
46
	}
47
48
49
50
	/**
51
	 * lock
52
	 * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
53
	 * If a lock has already been set, then we will attempt to remove it in case it has expired.
54
	 * If that also fails, then an exception is thrown.
55
	 *
56
	 * @access public
57
	 * @throws \EE_Error
58
	 */
59
	public function lock() {
60
		// attempt to set lock, but if that fails...
61
		if ( ! $this->add_extra_meta( 'lock', time(), true )  ) {
62
			// then attempt to remove the lock in case it is expired
63
			if ( $this->_remove_expired_lock() ) {
64
				// if removal was successful, then try setting lock again
65
				$this->lock();
66
			} else {
67
				// but if the lock can not be removed, then throw an exception
68
				throw new EE_Error(
69
					sprintf(
70
						__( 'Could not lock Transaction %1$d because it is already locked, meaning another part of the system is currently editing it. It should already be unlocked by the time you read this, so please refresh the page and try again.', 'event_espresso' ),
71
						$this->ID()
72
					)
73
				);
74
			}
75
		}
76
	}
77
78
79
80
	/**
81
	 * unlock
82
	 * removes transaction lock applied in EE_Transaction::lock()
83
	 *
84
	 * @access public
85
	 * @return int
86
	 * @throws \EE_Error
87
	 */
88
	public function unlock() {
89
		return $this->delete_extra_meta( 'lock' );
90
	}
91
92
93
94
	/**
95
	 * is_locked
96
	 * Decides whether or not now is the right time to update the transaction.
97
	 * This is useful because we don't always know if it is safe to update the transaction
98
	 * and its related data. why?
99
	 * because it's possible that the transaction is being used in another
100
	 * request and could overwrite anything we save.
101
	 * So we want to only update the txn once we know that won't happen.
102
	 * We also check that the lock isn't expired, and remove it if it is
103
	 *
104
	 * @access public
105
	 * @return boolean
106
	 * @throws \EE_Error
107
	 */
108
	public function is_locked() {
109
		// if TXN is not locked, then return false immediately
110
		if ( ! $this->_get_lock() ) {
111
			return false;
112
		}
113
		// if not, then let's try and remove the lock in case it's expired...
114
		// _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
115
		// and a positive number if the lock was removed (ie: number of locks deleted),
116
		// so we need to return the opposite
117
		return ! $this->_remove_expired_lock() ? true : false;
118
	}
119
120
121
122
	/**
123
	 * _get_lock
124
	 * Gets the meta field indicating that this TXN is locked
125
	 *
126
	 * @access protected
127
	 * @return int
128
	 * @throws \EE_Error
129
	 */
130
	protected function _get_lock() {
131
		return (int)$this->get_extra_meta( 'lock', true, 0 );
132
	}
133
134
135
136
	/**
137
	 * remove_expired_lock
138
	 * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
139
	 *
140
	 * @access public
141
	 * @return int
142
	 * @throws \EE_Error
143
	 */
144
	protected function _remove_expired_lock() {
145
		$locked = $this->_get_lock();
146
		if ( $locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked ) {
147
			return $this->unlock();
148
		}
149
		return 0;
150
	}
151
152
153
154
	/**
155
	 *        Set transaction total
156
	 *
157
	 * @access        public
158
	 * @param        float $total total value of transaction
159
	 * @throws \EE_Error
160
	 */
161
	public function set_total( $total = 0.00 ) {
162
		$this->set( 'TXN_total', (float)$total );
163
	}
164
165
166
167
	/**
168
	 *        Set Total Amount Paid to Date
169
	 *
170
	 * @access        public
171
	 * @param        float $total_paid total amount paid to date (sum of all payments)
172
	 * @throws \EE_Error
173
	 */
174
	public function set_paid( $total_paid = 0.00 ) {
175
		$this->set( 'TXN_paid', (float)$total_paid );
176
	}
177
178
179
180
	/**
181
	 *        Set transaction status
182
	 *
183
	 * @access        public
184
	 * @param        string $status whether the transaction is open, declined, accepted, or any number of custom values that can be set
185
	 * @throws \EE_Error
186
	 */
187
	public function set_status( $status = '' ) {
188
		$this->set( 'STS_ID', $status );
189
	}
190
191
192
193
	/**
194
	 *        Set hash salt
195
	 *
196
	 * @access        public
197
	 * @param        string $hash_salt required for some payment gateways
198
	 * @throws \EE_Error
199
	 */
200
	public function set_hash_salt( $hash_salt = '' ) {
201
		$this->set( 'TXN_hash_salt', $hash_salt );
202
	}
203
204
205
206
	/**
207
	 * Sets TXN_reg_steps array
208
	 *
209
	 * @param array $txn_reg_steps
210
	 * @throws \EE_Error
211
	 */
212
	public function set_reg_steps( array $txn_reg_steps ) {
213
		$this->set( 'TXN_reg_steps', $txn_reg_steps );
214
	}
215
216
217
218
	/**
219
	 * Gets TXN_reg_steps
220
	 *
221
	 * @return array
222
	 * @throws \EE_Error
223
	 */
224
	public function reg_steps() {
225
		$TXN_reg_steps = $this->get( 'TXN_reg_steps' );
226
		return is_array( $TXN_reg_steps ) ? (array)$TXN_reg_steps : array();
227
	}
228
229
230
231
	/**
232
	 * @return string of transaction's total cost, with currency symbol and decimal
233
	 * @throws \EE_Error
234
	 */
235
	public function pretty_total() {
236
		return $this->get_pretty( 'TXN_total' );
237
	}
238
239
240
241
	/**
242
	 * Gets the amount paid in a pretty string (formatted and with currency symbol)
243
	 *
244
	 * @return string
245
	 * @throws \EE_Error
246
	 */
247
	public function pretty_paid() {
248
		return $this->get_pretty( 'TXN_paid' );
249
	}
250
251
252
253
	/**
254
	 * calculate the amount remaining for this transaction and return;
255
	 *
256
	 * @access public
257
	 * @return float amount remaining
258
	 * @throws \EE_Error
259
	 */
260
	public function remaining() {
261
		return (float)( $this->total() - $this->paid() );
262
	}
263
264
265
266
	/**
267
	 *        get Transaction Total
268
	 *
269
	 * @access        public
270
	 * @return float
271
	 * @throws \EE_Error
272
	 */
273
	public function total() {
274
		return (float)$this->get( 'TXN_total' );
275
	}
276
277
278
279
	/**
280
	 *        get Total Amount Paid to Date
281
	 *
282
	 * @access        public
283
	 * @return float
284
	 * @throws \EE_Error
285
	 */
286
	public function paid() {
287
		return (float)$this->get( 'TXN_paid' );
288
	}
289
290
291
292
	/**
293
	 *    get_cart_session
294
	 *
295
	 * @access        public
296
	 * @throws \EE_Error
297
	 */
298
	public function get_cart_session() {
299
		$session_data = (array)$this->get( 'TXN_session_data' );
300
		return isset( $session_data[ 'cart' ] ) && $session_data[ 'cart' ] instanceof EE_Cart
301
			? $session_data[ 'cart' ]
302
			: null;
303
	}
304
305
306
307
	/**
308
	 *        get Transaction session data
309
	 *
310
	 * @access        public
311
	 * @throws \EE_Error
312
	 */
313
	public function session_data() {
314
		$session_data = $this->get( 'TXN_session_data' );
315
		if ( empty( $session_data ) ) {
316
			$session_data = array(
317
				'id'            => null,
318
				'user_id'       => null,
319
				'ip_address'    => null,
320
				'user_agent'    => null,
321
				'init_access'   => null,
322
				'last_access'   => null,
323
				'pages_visited' => array()
324
			);
325
		}
326
		return $session_data;
327
	}
328
329
330
331
	/**
332
	 *        Set session data within the TXN object
333
	 *
334
	 * @access        public
335
	 * @param        EE_Session|array $session_data
336
	 * @throws \EE_Error
337
	 */
338
	public function set_txn_session_data( $session_data ) {
339
		if ( $session_data instanceof EE_Session ) {
340
			$this->set( 'TXN_session_data', $session_data->get_session_data( NULL, TRUE ));
341
		} else {
342
			$this->set( 'TXN_session_data', $session_data );
343
		}
344
	}
345
346
347
348
	/**
349
	 *        get Transaction hash salt
350
	 *
351
	 * @access        public
352
	 * @throws \EE_Error
353
	 */
354
	public function hash_salt_() {
355
		return $this->get( 'TXN_hash_salt' );
356
	}
357
358
359
360
	/**
361
	 *    datetime
362
	 *    Returns the transaction datetime as either:
363
	 *            - unix timestamp format ($format = false, $gmt = true)
364
	 *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
365
	 *              has no affect with this option)), this also may include a timezone abbreviation if the
366
	 *              set timezone in this class differs from what the timezone is on the blog.
367
	 *            - formatted date string including the UTC (timezone) offset (default).
368
	 *
369
	 * @access    public
370
	 * @param    boolean $format - whether to return a unix timestamp (default) or formatted date string
371
	 * @param    boolean $gmt    - whether to return a unix timestamp with UTC offset applied (default) or no UTC offset applied
372
	 * @return    string | int
373
	 * @throws \EE_Error
374
	 */
375
	public function datetime( $format = FALSE, $gmt = FALSE ) {
376
		if ( $format ) {
377
			return $this->get_pretty( 'TXN_timestamp' );
378
		} else if ( $gmt ) {
379
			return $this->get_raw( 'TXN_timestamp' );
380
		} else {
381
			return $this->get( 'TXN_timestamp' );
382
		}
383
	}
384
385
386
387
	/**
388
	 *    Gets registrations on this transaction
389
	 *
390
	 * @param        array   $query_params array of query parameters
391
	 * @param        boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
392
	 * @return EE_Registration[]
393
	 * @throws \EE_Error
394
	 */
395
	public function registrations( $query_params = array(), $get_cached = FALSE ) {
396
		$query_params = ( empty( $query_params ) || ! is_array( $query_params ) )
397
			? array(
398
				'order_by' => array(
399
					'Event.EVT_name' => 'ASC',
400
					'Attendee.ATT_lname' => 'ASC',
401
					'Attendee.ATT_fname' => 'ASC'
402
				)
403
			)
404
			: $query_params;
405
		$query_params = $get_cached ? array() : $query_params;
406
		return $this->get_many_related( 'Registration', $query_params );
407
	}
408
409
410
411
	/**
412
	 * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event function
413
	 * for getting attendees and how many registrations they each have for an event)
414
	 *
415
	 * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
416
	 * @throws \EE_Error
417
	 */
418
	public function attendees() {
419
		return $this->get_many_related( 'Attendee', array( array( 'Registration.Transaction.TXN_ID' => $this->ID() ) ) );
420
	}
421
422
423
424
	/**
425
	 * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
426
	 *
427
	 * @param array $query_params like EEM_Base::get_all
428
	 * @return EE_Payment[]
429
	 * @throws \EE_Error
430
	 */
431
	public function payments( $query_params = array() ) {
432
		return $this->get_many_related( 'Payment', $query_params );
433
	}
434
435
436
437
	/**
438
	 * gets only approved payments for this transaction
439
	 *
440
	 * @return EE_Payment[]
441
	 * @throws \EE_Error
442
	 */
443
	public function approved_payments() {
444
		EE_Registry::instance()->load_model( 'Payment' );
445
		return $this->get_many_related( 'Payment', array( array( 'STS_ID' => EEM_Payment::status_id_approved ), 'order_by' => array( 'PAY_timestamp' => 'DESC' ) ) );
446
	}
447
448
449
450
	/**
451
	 * echoes $this->pretty_status()
452
	 *
453
	 * @param bool $show_icons
454
	 * @return string
455
	 * @throws \EE_Error
456
	 */
457
	public function e_pretty_status( $show_icons = FALSE ) {
458
		echo $this->pretty_status( $show_icons );
459
	}
460
461
462
463
	/**
464
	 * returns a pretty version of the status, good for displaying to users
465
	 *
466
	 * @param bool $show_icons
467
	 * @return string
468
	 * @throws \EE_Error
469
	 */
470
	public function pretty_status( $show_icons = FALSE ) {
471
		$status = EEM_Status::instance()->localized_status( array( $this->status_ID() => __( 'unknown', 'event_espresso' ) ), FALSE, 'sentence' );
472
		$icon = '';
473
		switch ( $this->status_ID() ) {
474
			case EEM_Transaction::complete_status_code:
475
				$icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
476
				break;
477
			case EEM_Transaction::incomplete_status_code:
478
				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>' : '';
479
				break;
480
			case EEM_Transaction::abandoned_status_code:
481
				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
482
				break;
483
			case EEM_Transaction::failed_status_code:
484
				$icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
485
				break;
486
			case EEM_Transaction::overpaid_status_code:
487
				$icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
488
				break;
489
		}
490
		return $icon . $status[ $this->status_ID() ];
491
	}
492
493
494
495
	/**
496
	 *        get Transaction Status
497
	 *
498
	 * @access        public
499
	 * @throws \EE_Error
500
	 */
501
	public function status_ID() {
502
		return $this->get( 'STS_ID' );
503
	}
504
505
506
507
	/**
508
	 * Returns TRUE or FALSE for whether or not this transaction cost any money
509
	 *
510
	 * @return boolean
511
	 * @throws \EE_Error
512
	 */
513
	public function is_free() {
514
		return EEH_Money::compare_floats( $this->get( 'TXN_total' ), 0, '==' );
515
	}
516
517
518
519
	/**
520
	 * Returns whether this transaction is complete
521
	 * Useful in templates and other logic for deciding if we should ask for another payment...
522
	 *
523
	 * @return boolean
524
	 * @throws \EE_Error
525
	 */
526
	public function is_completed() {
527
		return $this->status_ID() === EEM_Transaction::complete_status_code ? TRUE : FALSE;
528
	}
529
530
531
532
	/**
533
	 * Returns whether this transaction is incomplete
534
	 * Useful in templates and other logic for deciding if we should ask for another payment...
535
	 *
536
	 * @return boolean
537
	 * @throws \EE_Error
538
	 */
539
	public function is_incomplete() {
540
		return $this->status_ID() === EEM_Transaction::incomplete_status_code ? TRUE : FALSE;
541
	}
542
543
544
545
	/**
546
	 * Returns whether this transaction is overpaid
547
	 * Useful in templates and other logic for deciding if monies need to be refunded
548
	 *
549
	 * @return boolean
550
	 * @throws \EE_Error
551
	 */
552
	public function is_overpaid() {
553
		return $this->status_ID() === EEM_Transaction::overpaid_status_code ? TRUE : FALSE;
554
	}
555
556
557
558
	/**
559
	 * Returns whether this transaction was abandoned
560
	 * meaning that the transaction/registration process was somehow interrupted and never completed
561
	 * but that contact information exists for at least one registrant
562
	 *
563
	 * @return boolean
564
	 * @throws \EE_Error
565
	 */
566
	public function is_abandoned() {
567
		return $this->status_ID() === EEM_Transaction::abandoned_status_code ? TRUE : FALSE;
568
	}
569
570
571
572
	/**
573
	 * Returns whether this transaction failed
574
	 * meaning that the transaction/registration process was somehow interrupted and never completed
575
	 * and that NO contact information exists for any registrants
576
	 *
577
	 * @return boolean
578
	 * @throws \EE_Error
579
	 */
580
	public function failed() {
581
		return $this->status_ID() === EEM_Transaction::failed_status_code ? TRUE : FALSE;
582
	}
583
584
585
586
	/**
587
	 * This returns the url for the invoice of this transaction
588
	 *
589
	 * @param string $type 'html' or 'pdf' (default is pdf)
590
	 * @access public
591
	 * @return string
592
	 * @throws \EE_Error
593
	 */
594
	public function invoice_url( $type = 'html' ) {
595
		$REG = $this->primary_registration();
596
		if ( ! $REG instanceof EE_Registration ) {
597
			return '';
598
		}
599
		return $REG->invoice_url( $type );
600
	}
601
602
603
604
	/**
605
	 * Gets the primary registration only
606
	 *
607
	 * @return EE_Registration
608
	 * @throws \EE_Error
609
	 */
610
	public function primary_registration() {
611
		return $this->get_first_related( 'Registration', array( array( 'REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT ) ) );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->get_first_...RY_REGISTRANT_COUNT))); (EE_Base_Class) is incompatible with the return type declared by the interface EEI_Transaction::primary_registration of type EEI_Registration.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
612
	}
613
614
615
616
	/**
617
	 * Gets the URL for viewing the receipt
618
	 *
619
	 * @param string $type 'pdf' or 'html' (default is 'html')
620
	 * @return string
621
	 * @throws \EE_Error
622
	 */
623
	public function receipt_url( $type = 'html' ) {
624
		$REG = $this->primary_registration();
625
		if ( ! $REG instanceof EE_Registration ) {
626
			return '';
627
		}
628
		return $REG->receipt_url( $type );
629
	}
630
631
632
633
	/**
634
	 * Gets the URL of the thank you page with this registration REG_url_link added as
635
	 * a query parameter
636
	 *
637
	 * @access public
638
	 * @return string
639
	 * @throws \EE_Error
640
	 */
641
	public function payment_overview_url() {
642
		$primary_registration = $this->primary_registration();
643
		return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : FALSE;
644
	}
645
646
647
648
	/**
649
	 * Updates the transaction's status and total_paid based on all the payments
650
	 * that apply to it
651
	 *
652
	 * @deprecated
653
	 * @return boolean
654
	 * @throws \EE_Error
655
	 */
656 View Code Duplication
	public function update_based_on_payments(){
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
657
		EE_Error::doing_it_wrong(
658
			__CLASS__ . '::' . __FUNCTION__,
659
			sprintf( __( 'This method is deprecated. Please use "%s" instead', 'event_espresso' ), 'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()' ),
660
			'4.6.0'
661
		);
662
		/** @type EE_Transaction_Processor $transaction_processor */
663
		$transaction_processor = EE_Registry::instance()->load_class( 'Transaction_Processor' );
664
		return  $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment( $this );
665
	}
666
667
668
669
	/**
670
	 * @return string
671
	 * @throws \EE_Error
672
	 */
673
	public function gateway_response_on_transaction() {
674
		$payment = $this->get_first_related( 'Payment' );
675
		return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
676
	}
677
678
679
680
	/**
681
	 * Get the status object of this object
682
	 *
683
	 * @return EE_Status
684
	 * @throws \EE_Error
685
	 */
686
	public function status_obj() {
687
		return $this->get_first_related( 'Status' );
688
	}
689
690
691
692
	/**
693
	 * Gets all the extra meta info on this payment
694
	 *
695
	 * @param array $query_params like EEM_Base::get_all
696
	 * @return EE_Extra_Meta
697
	 * @throws \EE_Error
698
	 */
699
	public function extra_meta( $query_params = array() ) {
700
		return $this->get_many_related( 'Extra_Meta', $query_params );
701
	}
702
703
704
705
	/**
706
	 * Wrapper for _add_relation_to
707
	 *
708
	 * @param EE_Registration $registration
709
	 * @return EE_Base_Class the relation was added to
710
	 * @throws \EE_Error
711
	 */
712
	public function add_registration( EE_Registration $registration ) {
713
		return $this->_add_relation_to( $registration, 'Registration' );
714
	}
715
716
717
718
	/**
719
	 * Removes the given registration from being related (even before saving this transaction).
720
	 * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
721
	 *
722
	 * @param int $registration_or_id
723
	 * @return EE_Base_Class that was removed from being related
724
	 * @throws \EE_Error
725
	 */
726
	public function remove_registration_with_id( $registration_or_id ) {
727
		return $this->_remove_relation_to( $registration_or_id, 'Registration' );
728
	}
729
730
731
732
	/**
733
	 * Gets all the line items which are for ACTUAL items
734
	 *
735
	 * @return EE_Line_Item[]
736
	 * @throws \EE_Error
737
	 */
738
	public function items_purchased() {
739
		return $this->line_items( array( array( 'LIN_type' => EEM_Line_Item::type_line_item ) ) );
740
	}
741
742
743
744
	/**
745
	 * Wrapper for _add_relation_to
746
	 *
747
	 * @param EE_Line_Item $line_item
748
	 * @return EE_Base_Class the relation was added to
749
	 * @throws \EE_Error
750
	 */
751
	public function add_line_item( EE_Line_Item $line_item ) {
752
		return $this->_add_relation_to( $line_item, 'Line_Item' );
753
	}
754
755
756
757
	/**
758
	 * Gets ALL the line items related to this transaction (unstructured)
759
	 *
760
	 * @param array $query_params
761
	 * @return EE_Line_Item[]
762
	 * @throws \EE_Error
763
	 */
764
	public function line_items( $query_params = array() ) {
765
		return $this->get_many_related( 'Line_Item', $query_params );
766
	}
767
768
769
770
	/**
771
	 * Gets all the line items which are taxes on the total
772
	 *
773
	 * @return EE_Line_Item[]
774
	 * @throws \EE_Error
775
	 */
776
	public function tax_items() {
777
		return $this->line_items( array( array( 'LIN_type' => EEM_Line_Item::type_tax ) ) );
778
	}
779
780
781
782
	/**
783
	 * Gets the total line item (which is a parent of all other related line items,
784
	 * meaning it takes them all into account on its total)
785
	 *
786
	 * @return EE_Line_Item
787
	 * @throws \EE_Error
788
	 */
789
	public function total_line_item() {
790
		$item =  $this->get_first_related( 'Line_Item', array( array( 'LIN_type' => EEM_Line_Item::type_total ) ) );
791
		if( ! $item ){
792
			$item = EEH_Line_Item::create_total_line_item( $this );
793
		}
794
		return $item;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $item; (EE_Base_Class) is incompatible with the return type declared by the interface EEI_Transaction::total_line_item of type EEI_Line_Item.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
795
	}
796
797
798
799
	/**
800
	 * Returns the total amount of tax on this transaction
801
	 * (assumes there's only one tax subtotal line item)
802
	 *
803
	 * @return float
804
	 * @throws \EE_Error
805
	 */
806
	public function tax_total() {
807
		$tax_line_item = $this->tax_total_line_item();
808
		if ( $tax_line_item ) {
809
			return (float)$tax_line_item->total();
810
		} else {
811
			return (float)0;
812
		}
813
	}
814
815
816
817
	/**
818
	 * Gets the tax subtotal line item (assumes there's only one)
819
	 *
820
	 * @return EE_Line_Item
821
	 * @throws \EE_Error
822
	 */
823
	public function tax_total_line_item() {
824
		return EEH_Line_Item::get_taxes_subtotal( $this->total_line_item() );
825
	}
826
827
828
829
	/**
830
	 *  Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
831
	 *
832
	 * @return EE_Form_Section_Proper
833
	 * @throws \EE_Error
834
	 */
835
	public function billing_info(){
836
		$payment_method = $this->payment_method();
837
		if ( !$payment_method){
838
			EE_Error::add_error(__("Could not find billing info for transaction because no gateway has been used for it yet", "event_espresso"), __FILE__, __FUNCTION__, __LINE__);
839
			return false;
840
		}
841
		$primary_reg = $this->primary_registration();
842
		if ( ! $primary_reg ) {
843
			EE_Error::add_error( __( "Cannot get billing info for gateway %s on transaction because no primary registration exists", "event_espresso" ), __FILE__, __FUNCTION__, __LINE__ );
844
			return FALSE;
845
		}
846
		$attendee = $primary_reg->attendee();
847
		if ( ! $attendee ) {
848
			EE_Error::add_error( __( "Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists", "event_espresso" ), __FILE__, __FUNCTION__, __LINE__ );
849
			return FALSE;
850
		}
851
		return $attendee->billing_info_for_payment_method($payment_method);
852
	}
853
854
855
856
	/**
857
	 * Gets PMD_ID
858
	 *
859
	 * @return int
860
	 * @throws \EE_Error
861
	 */
862
	public function payment_method_ID() {
863
		return $this->get('PMD_ID');
864
	}
865
866
867
868
	/**
869
	 * Sets PMD_ID
870
	 *
871
	 * @param int $PMD_ID
872
	 * @return boolean
873
	 * @throws \EE_Error
874
	 */
875
	public function set_payment_method_ID($PMD_ID) {
876
		$this->set('PMD_ID', $PMD_ID);
877
	}
878
879
880
881
	/**
882
	 * Gets the last-used payment method on this transaction
883
	 * (we COULD just use the last-made payment, but some payment methods, namely
884
	 * offline ones, dont' create payments)
885
	 *
886
	 * @return EE_Payment_Method
887
	 * @throws \EE_Error
888
	 */
889
	public function payment_method(){
890
		$pm = $this->get_first_related('Payment_Method');
891
		if( $pm instanceof EE_Payment_Method ){
892
			return $pm;
893
		}else{
894
			$last_payment = $this->last_payment();
895
			if( $last_payment instanceof EE_Payment && $last_payment->payment_method() ){
896
				return $last_payment->payment_method();
897
			}else{
898
				return NULL;
899
			}
900
		}
901
	}
902
903
904
905
	/**
906
	 * Gets the last payment made
907
	 *
908
	 * @return EE_Payment
909
	 * @throws \EE_Error
910
	 */
911
	public function last_payment() {
912
		return $this->get_first_related( 'Payment', array( 'order_by' => array( 'PAY_ID' => 'desc' ) ) );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->get_first_...('PAY_ID' => 'desc'))); (EE_Base_Class) is incompatible with the return type declared by the interface EEI_Transaction::last_payment of type EEI_Payment.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
913
	}
914
915
916
917
	/**
918
	 * Gets all the line items which are unrelated to tickets on this transaction
919
	 *
920
	 * @return EE_Line_Item[]
921
	 * @throws \EE_Error
922
	 */
923
	public function non_ticket_line_items(){
924
		return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction( $this->ID() );
925
	}
926
927
928
929
930
}/* End of file EE_Transaction.class.php */
931
/* Location: includes/classes/EE_Transaction.class.php */
932