Completed
Pull Request — master (#610)
by Will
17:11
created

CheckoutPage   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 70
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 4

Test Coverage

Coverage 16.22%

Importance

Changes 0
Metric Value
wmc 6
lcom 0
cbo 4
dl 0
loc 70
ccs 6
cts 37
cp 0.1622
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A find_link() 0 8 2
B getCMSFields() 0 24 1
A requireDefaultRecords() 0 17 3
1
<?php
2
3
/**
4
 * CheckoutPage is a CMS page-type that shows the order
5
 * details to the customer for their current shopping
6
 * cart on the site.
7
 *
8
 * @see     CheckoutPage_Controller->Order()
9
 *
10
 * @package shop
11
 */
12
class CheckoutPage extends Page
13
{
14
    private static $db   = array(
15
        'PurchaseComplete' => 'HTMLText',
16
    );
17
18
    private static $icon = 'silvershop/images/icons/money';
19
20
    /**
21
     * Returns the link to the checkout page on this site
22
     *
23
     * @param boolean $urlSegment If set to TRUE, only returns the URLSegment field
24
     *
25
     * @return string Link to checkout page
26
     */
27 4
    public static function find_link($urlSegment = false, $action = null, $id = null)
28
    {
29 4
        $base = CheckoutPage_Controller::config()->url_segment;
30 4
        if ($page = self::get()->first()) {
31 4
            $base = $page->Link();
32 4
        }
33 4
        return Controller::join_links($base, $action, $id);
34
    }
35
36
    public function getCMSFields()
37
    {
38
        $this->beforeUpdateCMSFields(function(FieldList $fields) {
39
            $fields->addFieldsToTab(
40
                'Root.Main',
41
                array(
42
                    HtmlEditorField::create(
43
                        'PurchaseComplete',
44
                        _t('CheckoutPage.db_PurchaseComplete', 'Purchase Complete'),
45
                        4
46
                    )
47
                        ->setDescription(
48
                            _t(
49
                                'CheckoutPage.PurchaseCompleteDescription',
50
                                "This message is included in reciept email, after the customer submits the checkout"
51
                            )
52
                        ),
53
                ),
54
                'Metadata'
55
            );
56
        });
57
58
        return parent::getCMSFields();
59
    }
60
61
    /**
62
     * This module always requires a page model.
63
     */
64
    public function requireDefaultRecords()
65
    {
66
        parent::requireDefaultRecords();
67
        if (!self::get()->exists() && $this->config()->create_default_pages) {
68
            $page = self::create(
69
                array(
70
                    'Title'       => 'Checkout',
71
                    'URLSegment'  => CheckoutPage_Controller::config()->url_segment,
72
                    'ShowInMenus' => 0,
73
                )
74
            );
75
            $page->write();
76
            $page->publish('Stage', 'Live');
77
            $page->flushCache();
78
            DB::alteration_message('Checkout page created', 'created');
79
        }
80
    }
81
}
82
83
/**
84
 * @package shop
85
 * @mixin CheckoutPage
86
 * @mixin SteppedCheckout
87
 * @mixin CheckoutStep_Address
88
 * @mixin CheckoutStep_AddressBook
89
 * @mixin CheckoutStep_ContactDetails
90
 * @mixin CheckoutStep_Membership
91
 * @mixin CheckoutStep_PaymentMethod
92
 * @mixin CheckoutStep_Summary
93
 */
94
class CheckoutPage_Controller extends Page_Controller
95
{
96
    private static $url_segment     = 'checkout';
97
98
    private static $allowed_actions = array(
99
        'OrderForm',
100
        'payment',
101
        'PaymentForm',
102
    );
103
104 6
    public function Title()
105
    {
106 6
        if ($this->Title) {
107 3
            return $this->Title;
108
        }
109
110 6
        return _t('CheckoutPage.DefaultTitle', "Checkout");
111
    }
112
113
    public function OrderForm()
114
    {
115
        if (!(bool)$this->Cart()) {
116
            return false;
117
        }
118
119
        /** @var CheckoutComponentConfig $config */
120
        $config = Injector::inst()->create("CheckoutComponentConfig", ShoppingCart::curr());
121
        $form = PaymentForm::create($this, 'OrderForm', $config);
122
123
        // Normally, the payment is on a second page, either offsite or through /checkout/payment
124
        // If the site has customised the checkout component config to include an onsite payment
125
        // component, we should honor that and change the button label. PaymentForm::checkoutSubmit
126
        // will also check this and process payment if needed.
127
        if ($config->getComponentByType('OnsitePaymentCheckoutComponent')) {
128
            $form->setActions(
129
                FieldList::create(
130
                    FormAction::create('checkoutSubmit', _t('CheckoutForm.SubmitPayment', 'Submit Payment'))
131
                )
132
            );
133
        }
134
135
        $form->Cart = $this->Cart();
136
        $this->extend('updateOrderForm', $form);
137
138
        return $form;
139
    }
140
141
    /**
142
     * Action for making on-site payments
143
     */
144
    public function payment()
145
    {
146
        if (!$this->Cart()) {
147
            return $this->redirect($this->Link());
148
        }
149
150
        return array(
151
            'Title'     => 'Make Payment',
152
            'OrderForm' => $this->PaymentForm(),
153
        );
154
    }
155
156
    public function PaymentForm()
157
    {
158
        if (!(bool)$this->Cart()) {
159
            return false;
160
        }
161
162
        $config = new CheckoutComponentConfig(ShoppingCart::curr(), false);
163
        $config->addComponent(OnsitePaymentCheckoutComponent::create());
164
165
        $form = PaymentForm::create($this, "PaymentForm", $config);
166
167
        $form->setActions(
168
            FieldList::create(
169
                FormAction::create("submitpayment", _t('CheckoutPage.SubmitPayment', "Submit Payment"))
170
            )
171
        );
172
173
        $form->setFailureLink($this->Link());
174
        $this->extend('updatePaymentForm', $form);
175
176
        return $form;
177
    }
178
179
    /**
180
     * Retrieves error messages for the latest payment (if existing).
181
     * This can originate e.g. from an earlier offsite gateway API response.
182
     *
183
     * @return string
184
     */
185
    public function PaymentErrorMessage()
186
    {
187
        $order = $this->Cart();
188
        if (!$order) {
189
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by CheckoutPage_Controller::PaymentErrorMessage of type string.

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...
190
        }
191
192
        $lastPayment = $order->Payments()->sort('Created', 'DESC')->first();
193
        if (!$lastPayment) {
194
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by CheckoutPage_Controller::PaymentErrorMessage of type string.

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...
195
        }
196
197
        $errorMessages = $lastPayment->Messages()->exclude('Message', '')->sort('Created', 'DESC');
198
        $lastErrorMessage = null;
199
        foreach ($errorMessages as $errorMessage) {
200
            if ($errorMessage instanceof GatewayErrorMessage) {
201
                $lastErrorMessage = $errorMessage;
202
                break;
203
            }
204
        }
205
        if (!$lastErrorMessage) {
206
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by CheckoutPage_Controller::PaymentErrorMessage of type string.

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...
207
        }
208
209
        return $lastErrorMessage->Message;
210
    }
211
}
212