Custom grid with modifications in backend of Magento 2 - php

I’m newbie Magento2 developer.
Now I’m making one small module and I’m stuck in one place.
I built admin grid with founded example and here is my di.xml:
<preference for="Magento\Catalog\Model\Product" type="Vendor\Module\Model\Product" />
<virtualType name="Vendor\Module\Model\ResourceModel\Grid\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult">
<arguments>
<argument name="mainTable" xsi:type="string">vendor_module</argument>
<argument name="resourceModel" xsi:type="string">Vendor\Module\Model\ResourceModel\Grid</argument>
</arguments>
</virtualType>
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="grid_record_grid_list_data_source" xsi:type="string">Vendor\Module\Model\ResourceModel\Grid\Grid\Collection</item>
</argument>
</arguments>
</type>
And also I use layout XML file with hardcoded columns inside:
...
<column name="customer" >
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">false</item>
<item name="label" xsi:type="string" translate="true">Customer</item>
</item>
</argument>
</column>
...
My table has columns like: product id, customer id, price, status
And my questions are:
How do I transform customer id to first+last name?
Column “status” has 3 different states (0, 1 and 2) - how do I convert them to human-readable words? (undefined, good, bad)
How to add to same grid another column for example $price + 10%?

Within the component XML you can define a UI class to assist in displaying custom/readable data within Magento 2. There are a number of examples within core, such as the thumbnail being displayed on the Catalog grid view.
Using that as an example, here's the column definition within catalog/view/adminhtml/ui_component/product_listing.xml:
<column name="thumbnail" class="Magento\Catalog\Ui\Component\Listing\Columns\Thumbnail">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/thumbnail</item>
<item name="add_field" xsi:type="boolean">true</item>
<item name="sortable" xsi:type="boolean">false</item>
<item name="altField" xsi:type="string">name</item>
<item name="has_preview" xsi:type="string">1</item>
<item name="label" xsi:type="string" translate="true">Thumbnail</item>
<item name="sortOrder" xsi:type="number">20</item>
</item>
</argument>
</column>
As you can see there are several arguments which can be passed to the column definition, including a component which depends on the type of data you're trying to display. In this case, it is a thumbnail. Reviewing that JS file reveals that it is logic to pull out the data being set in the below method to be displayed as the actual thumbnail. This is not necessarily a requirement.
Within the defined class on the column tag, you see Magento\Catalog\Ui\Component\Listing\Columns\Thumbnail. This is a class which defines helper methods for the way the data is to be displayed, as well as parsing the data to be displayed in such a way that the defined column component can properly render it.
Pay close attention to the method within that class, prepareDataSource:
/**
* Prepare Data Source
*
* #param array $dataSource
* #return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
$fieldName = $this->getData('name');
foreach ($dataSource['data']['items'] as & $item) {
$product = new \Magento\Framework\DataObject($item);
$imageHelper = $this->imageHelper->init($product, 'product_listing_thumbnail');
$item[$fieldName . '_src'] = $imageHelper->getUrl();
$item[$fieldName . '_alt'] = $this->getAlt($item) ?: $imageHelper->getLabel();
$item[$fieldName . '_link'] = $this->urlBuilder->getUrl(
'catalog/product/edit',
['id' => $product->getEntityId(), 'store' => $this->context->getRequestParam('store')]
);
$origImageHelper = $this->imageHelper->init($product, 'product_listing_thumbnail_preview');
$item[$fieldName . '_orig_src'] = $origImageHelper->getUrl();
}
}
return $dataSource;
}
You would use this method to format the data you're displaying into the format you need.
For instance, the way Price is displayed on the catalog grid (formatted to the proper currency) through their defined column class is:
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
$store = $this->storeManager->getStore(
$this->context->getFilterParam('store_id', \Magento\Store\Model\Store::DEFAULT_STORE_ID)
);
$currency = $this->localeCurrency->getCurrency($store->getBaseCurrencyCode());
$fieldName = $this->getData('name');
foreach ($dataSource['data']['items'] as & $item) {
if (isset($item[$fieldName])) {
$item[$fieldName] = $currency->toCurrency(sprintf("%f", $item[$fieldName]));
}
}
}
return $dataSource;
}
I hope this helps make it clear how to format data in columns on grids.

Related

Sulu CMS - Visiblity condition from underlying value of resource single selection

I have three entities Provision, Gatekeeper and Data.
class Provision {
private Gatekeeper $keeper; // ManyToOne
private Collection $points; // ManyToMany
}
class Gatekeeper {
private int $id;
private string $name;
private bool $allowData;
}
class Data {
private int $value;
}
All entities have REST-CRUD logic implemented. In order to edit the Provision entity I've added configurations to the sulu_admin.yaml:
sulu_admin:
field_type_options:
selection:
data_selection:
default_type: 'list_overlay'
resource_key: 'data'
types:
list_overlay:
adapter: 'table'
list_key: 'data'
display_properties: ['value']
icon: 'su-plus'
label: 'app.data'
overlay_title: 'app.action.select.data'
single_selection:
single_gatekeeper_selection:
default_type: 'single_select'
resource_key: 'gatekeepers'
types:
single_select:
display_property: 'name'
id_property: 'id'
overlay_title: 'app.action.select.gatekeeper'
Now, in the form configuration file for Provision entities, I want to include a visibleCondition for the data list:
<form xmlns="http://schemas.sulu.io/template/template"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.sulu.io/template/form-1.0.xsd">
<key>provision_details</key>
<properties>
<property name="gatekeeper" type="single_gatekeeper_selection" mandatory="true">
<meta>
<title>app.gatekeeper</title>
</meta>
</property>
<property name="data" type="data_selection" visibleCondition="!!gatekeeper && gatekeeper.allowData">
<meta>
<title>app.data</title>
</meta>
</property>
</properties>
</form>
Is there any possibility to implement this behaviour? I've already tried adding a custom ConditionDataProvider, but the main problem is that the visibleCondition is evaluated at Field-level while the only place I could pull the required data from would be the ResourceListStore in the SingleSelect.
This is not possible you only have access to the raw form data. For a single selection the gatekeeper value contains only the id of the selected element. Not any data of the element behind this element.
All data you have access to it, you see in save (POST/PUT) request of the form.
The only thing which is possible would be using resource_store_properties_to_request to filter the result of your data_selection by the gatekeeper id, this requires that your data api need to keep the gatekeeper parameter in mind to filter by it:
<properties>
<property name="gatekeeper" type="single_gatekeeper_selection" mandatory="true">
<meta>
<title>app.gatekeeper</title>
</meta>
</property>
<property name="data" type="data_selection">
<meta>
<title>app.data</title>
</meta>
<params>
<param name="resource_store_properties_to_request" type="collection">
<param name="gatekeeper" value="gatekeeper"/>
</param>
</params>
</property>
</properties>

Sulu CMF - Pass parameter to autocomplete field in FormOverlayList add form

This question is a followup.
I have two entities DataSet and DataGroup.
class DataSet {
string $name;
Collection $groups; // Collection<int, DataGroup>
}
class DataGroup {
string $name;
DataSet $dataSet;
?DataGroup $nextGroup; // Condition: $nextGroup !== $this && $nextGroup->dataSet === $this->dataSet
}
The property DataGroup::nextGroup may refer to any other DataGroup entity associated with the same DataSet.
I want to create a CRUD form where I can add, edit and remove DataSet entities. In this DataSet form, I also want to include a tab where I can CRUD DataGroup entities associated with the current DataSet.
I have created list metadata data_sets.xml and data_groups.xml, as well as form metadata data_set.xml and data_group.xml.
<!-- lists/data_sets.xml -->
<list xmlns="http://schemas.sulu.io/list-builder/list">
<key>data_sets</key>
<properties>
<property name="name" visibility="always" searchability="yes">
<field-name>name</field-name>
<entity-name>App\Entity\DataSet</entity-name>
</property>
</properties>
</list>
<!-- lists/data_groups.xml -->
<list xmlns="http://schemas.sulu.io/list-builder/list">
<key>data_groups</key>
<properties>
<property name="name" visibility="always" searchability="yes">
<field-name>name</field-name>
<entity-name>App\Entity\DataSet</entity-name>
</property>
<property name="dataSet" visibility="always">
<field-name>name</field-name>
<entity-name>App\Entity\DataSet</entity-name>
<joins>
<join>
<entity-name>App\Entity\DataSet</entity-name>
<field-name>App\Entity\DataGroup.dataSet</field-name>
</join>
</joins>
</property>
</properties>
</list>
<!-- forms/data_set.xml -->
<form xmlns="http://schemas.sulu.io/template/template"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.sulu.io/template/template
http://schemas.sulu.io/template/form-1.0.xsd"
>
<key>data_set</key>
<properties>
<property name="name" type="text_line" mandatory="true">
<params>
<param name="headline" value="true"/>
</params>
</property>
</properties>
</form>
<!-- forms/data_group.xml -->
<form xmlns="http://schemas.sulu.io/template/template"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.sulu.io/template/template
http://schemas.sulu.io/template/form-1.0.xsd"
>
<key>data_group</key>
<properties>
<property name="name" type="text_line" mandatory="true">
<params>
<param name="headline" value="true"/>
</params>
</property>
<property name="nextGroup" type="single_data_group_selection">
<params>
<param name="type" value="auto_complete"/>
<param name="resource_store_properties_to_request" type="collection">
<param name="dataSetId" value="id"/>
</param>
</params>
</property>
</properties>
</form>
I have also configured sulu_admin.yaml for REST routes and the custom single selection:
sulu_admin:
resources:
data_sets:
routes:
list: app.get_data_sets
detail: app.get_data_set
data_groups:
routes:
list: app.get_data_groups
details: app.get_data_group
field_type_options:
single_selection:
single_data_group_selection:
default_type: 'auto_complete'
resource_key: 'data_groups'
types:
auto_complete:
display_property: 'name'
search_properties:
- 'name'
I implemented two REST controllers for both entities. DataSetController is built as of the Docs, while DataGroupController has a small extension for the list route:
class DataGroupController implements ClassResourceInterface
{
public function cgetAction(int $dataSetId, Request $request): Response
{
// ... Init field descriptors and execute listBuilder
$list = new ListRepresentation(
$listResponse,
'data_groups',
\array_merge(['dataSetId' => $dataSetId], $request->query->all()), // add DataSet ID
$listBuilder->getCurrentPage(),
$listBuilder->getLimit(),
$listBuilder->count()
);
// ... handle view
}
// ...
}
Finally, I implemented a custom Admin class like explained in the Docs.
class DataAdmin extends Admin
{
public const DATA_SET_DETAILS = 'data_set_details';
public const DATA_SET_GROUPS = 'data_set_groups';
public const DATA_SET_LIST = 'app.data_sets_list';
public const DATA_SET_ADD_FORM = 'app.data_set_add_form';
public const DATA_SET_ADD_FORM_DETAILS = 'app.data_set_add_form.details';
public const DATA_SET_EDIT_FORM = 'app.data_set_edit_form';
public const DATA_SET_EDIT_FORM_DETAILS = 'app.data_set_edit_form.details';
public const DATA_SET_EDIT_FORM_GROUPS = 'app.data_set_edit_form.groups';
public function configureViews(ViewCollection $viewCollection): void
{
// Add DataSet list view
// Add DataSet add form view
// Add DataSet edit form view (details)
/*
* Custom second DataSet edit form tab for DataGroup CRUDding
*/
$groupsFormOverlayList = $this->viewBuilderFactory
->createFormOverlayListViewBuilder(self::DATA_SET_EDIT_FORM_GROUPS, '/groups')
->setResourceKey(self::DATA_SET_GROUPS)
->setListKey(self::DATA_SET_GROUPS)
->addListAdapters(['table'])
->addRouterAttributesToListRequest(['id' => 'dataSetId'])
->setFormKey(self::DATA_SET_GROUPS)
->addRouterAttributesToFormRequest(['id' => 'dataSetId'])
->setTabTitle('app.data_groups')
->addToolbarActions([
new ToolbarAction('sulu_admin.add'),
new ToolbarAction('sulu_admin.delete')
])
;
$viewCollection->add($groupsFormOverlayList->setParent(self::DATA_SET_EDIT_FORM));
}
}
As of now
I can perform all CRUD operations on DataSet entities.
I can list and remove DataGroup entities belonging to a DataSet.
When trying to add a new DataGroup, I can't perform autocomplete search for nextGroup because the dataSetId parameter is not passed to the field, forbidding the ResourceRequester to perform the REST request.
How do I pass dataSetId to fields of the "New DataGroup" form in order to search for matching entities only?
Thanks a lot for the detailed description! Unfortunately, I am afraid that it is not possible to implement what you are trying to do while using the built-in autocomplete component at the moment.
The resource_store_properties_to_request param reads the values from the data of the form that renders the autocomplete component. If you are creating a new DataGroup entity, the data of the form is empty (because there is now existing data for a new entity) and therefore the resource_store_properties_to_request param is not able to read a dataSetId value.
I am sorry to say, but I think you would need to implement a custom autocomplete field-type that reads the dataSetId value from the current url to achieve the desired functionality. If you are interested in doing this, I would recommend to have a look at the basic field-type example in the sulu-demo repository.

Add custom button on add/edit product page

https://i.stack.imgur.com/uHZ33.png
How to add a custom button, in add/edit product page (magento2). I need to open popup on clicking of the button.
The product form is generated via ui-components.
The ui component name for product form is view/adminhtml/ui_component/product_form.xml.
You need to create a file with the same name and path in your own module with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="buttons" xsi:type="array">
<item name="button-unique-identifier-here" xsi:type="string">[Namespace]\[Module]\Block\Adminhtml\Product\Edit\Button\CustomButton</item>
</item>
</argument>
</form>
Then create the class [Namespace]\[Module]\Block\Adminhtml\Product\Edit\Button\CustomButton in the file [Namespace]/[Module]/Block/Adminhtml/Product/Edit/Button/CustomButton.php
<?php
namespace [Namespace]\[Module]\Block\Adminhtml\Product\Edit\Button;
use Magento\Catalog\Block\Adminhtml\Product\Edit\Button\Generic;
class CustomButton extends Generic
{
public function getButtonData()
{
return [
'label' => __('Your button label here'),
'on_click' => "alert('it works')",
'sort_order' => 100
];
}
}
Your ui component file should be merged with the main file and your buttons should appear among the other buttons.
Check these changes after cache flush

Magento 2: Add Grid Column to Admin Sales Order Without Adding Column to Database?

I want to add a column to sales order grid on magento admin dashboard. But the value of the column is from some process, not from database. Is that possible? And how to do it? Thanks in advance.
You can add a column to the admin grid by adding a file called view/adminhtml/ui_component/sales_order_grid.xml to your custom module with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<listingToolbar name="listing_top"/>
<columns name="sales_order_columns">
<column name="order_reference" class="Vendor\Example\Ui\Component\Listing\Column\Example">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataType" xsi:type="string">text</item>
<item name="visible" xsi:type="boolean">true</item>
<item name="label" xsi:type="string" translate="true">Example Column</item>
</item>
</argument>
</column>
</columns>
</listing>
Your Example.php-file should extend Magento\Ui\Component\Listing\Columns\Column and have a prepareDataSource()-method to populate the data:
/**
* #param array $dataSource
* #return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as & $item) {
$item[$this->getData('name')] = 'Something'
}
}
return $dataSource;
}
Note that if you want to add sort- and filter-options you need to add some other adjustments, but that depends on what kind of data you want to show in the column.

How to add MassAction on Order Grid in Magento2?

I want to add a MassAction in Order Grid in Magento2.
I add xml in view/adminhtml/ui_component/sales_order_grid.xml.By adding this XML MassAction is show in dropdown.But when I select the MassAction I did not get the order ids in post request.
<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<listingToolbar name="listing_top">
<massaction name="listing_massaction">
<action name="mass_order_assigntrackingnumber">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">order_assigntrackingnumber</item>
<item name="label" xsi:type="string" translate="true">Assign Tracking Number</item>
<item name="url" xsi:type="url" path="helloworld/orderprocessing/"/>
</item>
</argument>
</action>
</massaction>
</listingToolbar>
</listing>
I got when I print the POST request :-
Array
(
[key] => 2c7824dfa98ff4e9f550d2f4d07e696c650fbb48448ac3f8a8377f1c793e9294
[excluded] => false
[filters] => Array
(
[placeholder] => true
)
[search] =>
[namespace] => sales_order_grid
[form_key] => hvUs2LDFHbjJN3zY
)
Mass Action Controller
<?php
namespace Inchoo\Helloworld\Controller\Adminhtml\Orderprocessing;
class Index extends \Magento\Backend\App\Action{
public function execute(){
$data = $this->getRequest()->getParams();
echo "<pre>";print_r($data); die;
die("Hello");
}
}
?>
Please help me in to find out how to get order ids via custom MassAction in MassAction controller in Magento2 ?
Mass Action Controller
Update code by this:
<?php
namespace Inchoo\Helloworld\Controller\Adminhtml\Orderprocessing;
class Index extends \Magento\Sales\Controller\Adminhtml\Order\AbstractMassAction {
protected function massAction(AbstractCollection $collection){
//your code here
//Here you will get collection
}
}
Reference code: /var/www/html/mage2/vendor/magento/module-sales/Controller/Adminhtml/Order/MassCancel.php

Categories