We are running into some issues using a custom update-cart controller and i'm not sure if what we are running into is the intended, or even required behaviour.
We are running the controller and catching the update cart response so we can handle errors and adjust the json / params returned to our application:
$response = Craft::$app->runAction('commerce/cart/update-cart');
At this point here, we expected the saveElement() method called in the commerce controller, by returning true, to have worked its magic and saved lineItems, adjustments and all other goodness to the database.
And therefore we would be able to get the latest / now saved order to work with, before returning our tweaked response.
Commerce::getInstance()->getOrders()->getOrderById($response->data[$this->_cartVariable]['id'])
It doesn't look like this is the case? Should it be? And if not is there any way around it?
Hi @samhibberd
It seems like maybe the EVENT_MODIFY_CART_INFO might be of use to you in this situation?
use craft\commerce\controllers\BaseFrontEndController;
use craft\commerce\events\ModifyCartInfoEvent;
use yii\base\Event;
Event::on(BaseFrontEndController::class, BaseFrontEndController::EVENT_MODIFY_CART_INFO, function(ModifyCartInfoEvent $e) {
$cartArray = $e->cartInfo;
$cartArray['anotherOne'] = 'Howdy';
$e->cartInfo = $cartArray;
});
This allows you to modify the returned cart data when calling the update-cart action.
Thats just what i was going to suggest as an option if this wasn't going to work out, no idea how i missed that :) Thanks @nfourtythree
Out of interest, is there a reason that latest version of the order is not saved to the db when I am trying to get it? Be good to understand.
Hi @samhibberd
Unfortunately, without digging into your code I am not entirely sure. It should definitely be saved as it is part of the update-cart action.
The only way it wouldn't be saved is if there was an error of some sort.
If you are trying to solve something similar in the future it is normally my recommendation to create a controller that extends the cart controller and post to your own update-cart action that calls parent::actionUpdateCart() instead of the runAction method.
But in theory, it shouldn't make a difference.
Afaict it isn't saved in full at that point and I have not uncovered any errors, and a refresh of the page shows that the save was actioned. Could suggest a bug?
Thanks for the controller tip, nice one!
@nfourtythree running into the same issue with the EVENT_MODIFY_CART_INFO as the cart is not sent to the ModifyCartInfoEvent, and i need to get the latest, could it be?
@samhibberd if you see in this example, $e->cartInfo contains the cart. I have just tested the code below and verified that anotherOne was returned in the cart JSON. Along with the updated cart data.
use craft\commerce\controllers\BaseFrontEndController; use craft\commerce\events\ModifyCartInfoEvent; use yii\base\Event; Event::on(BaseFrontEndController::class, BaseFrontEndController::EVENT_MODIFY_CART_INFO, function(ModifyCartInfoEvent $e) { $cartArray = $e->cartInfo; $cartArray['anotherOne'] = 'Howdy'; $e->cartInfo = $cartArray; });
Ah but thats just the arrayable values from the Cart (Order) model, i need access to the model / element, so that I can use some methods set in a custom element behavior?
Ah, apologies I misunderstood what you were trying to achieve. I thought you just wanted some extra data returned in the call.
In that case, you are best going with my suggestion of extending the CartController class.
This will mean you will have access to the $_cart private property which will contain the update Order element.
Hope this helps!
Ok great thanks, will have a play with that, it would still be great understand why it's not saved at that point? I would be happy to supply anything you need: All I am currently doing in my custom controller is this:
public function actionUpdateCart()
{
$this->requirePostRequest();
$this->requireAcceptsJson();
$response = Craft::$app->runAction('commerce/cart/update-cart');
// This is where i handle errors, would like to get the order and tweak the response data etc etc
// $order = Commerce::getInstance()->getOrders()->getOrderById($usingCartIdFromResponse)
return $this->asJson(['my' => 'response']);
}
Also could you include the $cart model to the EVENT_MODIFY_CART_INFO as it would be useful there?
This will mean you will have access to the
$_cartprivate property which will contain the updateOrderelement.
@nfourtythree Apologies if I a making a real rookie error here, but surely I won't have access to the private $_cart variable defined in the commerce CartController in my child class.
Ah, my bad @samhibberd when I said private I meant protected but at the same time it is actually private so my advice is null and void. Sorry about that!
And on a separate note, I think I might have just been able to replicate the initial thing you mentioned. So I will reopen this and investigate further.
Thanks @nfourtythree, just shout if you need anything further from me!
Hi @samhibberd
Quick update, we have pushed a fix that should get this working for you. Also have made $_cart a protected variable.
This should mean you can create more clean extensions of the cart controller without having to retrieve the order again. Example of a custom module controller extending the cart controller:
<?php
namespace modules\controllers;
use craft\commerce\controllers\CartController;
class FooController extends CartController
{
public function actionUpdateCart()
{
$result = parent::actionUpdateCart();
if ($this->_cart->hasErrors()) {
return $result;
}
// Custom code
$total = $this->_cart->getTotal();
return $result;
}
}
This fix will be in the next release which we are looking to get out soon.
Thanks.
Brilliant. Thanks.
@nfourtythree Could you also make _getCart() and _returnCart() protected?
Hi @sjelfull
I have created a new issue/feature request for this: #1410
Thanks.
Hello @nfourtythree,
I have tried the approach above, but am currently running into an exception.
Error: Call to a member function getSettings() on null in /app/vendor/craftcms/commerce/src/controllers/CartController.php:57
Stack trace:
#0 /app/vendor/yiisoft/yii2/base/BaseObject.php(109): craft\commerce\controllers\CartController->init()
#1 /app/vendor/yiisoft/yii2/base/Controller.php(98): yii\base\BaseObject->__construct(Array)
#2 [internal function]: yii\base\Controller->__construct('custom-cart', Object(craft\web\Application), Array)
#3 /app/vendor/yiisoft/yii2/di/Container.php(412): ReflectionClass->newInstanceArgs(Array)
#4 /app/vendor/yiisoft/yii2/di/Container.php(171): yii\di\Container->build('modules\\control...', Array, Array)
#5 /app/vendor/yiisoft/yii2/BaseYii.php(345): yii\di\Container->get('modules\\control...', Array)
#6 /app/vendor/yiisoft/yii2/base/Module.php(427): yii\BaseYii::createObject('modules\\control...', Array)
#7 /app/vendor/yiisoft/yii2/base/Application.php(321): yii\base\Module->getModule('custom-cart')
#8 /app/vendor/craftcms/cms/src/web/Application.php(110): yii\base\Application->bootstrap()
#9 /app/vendor/yiisoft/yii2/base/Application.php(279): craft\web\Application->bootstrap()
#10 /app/vendor/craftcms/cms/src/web/Application.php(91): yii\base\Application->init()
#11 /app/vendor/yiisoft/yii2/base/BaseObject.php(109): craft\web\Application->init()
#12 /app/vendor/yiisoft/yii2/base/Application.php(212): yii\base\BaseObject->__construct(Array)
#13 [internal function]: yii\base\Application->__construct(Array)
#14 /app/vendor/yiisoft/yii2/di/Container.php(420): ReflectionClass->newInstanceArgs(Array)
#15 /app/vendor/yiisoft/yii2/di/Container.php(171): yii\di\Container->build('craft\\web\\Appli...', Array, Array)
#16 /app/vendor/yiisoft/yii2/BaseYii.php(365): yii\di\Container->get('craft\\web\\Appli...', Array, Array)
#17 /app/vendor/craftcms/cms/bootstrap/bootstrap.php(246): yii\BaseYii::createObject(Array)
#18 /app/vendor/craftcms/cms/bootstrap/web.php(51): require('/app/vendor/cra...')
#19 /app/web/index.php(20): require('/app/vendor/cra...')
#20 {main}
My controller looks exactly like yours above:
namespace modules\controller;
use craft\commerce\controllers\CartController;
class CustomCartController extends CartController {
public function actionUpdateCart() {
$result = parent::actionUpdateCart();
if ($this->_cart->hasErrors()) {
return $result;
}
// Custom code
$total = $this->_cart->getTotal();
return $result;
}
}
Any help with this will be greatly appreciated. Thank you.
Hi @lukkigi
I can't seem to replicate this locally with a custom module controller. Everything works as expected.
I noticed you have namespace modules\controller; are you correctly setting the controller namespace in your module's init() method?
To have things working with a fresh install with Craft and Commerce I had the following.
app.php
use craft\helpers\App;
return [
'id' => App::env('APP_ID') ?: 'CraftCMS',
'modules' => [
'my-module' => \modules\Module::class,
],
'bootstrap' => ['my-module'],
];
Module.php
namespace modules;
use Craft;
class Module extends \yii\base\Module
{
public $moreThanTwo = false;
/**
* Initializes the module.
*/
public function init()
{
// Set a @modules alias pointed to the modules/ directory
Craft::setAlias('@modules', __DIR__);
// Set the controllerNamespace based on whether this is a console or web request
if (Craft::$app->getRequest()->getIsConsoleRequest()) {
$this->controllerNamespace = 'modules\\console\\controllers';
} else {
$this->controllerNamespace = 'modules\\controllers';
}
}
}
CustomCartController.php
namespace modules\controllers;
use craft\commerce\controllers\CartController;
class CustomCartController extends CartController
{
public function actionUpdateCart() {
parent::actionUpdateCart();
}
}
Hope this helps, thanks!
@nfourtythree, thanks a lot for your reply. I was able to figure out my error based on your help. I was trying to implement the controller as its own module.
Most helpful comment
Hi @samhibberd
Quick update, we have pushed a fix that should get this working for you. Also have made
$_carta protected variable.This should mean you can create more clean extensions of the cart controller without having to retrieve the order again. Example of a custom module controller extending the cart controller:
This fix will be in the next release which we are looking to get out soon.
Thanks.