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) =)
Related
I'm trying to use my custom shipping method while I import orders using a console command.
Everything works fine, except the shipping method not showing in the back-end, when I check the order afterwards:
Screenshot
The module base structure and shipping method was generated using mage2gen.com and works in the front-end and back-end if I manually create orders.
Interestingly the shipping method is set correctly if I use $shippingAddress->setShippingMethod('flatrate_flatrate'); after collecting the shipping rates, but I couldn't get it to work with any other shipping method or my custom one.
Help would be greatly appreciated! Please let me know if I should add some more code.
Thanks! (pretty new to Mage2)
system.xml:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<section id="carriers" sortOrder="1000" showInWebsite="1" showInStore="1" showInDefault="1" translate="label">
<group id="hellocash" sortOrder="10" showInWebsite="1" showInStore="1" showInDefault="1" translate="label">
<label>HelloCash</label>
<field id="active" type="select" sortOrder="10" showInWebsite="1" showInStore="1" showInDefault="1" translate="label">
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="name" type="text" sortOrder="20" showInWebsite="1" showInStore="1" showInDefault="1" translate="label">
<label>Method Name</label>
</field>
<field id="price" type="text" sortOrder="30" showInWebsite="1" showInStore="1" showInDefault="1" translate="label">
<label>Price</label>
<validate>validate-number validate-zero-or-greater</validate>
</field>
<field id="sort_order" type="text" sortOrder="40" showInWebsite="1" showInStore="1" showInDefault="1" translate="label">
<label>Sort Order</label>
</field>
<field id="title" type="text" sortOrder="50" showInWebsite="1" showInStore="1" showInDefault="1" translate="label">
<label>Title</label>
</field>
<field id="sallowspecific" type="select" sortOrder="60" showInWebsite="1" showInStore="1" showInDefault="1" translate="label">
<label>Ship to Applicable Countries</label>
<frontend_class>shipping-applicable-country</frontend_class>
<source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model>
</field>
<field id="specificcountry" type="multiselect" sortOrder="70" showInWebsite="1" showInStore="1" showInDefault="1" translate="label">
<label>Ship to Specific Countries</label>
<can_be_empty>1</can_be_empty>
<source_model>Magento\Directory\Model\Config\Source\Country</source_model>
</field>
<field id="specificerrmsg" type="textarea" sortOrder="80" showInWebsite="1" showInStore="1" showInDefault="1" translate="label">
<label>Displayed Error Message</label>
</field>
</group>
</section>
</system>
</config>
Carrier Model:
<?php
/**
* Copyright © Dan All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Dan\HelloCash\Model\Carrier;
use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Shipping\Model\Rate\Result;
class HelloCash extends \Magento\Shipping\Model\Carrier\AbstractCarrier implements
\Magento\Shipping\Model\Carrier\CarrierInterface
{
protected $_code = 'hellocash';
protected $_isFixed = true;
protected $_rateResultFactory;
protected $_rateMethodFactory;
/**
* Constructor
*
* #param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* #param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
* #param \Psr\Log\LoggerInterface $logger
* #param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory
* #param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory
* #param array $data
*/
public function __construct(
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,
\Psr\Log\LoggerInterface $logger,
\Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory,
\Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory,
array $data = []
) {
$this->_rateResultFactory = $rateResultFactory;
$this->_rateMethodFactory = $rateMethodFactory;
parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
}
/**
* {#inheritdoc}
*/
public function collectRates(RateRequest $request)
{
if (!$this->getConfigFlag('active')) {
return false;
}
$shippingPrice = $this->getConfigData('price');
$result = $this->_rateResultFactory->create();
if ($shippingPrice !== false) {
$method = $this->_rateMethodFactory->create();
$method->setCarrier($this->_code);
$method->setCarrierTitle($this->getConfigData('title'));
$method->setMethod($this->_code);
$method->setMethodTitle($this->getConfigData('name'));
if ($request->getFreeShipping() === true) {
$shippingPrice = '0.00';
}
$method->setPrice($shippingPrice);
$method->setCost($shippingPrice);
$result->append($method);
}
return $result;
}
/**
* getAllowedMethods
*
* #return array
*/
public function getAllowedMethods()
{
return [$this->_code => $this->getConfigData('name')];
}
}
Import order console command:
<?php
/**
* Copyright © DAN All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Dan\HelloCash\Console\Command;
use Dan\HelloCash\Api\Data\SyncLogInterfaceFactory;
use Dan\HelloCash\Api\HelloCash as HelloCashApi;
use Dan\HelloCash\Enum\SyncLogType;
use Dan\HelloCash\Exception\SyncException;
use Dan\HelloCash\Exception\SyncOrderException;
use Dan\HelloCash\Model\SyncLogRepository;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\ProductFactory;
use Magento\Config\Model\Config;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Framework\App\State;
use Magento\Framework\DB\Transaction;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\View\Result\PageFactory;
use Magento\Quote\Model\Quote\Address\Rate as ShippingRate;
use Magento\Quote\Model\QuoteManagement;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\Email\Sender\OrderSender;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Customer\Model\Customer\Interceptor as CustomerInterceptor;
use Magento\Customer\Model\CustomerFactory;
use Magento\Framework\App\Area;
use Magento\Framework\Encryption\Encryptor;
use Magento\Quote\Model\QuoteFactory;
use Magento\Store\Model\StoreManagerInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Magento\Sales\Model\Service\InvoiceService;
use Magento\Sales\Model\Order\Email\Sender\InvoiceSender;
class SyncOrders extends SyncCommand
{
const NAME_ARGUMENT = 'name';
const NAME_OPTION = 'option';
protected CustomerFactory $customerFactory;
protected CustomerInterceptor $customer;
protected OutputInterface $output;
protected StoreInterface $store;
protected StoreManagerInterface $storeManager;
protected QuoteFactory $quoteFactory;
/**
* #var PageFactory
*/
protected PageFactory $resultPageFactory;
/**
* #var OrderRepositoryInterface
*/
protected OrderRepositoryInterface $orderRepository;
/**
* #var ProductRepositoryInterface
*/
protected ProductRepositoryInterface $productRepository;
/**
* #var ProductRepositoryInterface
*/
protected $customerRepository;
/**
* #var QuoteFactory
*/
protected QuoteFactory $quote;
/**
* #var QuoteManagement
*/
protected QuoteManagement $quoteManagement;
/**
* #var OrderSender
*/
protected OrderSender $orderSender;
protected ProductFactory $productFactory;
protected ObjectManagerInterface $objectManager;
protected ShippingRate $shippingRate;
protected InvoiceService $invoiceService;
protected Transaction $transaction;
protected InvoiceSender $invoiceSender;
private string $customerEmail;
private string $customerPassword;
private string $customerPhone;
protected function __construct(
HelloCashApi $api,
Config $config,
Encryptor $encryptor,
SyncLogRepository $syncLogRepository,
SyncLogInterfaceFactory $syncLogFactory,
CustomerFactory $customerFactory,
StoreManagerInterface $storeManager,
CustomerRepositoryInterface $customerRepository,
QuoteFactory $quote,
ProductFactory $productFactory,
ObjectManagerInterface $objectManager,
ShippingRate $shippingRate,
State $state,
Transaction $transaction
)
{
$this->customerFactory = $customerFactory;
$this->storeManager = $storeManager;
$this->customerRepository = $customerRepository;
$this->quote = $quote;
$this->productFactory = $productFactory;
$this->state = $state;
$this->objectManager = $objectManager;
$this->shippingRate = $shippingRate;
$this->transaction = $transaction;
parent::__construct($api, $config, $encryptor, $syncLogRepository, $syncLogFactory);
$this->customerEmail = (string)$this->config->getConfigDataValue('hello_cash_general/sync_order/customer_email');
$this->customerPassword = $this->encryptor->decrypt($this->config->getConfigDataValue('hello_cash_general/sync_order/customer_password'));
$this->customerPhone = (string)$this->config->getConfigDataValue('hello_cash_general/sync_order/customer_phone');
}
/**
* {#inheritdoc}
*/
protected function configure()
{
$this->setName('dan_hellocash:sync-orders');
$this->setDescription('Imports the HelloCash orders into your Magento shop.');
$this->setDefinition([
new InputArgument(self::NAME_ARGUMENT, InputArgument::OPTIONAL, 'Name'),
new InputOption(self::NAME_OPTION, '-a', InputOption::VALUE_NONE, 'Option functionality')
]);
parent::configure();
}
/**
* {#inheritdoc}
*/
protected function execute(
InputInterface $input,
OutputInterface $output
)
{
try {
$this->output = $output;
$this->state->setAreaCode(Area::AREA_FRONTEND);
$this->store = $this->storeManager->getStore();
$this->websiteId = $this->storeManager->getWebsite()->getId();
$this->createSyncLog(SyncLogType::ORDERS);
$ordersHellocash = $this->getOrders();
$this->syncLog->setTotal((int)$ordersHellocash->count);
$this->syncLogRepository->save($this->syncLog);
foreach ($ordersHellocash->invoices as $orderHellocash) {
try {
if ('1' === $orderHellocash->invoice_cancellation) {
continue;
}
$orderHellocash = $this->getOrder((int)$orderHellocash->invoice_id);
$this->createOrder($orderHellocash);
} catch (SyncOrderException $exception) {
$this->handleOrderError($orderHellocash, $exception);
} finally {
$this->syncLog->setProcessed($this->syncLog->getProcessed() + 1);
$this->syncLogRepository->save($this->syncLog);
}
}
$this->handleSuccess();
} catch (SyncException $exception) {
$this->handleFatalError('A problem occurred calling the HelloCash API. (status: ' . $exception->getStatusCode() . ', message: "' . $exception->getMessage() . '")');
} finally {
$this->finishSync();
}
}
protected function createOrder(\stdClass $orderHelloCash)
{
$this->createCustomer($orderHelloCash);
$quote = $this->quote->create();
$quote->setStore($this->store);
$quote->setCurrency();
/**
* Registered Customer
*/
$customer = $this->customerRepository->getById($this->customer->getId());
$quote->assignCustomer($customer);
foreach ($orderHelloCash->items as $item) {
$product = $this->productFactory
->create()
->loadByAttribute('hellocash_article_id', (int)$item->item_article_id);
if (!$product) {
throw new SyncOrderException('Product not found. (HelloCash ID: ' . $item->item_article_id . ', name: "' . $item->item_name . '")');
}
$product->setPrice((float)$item->item_price);
$quote->addProduct(
$product,
(float)$item->item_quantity
);
}
$addressData = $this->createAddressData($orderHelloCash->company);
$quote->getBillingAddress()->addData($addressData);
$quote->getShippingAddress()->addData($addressData);
$this->shippingRate
->setCode('hellocash_hellocash')
->getPrice();
$shippingAddress = $quote
->getShippingAddress()
->addShippingRate($this->shippingRate);
$shippingAddress->setCollectShippingRates(true);
$shippingAddress->collectShippingRates();
$shippingAddress->setShippingMethod('hellocash_hellocash');
$quote->setPaymentMethod('hellocash');
$quote->setInventoryProcessed(true);
$quote->save();
$quote->getPayment()->importData(['method' => 'hellocash']);
$quote->collectTotals()->save();
$quoteManagement = $this->objectManager->create(QuoteManagement::class);
$order = $quoteManagement->submit($quote);
$order
->setHellocashInvoiceId((int)$orderHelloCash->invoice_id)
->setEmailSent(1)
->setStatus('pending')
->save();
if (!$order->getEntityId()) {
throw new SyncOrderException('Failed to get the order ID after saving.');
}
$this->createInvoice($order);
$order
->setStatus('complete')
->save();
return $order;
}
protected function createCustomer(\stdClass $invoiceHellocash): CustomerInterceptor
{
if (isset($this->customer)) {
return $this->customer;
}
$this->verifyCustomerCredentials();
$this->customer = $this->customerFactory
->create()
->setWebsiteId($this->websiteId)
->setStore($this->store);
if (!$this->customer->loadByEmail($this->customerEmail)->getId()) {
$this->customer
->setFirstname('Cashier')
->setLastname('HelloCash')
->setEmail($this->customerEmail)
->setPassword($this->customerPassword);
$this->customer->save();
/**
* #TODO: Ask Daniel why this is not working.
*/
// $this->customerRepository->save($this->customer);
}
return $this->customer;
}
protected function createAddressData(\stdClass $companyHellocash): array
{
$address = [
'firstname' => $companyHellocash->name,
'lastname' => 'HelloCash',
'country_id' => 'AT',
'city' => $companyHellocash->city,
'street' => $companyHellocash->street,
//'region' => $companyHellocash->city,
'postcode' => $companyHellocash->postalCode,
'telephone' => $this->customerPhone,
// 'fax' => $companyHellocash->postalCode,
'save_in_address_book' => 1
];
return $address;
}
public function createInvoice($order)
{
$this->invoiceService = $this->objectManager->create(InvoiceService::class);
$invoice = $this->invoiceService
->prepareInvoice($order)
->register()
->save();
$this->transaction
->addObject($invoice)
->addObject($invoice->getOrder())
->save();
$this->invoiceSender = $this->objectManager->create(InvoiceSender::class);
$this->invoiceSender->send($invoice);
$order
->addCommentToStatusHistory(__('Notified customer about invoice creation #%1.', $invoice->getId()))
->setIsCustomerNotified(true)
->save();
return $invoice;
}
protected function verifyCustomerCredentials()
{
if (empty($this->customerEmail)) {
throw new SyncException('The customer email is not set in the module\'s admin configuration! Please set it.');
}
if (empty($this->customerPassword)) {
throw new SyncException('The customer password is not set in the module\'s admin configuration! Please set it.');
}
if (empty($this->customerPhone)) {
throw new SyncException('The customer phone number is not set in the module\'s admin configuration! Please set it.');
}
}
}
A conflicting badly written shipping plugin was the issue. So never mind and thanks to everyone who took the time to go through my code.
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.
I do have an entity in AppBundle/Entity which I generated me from database.
<?php
namespace AppBundle\Entity;
/*
*
* ExtensionSyliusShopUser
*/
class ExtensionSyliusShopUser
{/**
* Set user
*
* #param \AppBundle\Entity\SyliusShopUser $user
*
* #return ExtensionSyliusShopUser
*/
public function setUser(\AppBundle\Entity\SyliusShopUser $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \AppBundle\Entity\SyliusShopUser
*/
public function getUser()
{
return $this->user;
}
Now I want to change the setUser() & getUser() to:
Sylius\Component\Core\Model\ShopUser
If I am going to change #parm and the value in braces e.g.:
public function setUser(\Sylius\Component\Core\Model\ShopUser $user = null)
I get the error:
Expected value of type "AppBundle\Entity\SyliusShopUser" for association field "AppBundle\Entity\ExtensionSyliusShopUser#$user", got "Sylius\Component\Core\Model\ShopUser" instead.
How can I change it?
<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="AppBundle\Entity\ExtensionSyliusShopUser" table="extension_sylius_shop_user">
<indexes>
<index name="user_id" columns="user_id"/>
</indexes>
<id name="id" type="integer" column="id">
<generator strategy="IDENTITY"/>
</id>
<field name="wishlist" type="text" column="wishlist" length="65535" nullable="true">
<options>
<option name="fixed"/>
</options>
</field>
<many-to-one field="user" target-entity="SyliusShopUser" fetch="LAZY">
<join-columns>
<join-column name="user_id" referenced-column-name="id"/>
</join-columns>
</many-to-one>
</entity>
</doctrine-mapping>
You need to change the definition of your $user property (column). which is not shown in a code sample you provided.
In case you use annotations is should look like this:
/**
* #ORM\ManyToOne(targetEntity="Sylius\Component\Core\Model\ShopUser")
*/
private $user;
In case of XML config:
<many-to-one field="user" target-entity="Sylius\Component\Core\Model\ShopUser">
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 currently use a extention to display the zipcode and SKU inside the grid.
I want to change this, so that the SKU is replaced by my new attribute with code "dpn".
How should I expand my code?
CODE:
app/code/local/Atwix/ExtendedGrid/etc/config.xml
<?xml version="1.0"?>
<config>
<modules>
<Atwix_ExtendedGrid>
<version>1.0.1</version>
</Atwix_ExtendedGrid>
</modules>
<global>
<blocks>
<atwix_extendedgrid>
<class>Atwix_ExtendedGrid_Block</class>
</atwix_extendedgrid>
</blocks>
<helpers>
<atwix_extendedgrid>
<class>Atwix_ExtendedGrid_Helper</class>
</atwix_extendedgrid>
</helpers>
<models>
<atwix_extendedgrid>
<class>Atwix_ExtendedGrid_Model</class>
</atwix_extendedgrid>
<sales_resource>
<rewrite>
<order_grid_collection>Atwix_ExtendedGrid_Model_Resource_Sales_Order_Grid_Collection</order_grid_collection>
</rewrite>
</sales_resource>
</models>
</global>
<adminhtml>
<events>
<sales_order_grid_collection_load_before>
<observers>
<atwix_exgrid>
<model>atwix_extendedgrid/observer</model>
<method>salesOrderGridCollectionLoadBefore</method>
</atwix_exgrid>
</observers>
</sales_order_grid_collection_load_before>
</events>
<layout>
<updates>
<atwix_extendedgrid>
<file>atwix/extendedgrid.xml</file>
</atwix_extendedgrid>
</updates>
</layout>
</adminhtml>
</config>
app/code/local/Atwix/ExtendedGrid/Helper/Data.php
class Atwix_ExtendedGrid_Helper_Data extends Mage_Core_Helper_Abstract
{
/**
* parameters for addColumnAfter method
* #return array
*/
public function getSkusColumnParams()
{
return array(
'header' => 'SKU',
'index' => 'skus',
'type' => 'text',
'filter_condition_callback' => array('Atwix_ExtendedGrid_Model_Observer', 'filterSkus'),
);
}
public function getPostcodeColumnParams()
{
return array(
'header' => 'Postcode',
'index' => 'postcode',
'type' => 'text',
);
}
}
app/code/local/Atwix/ExtendedGrid/Model/Observer.php
class Atwix_ExtendedGrid_Model_Observer
{
/**
* Joins extra tables for adding custom columns to Mage_Adminhtml_Block_Sales_Order_Grid
* #param Varien_Object $observer
* #return Atwix_Exgrid_Model_Observer
*/
public function salesOrderGridCollectionLoadBefore($observer)
{
$collection = $observer->getOrderGridCollection();
$select = $collection->getSelect();
$select->joinLeft(array('payment' => $collection->getTable('sales/order_payment')), 'payment.parent_id=main_table.entity_id', array('payment_method' => 'method'));
$select->join(
array('address' => $collection->getTable("sales/order_address")),
'main_table.entity_id = address.parent_id AND address.address_type = "shipping"',
array('postcode')
);
$select->join('sales_flat_order_item', '`sales_flat_order_item`.order_id=`main_table`.entity_id', array('skus' => new Zend_Db_Expr('group_concat(`sales_flat_order_item`.sku SEPARATOR ", ")')));
$select->group('main_table.entity_id');
}
/**
* callback function used to filter collection
* #param $collection
* #param $column
* #return $this
*/
public function filterSkus($collection, $column)
{
if (!$value = $column->getFilter()->getValue()) {
return $this;
}
$collection->getSelect()->having(
"group_concat(`sales_flat_order_item`.sku SEPARATOR ', ') like ?", "%$value%");
return $this;
}
}
app/code/local/Atwix/ExtendedGrid/Model/Resource/Sales/Order/Grid/Collection.php
class Atwix_ExtendedGrid_Model_Resource_Sales_Order_Grid_Collection extends Mage_Sales_Model_Resource_Order_Grid_Collection
{
/**
* Get SQL for get record count
*
* #return Varien_Db_Select
*/
public function getSelectCountSql()
{
$countSelect = parent::getSelectCountSql();
if (Mage::app()->getRequest()->getControllerName() == 'sales_order') {
$countSelect->reset(Zend_Db_Select::GROUP);
$countSelect->reset(Zend_Db_Select::COLUMNS);
$countSelect->columns("COUNT(DISTINCT main_table.entity_id)");
$havingCondition = $countSelect->getPart(Zend_Db_Select::HAVING);
if (count($havingCondition)) {
$countSelect->where(
str_replace("group_concat(`sales_flat_order_item`.sku SEPARATOR ', ')", 'sales_flat_order_item.sku', $havingCondition[0])
);
$countSelect->reset(Zend_Db_Select::HAVING);
}
}
return $countSelect;
}
/**
* Init select
* #return Mage_Core_Model_Resource_Db_Collection_Abstract
*/
protected function _initSelect()
{
$this->addFilterToMap('store_id', 'main_table.store_id')
->addFilterToMap('created_at', 'main_table.created_at')
->addFilterToMap('updated_at', 'main_table.updated_at');
return parent::_initSelect();
}
}
app/design/adminhtml/default/default/layout/atwix/extendedgrid.xml
<layout>
<sales_order_grid_update_handle>
<reference name="sales_order.grid">
<action method="addColumnAfter">
<columnId>payment_method</columnId>
<arguments>
<header>Betaalmethode</header>
<index>payment_method</index>
<filter_index>payment.method</filter_index>
<type>text</type>
</arguments>
<after>shipping_name</after>
</action>
<action method="addColumnAfter">
<columnId>skus</columnId>
<arguments helper="atwix_extendedgrid/getSkusColumnParams" />
<after>payment_method</after>
</action>
<action method="addColumnAfter">
<columnId>postcode</columnId>
<arguments helper="atwix_extendedgrid/getPostcodeColumnParams" />
<after>skus</after>
</action>
</reference>
</sales_order_grid_update_handle>
<adminhtml_sales_order_grid>
<!-- apply layout handle defined above -->
<update handle="sales_order_grid_update_handle" />
</adminhtml_sales_order_grid>
<adminhtml_sales_order_index>
<!-- apply layout handle defined above -->
<update handle="sales_order_grid_update_handle" />
</adminhtml_sales_order_index>
</layout>