I have exception when I selected some records in grid and trying to delete them. Here is that exception:
1 exception(s): Exception #0 (InvalidArgumentException):
Amiddio\News\Model\News does not extend \Magento\Framework\DataObject
Magento version is 2.1.8
app\code\Amiddio\News\view\adminhtml\ui_component\news_grid_listing.xml
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="sticky" xsi:type="boolean">true</item>
</item>
</argument>
<massaction name="listing_massaction">
<action name="delete">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">delete</item>
<item name="label" xsi:type="string" translate="true">Delete</item>
<item name="url" xsi:type="url" path="news/actions/massDelete"/>
<item name="confirm" xsi:type="array">
<item name="title" xsi:type="string" translate="true">Delete items</item>
<item name="message" xsi:type="string" translate="true">Are you sure you want to delete selected items?</item>
</item>
</item>
</argument>
</action>
</massaction>
app\code\Amiddio\News\etc\di.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="news_grid_listing_data_source" xsi:type="string">Amiddio\News\Model\ResourceModel\News\Grid\Collection</item>
</argument>
</arguments>
</type>
</config>
app\code\Amiddio\News\Model\News.php
namespace Amiddio\News\Model;
use Magento\Framework\Model\AbstractModel;
class News extends AbstractModel
{
protected function _construct()
{
$this->_init('Amiddio\News\Model\ResourceModel\News');
}
}
app\code\Amiddio\News\Model\ResourceModel\News.php
namespace Amiddio\News\Model\ResourceModel;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
class News extends AbstractDb
{
protected function _construct()
{
$this->_init('amiddio_news', 'news_id');
}
}
app\code\Amiddio\News\Model\ResourceModel\News\Collection.php
namespace Amiddio\News\Model\ResourceModel\News;
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
protected function _construct()
{
$this->_init(' Amiddio\News\Model\News', 'Amiddio\News\Model\ResourceModel\News');
}
}
app\code\Amiddio\News\Model\ResourceModel\News\Grid\Collection.php
namespace Amiddio\News\Model\ResourceModel\News\Grid;
use Amiddio\News\Model\ResourceModel\News\Collection as GridCollection;
use Magento\Framework\Search\AggregationInterface;
use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\View\Element\UiComponent\DataProvider\Document;
use Amiddio\News\Model\ResourceModel\News;
use Magento\Framework\Api\SearchCriteriaInterface;
class Collection extends GridCollection implements SearchResultInterface
{
protected $aggregations;
protected function _construct()
{
$this->_init(Document::class, News::class);
}
public function getAggregations()
{
return $this->aggregations;
}
public function setAggregations($aggregations)
{
$this->aggregations = $aggregations;
}
public function getAllIds($limit = null, $offset = null)
{
return $this->getConnection()->fetchCol($this->_getAllIdsSelect($limit, $offset), $this->_bindParams);
}
public function getSearchCriteria()
{
return null;
}
public function setSearchCriteria(SearchCriteriaInterface $searchCriteria = null)
{
return $this;
}
public function getTotalCount()
{
return $this->getSize();
}
public function setTotalCount($totalCount)
{
return $this;
}
public function setItems(array $items = null)
{
return $this;
}
}
app\code\Amiddio\News\Controller\Adminhtml\Actions\MassDelete.php
namespace Amiddio\News\Controller\Adminhtml\Actions;
use Magento\Framework\Controller\ResultFactory;
use Magento\Backend\App\Action\Context;
use Magento\Ui\Component\MassAction\Filter;
use Amiddio\News\Model\ResourceModel\News\CollectionFactory;
/**
* Class MassDelete
*/
class MassDelete extends \Magento\Backend\App\Action
{
/**
* #var Filter
*/
protected $filter;
/**
* #var CollectionFactory
*/
protected $collectionFactory;
/**
* #param Context $context
* #param Filter $filter
* #param CollectionFactory $collectionFactory
*/
public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
{
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
parent::__construct($context);
}
/**
* Execute action
*
* #return \Magento\Backend\Model\View\Result\Redirect
* #throws \Magento\Framework\Exception\LocalizedException|\Exception
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collectionSize = $collection->getSize();
foreach ($collection as $page) {
$page->delete();
}
$this->messageManager->addSuccess(__('A total of %1 record(s) have been deleted.', $collectionSize));
/** #var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('news/index/');
}
}
I'm beginner in Magento 2, and I'm trying to resolve this issue some days already. Maybe someone had that problem and he/she knows solution?!
Thanks very much for any help!
In your News model (app\code\Amiddio\News\Model\News.php), your class should implement IdentityInterface.
change:
class News extends AbstractDb{ ...
to:
use Magento\Framework\DataObject\IdentityInterface;
class News extends AbstractDb implements IdentityInterface{ ....
I found the bug im my code.
In the file '...ResourceModel\News\Collection.php' I have space before 'Amiddio\News\Model\News'.
The problem solved.
Related
Does anyone knows to modify product data using Shopware\Storefront\Page\Product\ProductPageLoadedEvent ?
services.xml
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="Swag\BasicExample\Service\AddDataToPage" >
<argument type="service" id="product.repository"/>
<tag name="kernel.event_subscriber" />
</service>
</services>
</container>
AddDataToPage.php
<?php declare(strict_types=1);
namespace Swag\BasicExample\Service;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
class AddDataToPage implements EventSubscriberInterface
{
/**
* #var EntityRepositoryInterface
*/
private $productRepository;
/**
* #param EntityRepositoryInterface $productRepository
*/
public function __construct(
EntityRepositoryInterface $productRepository
)
{
$this->productRepository = $productRepository;
}
/**
* #return string[]
*/
public static function getSubscribedEvents(): array
{
return [
ProductPageLoadedEvent::class => 'onProductsLoaded'
];
}
/**
* #param ProductPageLoadedEvent $event
* #return void
*/
public function onProductsLoaded(
ProductPageLoadedEvent $event
)
{
// the product is inside the page object
$productData = $event->getPage()->getProduct();
//modifying name
$this->log($productData->getName());
$productData->setName('Prefix Product Name' . $productData->getName());
$this->log($productData->getName());
//modifying ManufacturerNumber
$this->log($productData->getManufacturerNumber());
$productData->setManufacturerNumber('Prefix ManufacturerNumber' . $productData->getManufacturerNumber());
$this->log($productData->getManufacturerNumber());
$event->getPage()->setProduct($productData);
}
/**
* #param $message
* #return void
*/
private function log($message)
{
$logFileName = 'someFile.log';
file_put_contents(
$logFileName,
$message . PHP_EOL,
FILE_APPEND
);
}
}
After modifying the above mentioned changes it still shows the original data although
$event->getPage()->setProduct($productData);
I'm in doubt whether ProductPageLoadedEvent is an after dispatching event or before dispatching the event.
hope your will have the time to help me.
On any circumstances my Magento 2 Array Mapping will not write selected="selected" to my field. Getting crazy about this.
The Data will be saved and fetched correct.
Big thanks to your help.
Version: Magento 2.2.2
/app/code/Sumedia/Switcher/Block/Adminhtml/System/Config/Form/Field/Group.php
<?php
namespace Sumedia\Switcher\Block\Adminhtml\System\Config\Form\Field;
class Group extends \Magento\Framework\View\Element\Html\Select {
/**
* #var \Magento\Store\Model\StoreManager
*/
protected $storeManager;
/**
* Store constructor.
* #param \Magento\Framework\View\Element\Context $context
* #param \Magento\Store\Model\StoreManager $storeManager
* #param array $data
*/
public function __construct(
\Magento\Framework\View\Element\Context $context,
\Magento\Store\Model\StoreManager $storeManager,
array $data = []
){
parent::__construct($context, $data);
$this->storeManager = $storeManager;
}
/**
* #return string
*/
public function _toHtml(){
if(!$this->getOptions()){
$groups = $this->storeManager->getGroups();
foreach($groups AS $row){
$this->addOption($row->getGroupId(),$row->getName());
}
}
return parent::_toHtml();
}
/**
* #param string $value
* #return $this
*/
public function setInputName($value){
return $this->setName($value);
}
}
/app/code/Sumedia/Switcher/Block/Adminhtml/System/Config/Form/Field/Groupmap.php
<?php
namespace Sumedia\Switcher\Block\Adminhtml\System\Config\Form\Field;
use Magento\Backend\Block\Template\Context;
use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray;
class Groupmap extends AbstractFieldArray
{
/**
* #var \Sumedia\Switcher\Block\Adminhtml\System\Config\Form\Field\Group
*/
protected $renderer;
public function __construct(
Context $context,
array $data = [])
{
parent::__construct($context, $data);
}
public function getRenderer()
{
if(!$this->renderer){
$this->renderer = $this->getLayout()->createBlock(
'Sumedia\Switcher\Block\Adminhtml\System\Config\Form\Field\Group',
'',['data' => ['is_renderer_to_js_template' => true]]);
}
return $this->renderer;
}
protected function _prepareToRender()
{
$renderer = $this->getRenderer();
$this->addColumn('store',[
'label' => __('Store'),
'renderer'=>$renderer
]);
$this->addColumn('name',[
'label' => __('Name')
]);
$this->_addAfter = false;
$this->_addButtonLabel = __('Add');
parent::_prepareToRender();
}
protected function _prepareArrayRow(\Magento\Framework\DataObject $row)
{
$store = $row->getStore();
$options = array();
if($store){
$options['option_'.$this->getRenderer()->calcOptionHash($store)] = 'selected="selected"';
}
$row->setData('option_extra_attrs',$options);
}
public function renderCellTemplate($columnName)
{
if($columnName == 'store'){
$this->_columns[$columnName]['class'] = 'input-text required-entry validate-number';
$this->_columns[$columnName]['style'] = 'width:50px';
}
return parent::renderCellTemplate($columnName);
}
}
/app/code/Sumedia/Switcher/Model/Adminhtml/System/Config/Groupmap.php
<?php
namespace Sumedia\Switcher\Model\Adminhtml\System\Config;
use Magento\Framework\App\Config\Value;
class Groupmap extends Value {
public function beforeSave(){
$data = array();
$value = $this->getValue();
if(is_array($value)) {
foreach($value AS $_data){
if(!isset($_data['store']) || !isset($_data['name'])){
continue;
}
$id = uniqid();
$data[$id] = array('store' => $_data['store'],'name' => $_data['name']);
}
}
$this->setValue(serialize($data));
return $this;
}
public function afterLoad(){
$value = #unserialize($this->getValue());
if (is_array($value)) {
$data = array();
foreach($value AS $id => $_data){
$data[$id] = array('store' => $_data['store'], 'name' => $_data['name']);
}
$this->setValue($data);
}
return $this;
}
}
/app/code/Sumedia/Switcher/etc/adminhtml/system.xml
<?xml version="1.0" encoding="UTF-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="sumedia" translate="label" sortOrder="100">
<label>Sumedia</label>
</tab>
<section id="sumedia_switcher" translate="label" sortOrder="100" showInDefault="1" showInStore="1" showInWebsite="1">
<class>seperator-top</class>
<label>Switcher</label>
<tab>sumedia</tab>
<resource>Sumedia_Switcher::config</resource>
<group id="general" translate="label" type="text" sortOrder="10" showInWebsite="1" showInStore="1" showInDefault="1">
<label>General</label>
<field id="enabled" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
</group>
<group id="store_switch" translate="label" type="text" sortOrder="10" showInWebsite="1" showInStore="1" showInDefault="1">
<label>Store Switch</label>
<field id="enabled" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="use_mapping" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
<label>Use Mapping</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="mapping" translate="label comment tooltip" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Mapping</label>
<frontend_model>Sumedia\Switcher\Block\Adminhtml\System\Config\Form\Field\Groupmap</frontend_model>
<backend_model>Sumedia\Switcher\Model\Adminhtml\System\Config\Groupmap</backend_model>
</field>
</group>
</section>
</system>
</config>
I see that you have reload all needed methods with your data, but there you have an issue in getRenderer method in Sumedia\Switcher\Block\Adminhtml\System\Config\Form\Field\Groupmap class, try to change 'is_renderer_to_js_template' to 'is_render_to_js_template'
I had the same issue during development custom module couple days ago and found a solution only after reviewing magento's core code (catalog inventory module's code) =)
I created a module in path app/code/Smartshore/Subscription
I want to create a route that displays a form and save data in it. I have the code but I don't know what is missing: Please see the below code:
Smartshore/Subscription/etc/frontend/routes.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="subscription" frontName="subscription">
<module name="Smartshore_Subscription" />
</route>
</router>
</config>
Smartshore/Subscription/Controller/Index/Index.php
<?php
namespace Smartshore\Subscription\Controller\Index;
use \Magento\Framework\App\Action\Action;
class Index extends Action
{
/** #var \Magento\Framework\View\Result\Page */
protected $resultPageFactory;
/**
* #param \Magento\Framework\App\Action\Context $context
*/
public function __construct(\Magento\Framework\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $resultPageFactory)
{
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
}
/**
* Subscription Index, shows a list of subscriptions
*
* #return \Magento\Framework\View\Result\PageFactory
*/
public function execute()
{
return $this->resultPageFactory->create();
}
}
Smartshore/Subscription/Controller/Index/Add.php
<?php
namespace Smartshore\Subscription\Controller;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
class Add extends Action
{
protected $resultPageFactory;
public function __construct(Context $context, PageFactory $pageFactory)
{
$this->resultPageFactory = $pageFactory;
parent::__construct($context);
}
public function execute()
{
$resultPage = $this->resultPageFactory->create();
return $resultPage;
}
}
Smartshore/Subscription/Controller/Index/Result.php
<?php
namespace Smartshore\Subscription\Controller;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\View\Element\Messages;
use Magento\Framework\View\Result\PageFactory;
class Result extends Action
{
/** #var PageFactory $resultPageFactory */
protected $resultPageFactory;
/**
* Result constructor.
* #param Context $context
* #param PageFactory $pageFactory
*/
public function __construct(Context $context, PageFactory $pageFactory)
{
$this->resultPageFactory = $pageFactory;
parent::__construct($context);
}
/**
* The controller action
*
* #return \Magento\Framework\View\Result\Page
*/
public function execute()
{
$number = $this->getRequest()->getParam('number');
$resultPage = $this->resultPageFactory->create();
/** #var Messages $messageBlock */
$messageBlock = $resultPage->getLayout()->createBlock(
'Magento\Framework\View\Element\Messages',
'answer'
);
if (is_numeric($number)) {
$messageBlock->addSuccess($number . ' times 2 is ' . ($number * 2));
}else{
$messageBlock->addError('You didn\'t enter a number!');
}
$resultPage->getLayout()->setChild(
'content',
$messageBlock->getNameInLayout(),
'answer_alias'
);
return $resultPage;
}
}
Smartshore/Subscription/view/frontend/layout/subscription_index_add.xml
<?xml version="1.0"?>
<page layout="2columns-left" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<title>New Subscription</title>
</head>
<body>
<referenceBlock name="navigation.sections" remove="true" />
<referenceContainer name="content">
<block class="Magento\Framework\View\Element\Template" name="subscriptionform.add" template="Smartshore_Subscription::form.phtml"/>
</referenceContainer>
</body>
</page>
Smartshore/Subscription/view/frontend/layout/subscription_index_result.xml
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<title>Result</title>
</head>
</page>
I have other files as well. But I have shown here files respect to my form and its submission.
When I enter /subscription/index/add I get No route found.
What is the problem?
Check namespaces in Add and Result files/classes. It should be namespace Smartshore\Subscription\Controller\Index
I'm trying to deserialise the following XML:
<?xml version="1.0" encoding="utf-8"?>
<customers>
<customer>
<name>..</name>
<address>..</address>
<email>..</email>
<website>..</website>
</customer>
<customer>
...
</customer>
<customers>
I have made the following entity:
use JMS\Serializer\Annotation as JMS;
class customers
{
public function __construct()
{
$this->customers = new ArrayCollection();
}
/**
* #JMS\Type("ArrayCollection<MyBundle\Entity\customer>")
* #JMS\XmlList(entry="customer")
*/
public $customers;
}
This is the second entity:
use JMS\Serializer\Annotation as JMS;
class customer
{
/**
* #JMS\Type("string")
*/
public $name;
/**
* #JMS\Type("string")
*/
public $address;
/**
* #JMS\Type("string")
*/
public $email;
/**
* #JMS\Type("string")
*/
public $website;
}
Then I use the following code in the controller to serialise:
$serializer = $this->get('jms_serializer');
$customers = $serializer->deserialize($inputStr, 'MyBundle\Entity\customers', 'xml');
Only the object keeps up getting empty?
Try changing, in class Customers, the annotation to #JMS\XmlList(inline=true, entry="customer")
So, the problem with my fresh installation of Symfony 2.7.9 is that SonataUserBundle didn't update their dependencies and I'm stuck with a buggy bundle (I already tried to upgrade to a greater version without success).
Here's the error :
Attempted to call an undefined method named "setCurrentUri" of class "Knp\Menu\MenuItem".Did you mean to call "setCurrent"?
Which is thrown in the Sonata/Userbundle/Block/ProfileMenuBlockService.php at line 91
$menu->setCurrentUri($settings['current_uri']);
Therefore, I want to overload this file in my Application/Sonata/UserBundle but I don't understand why it won't work.
Here's what I did :
I copied the file responsible under Application/Sonata/UserBundle/Block/
<?php
namespace Application\Sonata\UserBundle\Block;
use Knp\Menu\ItemInterface;
use Knp\Menu\Provider\MenuProviderInterface;
use Sonata\AdminBundle\Validator\ErrorElement;
use Sonata\BlockBundle\Block\BlockContextInterface;
use Sonata\BlockBundle\Block\Service\MenuBlockService;
use Sonata\BlockBundle\Model\BlockInterface;
use Sonata\UserBundle\Menu\ProfileMenuBuilder;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Class ProfileMenuBlockService
*
* #package Sonata\UserBundle\Block
*
* #author Hugo Briand <briand#ekino.com>
*/
class ProfileMenuBlockService extends MenuBlockService
{
/**
* #var ProfileMenuBuilder
*/
private $menuBuilder;
/**
* Constructor
*
* #param string $name
* #param EngineInterface $templating
* #param MenuProviderInterface $menuProvider
* #param ProfileMenuBuilder $menuBuilder
*/
public function __construct($name, EngineInterface $templating, MenuProviderInterface $menuProvider, ProfileMenuBuilder $menuBuilder)
{
parent::__construct($name, $templating, $menuProvider, array());
$this->menuBuilder = $menuBuilder;
}
/**
* {#inheritdoc}
*/
public function getName()
{
return 'User Profile Menu';
}
/**
* {#inheritdoc}
*/
public function setDefaultSettings(OptionsResolverInterface $resolver)
{
parent::setDefaultSettings($resolver);
$resolver->setDefaults(array(
'cache_policy' => 'private',
'menu_template' => "SonataBlockBundle:Block:block_side_menu_template.html.twig",
));
}
/**
* {#inheritdoc}
*/
protected function getMenu(BlockContextInterface $blockContext)
{
$settings = $blockContext->getSettings();
$menu = parent::getMenu($blockContext);
if (null === $menu || "" === $menu) {
$menu = $this->menuBuilder->createProfileMenu(
array(
'childrenAttributes' => array('class' => $settings['menu_class']),
'attributes' => array('class' => $settings['children_class']),
)
);
$menu->setCurrent($settings['current_uri']); // The corrected line
}
return $menu;
}
}
As it didn't worked, I also overloaded the configuration file Sonata/UserBundle/Resources/config/block.xml :
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="sonata.user.block.menu" class="Application\Sonata\UserBundle\Block\ProfileMenuBlockService">
<tag name="sonata.block" />
<argument>sonata.user.block.menu</argument>
<argument type="service" id="templating" />
<argument type="service" id="knp_menu.menu_provider" />
<argument type="service" id="sonata.user.profile.menu_builder" />
</service>
<service id="sonata.user.block.account" class="Sonata\UserBundle\Block\AccountBlockService">
<tag name="sonata.block" />
<argument>sonata.user.block.account</argument>
<argument type="service" id="templating" />
<argument type="service" id="security.context" />
</service>
</services>
</container>
Do you see anything that I am missing ?
Not to mention that I already cleared the cache and even rebooted my computer.
Thanks by advance.
After pavlovich suggestion, I tried again this solution which failed before and failed once again. Then, I noticed the comment of the correct answer and tried it too : it failed as I was already using sonata-project/user-bundle 2.2.5. I then used the following command :
composer require composer require knplabs/knp-menu ~1.1 knplabs/knp-menu-bundle ~1.1 sonata-project/user-bundle 2.2.4
Which worked but I got another error with a missing dependency for a sonata service. I therefore rolled back to sonata-project/user-bundle 2.2.5, which didn't update the knplabs/*-bundle versions.
To my own surprise, it actually resolved my problem.