How does the CMS page admin form data provider gets its data?
-
08-10-2020 - |
Question
Long story short:
The CMS page admin form data provider class Magento\Cms\Model\Page\DataProvider
, more specific the method getData
always returns an array with a single element that contains the data for the page that I'm editing and the element key is the page id.
How does this happen? Where is the filter by page id applied?
Short story long:
At a first glance, what the method Magento\Cms\Model\Page\DataProvider::getData
returns is not that obvious.
In the method there is this line
$items = $this->collection->getItems();
and the collection member var is initialized in __construct
with
$this->collection = $pageCollectionFactory->create();`
Where $pageCollectionFactory
is an instance of Magento\Cms\Model\ResourceModel\Page\CollectionFactory
.
So, when I first looked, I thought "why the hell is it loading the full collection since I am editing only one item? This doesn't scale.".
Upon a closer inspection, I saw that the collection returns one single item. The one I am editing. This is good, but how does it work?
If I print the collection query echo $this->collection->getSelect()
I get back
SELECT `main_table`.* FROM `cms_page` AS `main_table` WHERE (`main_table`.`page_id` = '2')
where 2
is the id of the page that I am editing.
Now the curious part (if this was not curious enough) If I create my own entity, following the same guidelines for the data provider class, it works the same. I get back one single item.
This makes me think the filtering happens somewhere higher in the inheritance tree, but I have no idea where?
Note: Please don't explain how to make this work on my custom module. I know how to do that. I just need an explanation on how/why/where the magic happens.
Solution
/**
* {@inheritdoc}
*/
public function getDataSourceData()
{
$dataSource = [];
$id = $this->getContext()->getRequestParam($this->getContext()->getDataProvider()->getRequestFieldName(), null);
$filter = $this->filterBuilder->setField($this->getContext()->getDataProvider()->getPrimaryFieldName())
->setValue($id)
->create();
$this->getContext()->getDataProvider()
->addFilter($filter);
$data = $this->getContext()->getDataProvider()->getData();
if (isset($data[$id])) {
$dataSource = [
'data' => $data[$id]
];
} elseif (isset($data['items'])) {
foreach ($data['items'] as $item) {
if ($item[$item['id_field_name']] == $id) {
$dataSource = ['data' => ['general' => $item]];
}
}
}
return $dataSource;
}
Following line is make collection as a single item
$this->getContext()->getDataProvider()
->addFilter($filter);
[Edit by OP].
The trace is:
- \Magento\Framework\View\Element\UiComponent\ContentType\Json::render()
- \Magento\Framework\View\Element\UiComponent\Context::getDataSourceData()
- \Magento\Ui\Component\Form::getDataSourceData()