Skip to content

Commit

Permalink
Merge pull request #3201 from craftcms/bugfix/email-property-on-order
Browse files Browse the repository at this point in the history
Deprecate `email` as an attribute on the `Order` element
  • Loading branch information
lukeholder committed Jul 17, 2023
2 parents d386c0f + 1f3d659 commit 8468b5d
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 95 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 4.3.0 - Unreleased

- Guest customers registering during checkout now have their addresses saved to their account.
- Deprecated `craft\commerce\elements\Order::setEmail()`. `Order::setCustomer()` should be used instead.

## Unreleased

Expand Down
3 changes: 2 additions & 1 deletion src/controllers/CartController.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ public function actionUpdateCart(): ?Response
$email = $this->request->getParam('email');
if ($email && ($this->_cart->getEmail() === null || $this->_cart->getEmail() != $email)) {
try {
$this->_cart->setEmail($email);
$user = Craft::$app->getUsers()->ensureUserByEmail($email);
$this->_cart->setCustomer($user);
} catch (\Exception $e) {
$this->_cart->addError('email', $e->getMessage());
}
Expand Down
46 changes: 14 additions & 32 deletions src/elements/Order.php
Original file line number Diff line number Diff line change
Expand Up @@ -1138,20 +1138,6 @@ class Order extends Element
*/
private ?string $_paymentCurrency = null;

/**
* @var string|null
* @see Order::setEmail() To set the order email
* @see Order::getEmail() To get the email
* ---
* ```php
* echo $order->email;
* ```
* ```twig
* {{ order.email }}
* ```
*/
private ?string $_email = null;

/**
* @var Transaction[]|null
* @see Order::getTransactions()
Expand All @@ -1177,7 +1163,7 @@ class Order extends Element
* {{ order.customer }}
* ```
*/
private User|null|false $_customer;
private User|null|false $_customer = null;

/**
* @var float|null
Expand Down Expand Up @@ -1356,7 +1342,6 @@ public function attributes(): array
$names[] = 'customerId';
$names[] = 'paymentCurrency';
$names[] = 'paymentAmount';
$names[] = 'email';
$names[] = 'isPaid';
$names[] = 'itemSubtotal';
$names[] = 'itemTotal';
Expand Down Expand Up @@ -1440,6 +1425,7 @@ public function fields(): array
};
}

$fields['email'] = 'email';
$fields['paidStatusHtml'] = 'paidStatusHtml';
$fields['customerLinkHtml'] = 'customerLinkHtml';
$fields['orderStatusHtml'] = 'orderStatusHtml';
Expand Down Expand Up @@ -1511,7 +1497,6 @@ protected function defineRules(): array

[['paymentSourceId'], 'number', 'integerOnly' => true],
[['paymentSourceId'], 'validatePaymentSourceId'],
[['email'], 'email'],

[['number', 'user', 'orderCompletedEmail', 'saveBillingAddressOnOrderComplete', 'saveShippingAddressOnOrderComplete'], 'safe'],
]);
Expand Down Expand Up @@ -2312,10 +2297,6 @@ public function getCustomer(): ?User
}
}

if ($this->_customer) {
$this->_email = $this->_customer->email;
}

return $this->_customer ?: null;
}

Expand All @@ -2329,7 +2310,8 @@ public function setCustomer(?User $customer = null): void
$this->_customer = $customer;
if ($this->_customer) {
$this->_customerId = $this->_customer->id;
$this->_email = $this->_customer->email;
} else {
$this->_customerId = null;
}
}

Expand All @@ -2338,7 +2320,7 @@ public function setCustomer(?User $customer = null): void
*/
public function getUser(): ?User
{
Craft::$app->getDeprecator()->log('Order::getUser()', 'The `Order::getUser()` is deprecated, use the `Order::getCustomer()` instead.');
Craft::$app->getDeprecator()->log('Order::getUser()', 'The `Order::getUser()` is deprecated, use `Order::getCustomer()` instead.');
return $this->getCustomer();
}

Expand All @@ -2347,37 +2329,37 @@ public function getUser(): ?User
*
* @param string|null $email
* @throws Exception
* @deprecated in 4.3.0. Use [[setCustomer()]] instead.
*/
public function setEmail(?string $email): void
{
Craft::$app->getDeprecator()->log(__METHOD__, '`Order::setEmail()` has been deprecated use `Order::setCustomer()` instead.');
if (!$email) {
$this->_customer = null;
$this->_customerId = null;
$this->_email = null;
return;
}

if ($this->_email === $email) {
if ($this->_customer && $this->_customer->email === $email) {
return;
}

$user = Craft::$app->getUsers()->ensureUserByEmail($email);
$this->_email = $email;
$this->setCustomer($user);
}

/**
* Returns the email for this order. Will always be the registered users email if the order's customer is related to a user.
* Returns the email for this order. Will always be the customer's email if they exist.
* @return string|null
*/
public function getEmail(): ?string
{
if ($user = $this->getCustomer()) {
$this->_email = $user->email;
}

return $this->_email ?? null;
return $this->getCustomer()?->email ?? null;
}

/**
* @return bool
*/
public function getIsPaid(): bool
{
return !$this->hasOutstandingBalance() && $this->isCompleted;
Expand Down
15 changes: 14 additions & 1 deletion src/elements/db/OrderQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,14 @@ public function withTransactions(bool $value = true): OrderQuery
*/
public function populate($rows): array
{
// @TODO remove at next breaking change
// Remove `email` key from each row.
array_walk($rows, function(&$row) {
if (array_key_exists('email', $row)) {
unset($row['email']);
}
});

/** @var Order[] $orders */
$orders = parent::populate($rows);

Expand Down Expand Up @@ -1463,7 +1471,10 @@ protected function beforePrepare(): bool
'commerce_orders.couponCode',
'commerce_orders.orderStatusId',
'commerce_orders.dateOrdered',

// @TODO remove at next breaking change
'commerce_orders.email',

'commerce_orders.isCompleted',
'commerce_orders.datePaid',
'commerce_orders.currency',
Expand Down Expand Up @@ -1538,7 +1549,9 @@ protected function beforePrepare(): bool
}

if (isset($this->email) && $this->email) {
$this->subQuery->andWhere(Db::parseParam('commerce_orders.email', $this->email, '=', true));
// Join and search the users table for email address
$this->subQuery->leftJoin(CraftTable::USERS . ' users', '[[users.id]] = [[commerce_orders.customerId]]');
$this->subQuery->andWhere(Db::parseParam('users.email', $this->email, '=', true));
}

// Allow true ot false but not null
Expand Down
7 changes: 3 additions & 4 deletions src/services/Carts.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,9 @@ public function getCart(bool $forceSave = false): Order
$this->_cart->paymentCurrency = $this->_getCartPaymentCurrencyIso();
$this->_cart->origin = Order::ORIGIN_WEB;

if ($currentUser) {
if ($this->_cart->getCustomer() === null || ($currentUser->email && $currentUser->email !== $this->_cart->email)) {
$this->_cart->setEmail($currentUser->email); // Will ensure the customer is also set
}
// Switch the cart customer if needed
if ($currentUser && ($this->_cart->getCustomer() === null || ($currentUser->email && $currentUser->email !== $this->_cart->getEmail()))) {
$this->_cart->setCustomer($currentUser);
}

$hasIpChanged = $originalIp != $this->_cart->lastIp;
Expand Down
97 changes: 47 additions & 50 deletions src/services/Customers.php
Original file line number Diff line number Diff line change
Expand Up @@ -380,75 +380,72 @@ private function _saveAddressesFromOrder(Order $order): void
*/
private function _activateUserFromOrder(Order $order): void
{
if (!$order->email) {
$user = $order->getCustomer();
if (!$user || $user->getIsCredentialed()) {
return;
}

$user = Craft::$app->getUsers()->ensureUserByEmail($order->email);
$billingAddress = $order->getBillingAddress();
$shippingAddress = $order->getShippingAddress();

if (!$user->getIsCredentialed()) {
$billingAddress = $order->getBillingAddress();
$shippingAddress = $order->getShippingAddress();

if (!$user->fullName) {
$user->fullName = $billingAddress?->fullName ?? $shippingAddress?->fullName ?? '';
}
if (!$user->fullName) {
$user->fullName = $billingAddress?->fullName ?? $shippingAddress?->fullName ?? '';
}

$user->username = $order->email;
$user->pending = true;
$user->setScenario(Element::SCENARIO_ESSENTIALS);
$user->username = $order->getEmail();
$user->pending = true;
$user->setScenario(Element::SCENARIO_ESSENTIALS);

if (Craft::$app->getElements()->saveElement($user)) {
Craft::$app->getUsers()->assignUserToDefaultGroup($user);
$emailSent = Craft::$app->getUsers()->sendActivationEmail($user);
if (Craft::$app->getElements()->saveElement($user)) {
Craft::$app->getUsers()->assignUserToDefaultGroup($user);
$emailSent = Craft::$app->getUsers()->sendActivationEmail($user);

if (!$emailSent) {
Craft::warning('"registerUserOnOrderComplete" used to create the user, but couldn’t send an activation email. Check your email settings.', __METHOD__);
}
if (!$emailSent) {
Craft::warning('"registerUserOnOrderComplete" used to create the user, but couldn’t send an activation email. Check your email settings.', __METHOD__);
}

if ($billingAddress || $shippingAddress) {
$newAttributes = ['ownerId' => $user->id];
if ($billingAddress || $shippingAddress) {
$newAttributes = ['ownerId' => $user->id];

// If there is only one address make sure we don't add duplicates to the user
if ($order->hasMatchingAddresses()) {
$newAttributes['title'] = Craft::t('commerce', 'Address');
$shippingAddress = null;
}
// If there is only one address make sure we don't add duplicates to the user
if ($order->hasMatchingAddresses()) {
$newAttributes['title'] = Craft::t('commerce', 'Address');
$shippingAddress = null;
}

// Copy addresses to user
if ($billingAddress) {
$newBillingAddress = Craft::$app->getElements()->duplicateElement($billingAddress, $newAttributes);
// Copy addresses to user
if ($billingAddress) {
$newBillingAddress = Craft::$app->getElements()->duplicateElement($billingAddress, $newAttributes);

/**
* Because we are cloning from an order address the `CustomerAddressBehavior` hasn't been instantiated
* therefore we are unable to simply set the `isPrimaryBilling` property when specifying the new attributes during duplication.
*/
if (!$newBillingAddress->hasErrors()) {
$this->savePrimaryBillingAddressId($user, $newBillingAddress->id);
/**
* Because we are cloning from an order address the `CustomerAddressBehavior` hasn't been instantiated
* therefore we are unable to simply set the `isPrimaryBilling` property when specifying the new attributes during duplication.
*/
if (!$newBillingAddress->hasErrors()) {
$this->savePrimaryBillingAddressId($user, $newBillingAddress->id);

if ($order->hasMatchingAddresses()) {
$this->savePrimaryShippingAddressId($user, $newBillingAddress->id);
}
if ($order->hasMatchingAddresses()) {
$this->savePrimaryShippingAddressId($user, $newBillingAddress->id);
}
}
}

if ($shippingAddress) {
$newShippingAddress = Craft::$app->getElements()->duplicateElement($shippingAddress, $newAttributes);
if ($shippingAddress) {
$newShippingAddress = Craft::$app->getElements()->duplicateElement($shippingAddress, $newAttributes);

/**
* Because we are cloning from an order address the `CustomerAddressBehavior` hasn't been instantiated
* therefore we are unable to simply set the `isPrimaryShipping` property when specifying the new attributes during duplication.
*/
if (!$newShippingAddress->hasErrors()) {
$this->savePrimaryShippingAddressId($user, $newShippingAddress->id);
}
/**
* Because we are cloning from an order address the `CustomerAddressBehavior` hasn't been instantiated
* therefore we are unable to simply set the `isPrimaryShipping` property when specifying the new attributes during duplication.
*/
if (!$newShippingAddress->hasErrors()) {
$this->savePrimaryShippingAddressId($user, $newShippingAddress->id);
}
}
} else {
$errors = $user->getErrors();
Craft::warning('Could not create user on order completion.', __METHOD__);
Craft::warning($errors, __METHOD__);
}
} else {
$errors = $user->getErrors();
Craft::warning('Could not create user on order completion.', __METHOD__);
Craft::warning($errors, __METHOD__);
}
}
}
Loading

0 comments on commit 8468b5d

Please sign in to comment.