I have found a solution for the use of save()
in a Repository, and I am trying to answer as @theSeeker also need a similar solution.
I created this around 7 months ago, and can't remember everything I have done, so the answer shows alot of code, but is relevant to a working repository, especially to save()
when you have custom non-default primary ID
.
Relevant code
- Your Resource (Database collection) should override the protected
_idFieldName
to the custom column primary id
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
/**
* @var string
*/
protected $_idFieldName = DbHelper::FIELD_ORDERS_COMMENT_HISTORY_ORDER_HISTORY_ID;
Save
function inside Repository class. (Rest of the Repository code truncated)
/**
* {@inheritdoc}
*/
public function save(
\Vendor\ModuleName\Api\Data\AssignedOrderCommentHistoryInterface $user
) {
/* if (empty($user->getStoreId())) {
$storeId = $this->storeManager->getStore()->getId();
$user->setStoreId($storeId);
} */
/**
* Not sure what these extensibleDataObjectConvertor is supposed to do, but I doubt it works
* correctly, even sure in /Model/UserRepository and /Model/UserTypeRepository.
* Same code taken from online resources.
*/
// $data = $this->extensibleDataObjectConverter->toNestedArray(
// $user,
// [],
// \Vendor\ModuleName\Api\Data\AssignedOrderCommentHistoryInterface::class
// );
$data = $user->getData();
/** Remove primary id from array if this is a new row */
if ($user->isNewRow()) {
$data = $user->dataToArray();
}
$userModel = $this->assignedOrderCommentHistoryFactory->create()->setData($data);
try {
$this->resource->save($userModel);
} catch (AlreadyExistsException $exExist) {
throw new CouldNotSaveException(
__(
'Could not save the assigned details: %1',
$exExist->getMessage()
)
);
} catch (\Exception $exception) {
throw new CouldNotSaveException(__(
'Could not save the assigned details: %1',
$exception->getMessage()
));
}
return $userModel->getDataModel();
}
- Data Model - You can then create an Interface for your Data Model:
interface BaseExtensibleDataInterface extends \Magento\Framework\Api\ExtensibleDataInterface
{
/**
* Return true if the object has data
*
* @return boolean
*/
public function hasData();
/**
* Return if this is a new row (no existing data)
*
* @return boolean
*/
public function isNewRow();
/**
* Set that this is a new row (no existing data)
* @return this
*/
public function setIsNewRow();
/**
* Return if this is an update row (not a new row)
*
* @return boolean
*/
public function isUpdateRow();
/**
* Set that this is an update row (not a new row)
* @return this
*/
public function setIsUpdateRow();
/**
* Get All data or data for a specific key when specified
*
* @param string $key If Empty will return all data
* @return array
*/
public function getData($key = "");
/**
* Convert object->getData() to Array
*
* @return array
*/
public function dataToArray(bool $ignorePrimaryId = true);
/**
* Set data from array of data
*
* @param array $data
* @return void
*/
public function setDataFromArray(array $data);
/**
* Set data from AbstractModel model
*
* @param \Magento\Framework\Model\AbstractModel $model
* @return void
*/
public function setDataFromModel(\Magento\Framework\Model\AbstractModel $model);
}
- I then created an abstract object implementing the interface as I used it for several implementations.
NOTE: I am truncating the class, just adding the code I feel is relevant to the issue.
abstract class AbstractApiInterfacedExtensibleObject
extends \Magento\Framework\Api\AbstractExtensibleObject
implements BaseExtensibleDataInterface
{
protected $_isNewRow;
/**
* Return true if the object has data
*
* @return boolean
*/
public function hasData()
{
return !empty($this->getData());
}
/**
* Return if this is a new row (no existing data)
*
* @return boolean
*/
public function isNewRow()
{
return $this->_isNewRow;
}
/**
* Set that this is a new row (no existing data)
* @return this
*/
public function setIsNewRow()
{
$this->_isNewRow = true;
}
/**
* Return if this is an update row (not a new row)
*
* @return boolean
*/
public function isUpdateRow()
{
return !$this->isNewRow();
}
/**
* Set that this is an update row (not a new row)
*
* @return this
*/
public function setIsUpdateRow()
{
$this->_isNewRow = false;
}
/**
* Get All data or data for a specific key when specified
*
* @param string $key If Empty will return all data
* @return array
*/
public function getData($key = "")
{
if (!is_null($this->_data)) {
if (!empty($key) && array_key_exists($key, $this->_data)) {
return $this->_data[$key];
}
return $this->_data;
}
return array();
}
/**
* Convert object->getData() to Array
*
* @return array
*/
public function dataToArray(bool $ignorePrimaryId = true)
{
if ($ignorePrimaryId) {
$data = $this->getData();
if (array_key_exists(static::PRIMARY_ID, $data)) {
unset($data[static::PRIMARY_ID]);
}
return $data;
}
return $this->getData();
}
/**
* Set data from array of data
*
* @param array $data
* @return this
*/
public function setDataFromArray(array $data)
{
foreach ($data as $key => $value) {
$this->_data[$key] = $value;
}
return $this;
}
/**
* Set data from AbstractModel model
*
* @param \Magento\Framework\Model\AbstractModel $model
* @return this
*/
public function setDataFromModel(\Magento\Framework\Model\AbstractModel $model)
{
return $this->setDataFromArray($model->getData());
}
}
- Then finally the actual Data Model
class AssignedOrderCommentHistoryDetail extends AbstractApiInterfacedExtensibleObject implements AssignedOrderCommentHistoryDetailInterface
{
/**
* Get PRIMARY_ID
* @return string|null
*/
public function getPrimaryId()
{
return $this->_get(static::PRIMARY_ID);
}
/**
* Set PRIMARY_ID
* @param string $primaryId
* @return \Vendor\ModuleName\Api\Data\AssignedOrderCommentHistoryDetailInterface
*/
public function setPrimaryId($primaryId)
{
return $this->setData(static::PRIMARY_ID, $primaryId);
}
/**
* Get RELATED_ORDER_HISTORY_ID
* @return string|null
*/
public function getRelatedOrderHistoryId()
{
return $this->_get(static::RELATED_ORDER_HISTORY_ID);
}
/**
* Set RELATED_ORDER_HISTORY_ID
* @param string $relatedOrderHistoryId
* @return \Vendor\ModuleName\Api\Data\AssignedOrderCommentHistoryDetailInterface
*/
public function setRelatedOrderHistoryId($relatedOrderHistoryId)
{
return $this->setData(static::RELATED_ORDER_HISTORY_ID, $relatedOrderHistoryId);
}
/**
* Get ASSIGNED_SALES_ORDER_HISTORY_COMMENT_ID
* @return string|null
*/
public function getAssignedSalesOrderHistoryCommentId()
{
return $this->_get(static::ASSIGNED_SALES_ORDER_HISTORY_COMMENT_ID);
}
/**
* Set ASSIGNED_SALES_ORDER_HISTORY_COMMENT_ID
* @param string $commentId
* @return \Vendor\ModuleName\Api\Data\AssignedOrderCommentHistoryDetailInterface
*/
public function setAssignedSalesOrderHistoryCommentId($commentId)
{
return $this->setData(static::ASSIGNED_SALES_ORDER_HISTORY_COMMENT_ID, $commentId);
}
/**
* Get ASSIGNED_USER_USER_ID
* @return string|null
*/
public function getAssignedUserId()
{
return $this->_get(static::ASSIGNED_USER_USER_ID);
}
/**
* Set ASSIGNED_USER_USER_ID
* @param string $userId
* @return \Vendor\ModuleName\Api\Data\AssignedOrderCommentHistoryDetailInterface
*/
public function setAssignedUserId($userId)
{
return $this->setData(static::ASSIGNED_USER_USER_ID, $userId);
}
/**
* Retrieve existing extension attributes object or create a new one.
* @return \Vendor\ModuleName\Api\Data\AssignedOrderCommentHistoryDetailExtensionInterface|null
*/
public function getExtensionAttributes()
{
return $this->_getExtensionAttributes();
}
/**
* Set an extension attributes object.
* @param \Vendor\ModuleName\Api\Data\AssignedOrderCommentHistoryDetailExtensionInterface $extensionAttributes
* @return $this
*/
public function setExtensionAttributes(
\Vendor\ModuleName\Api\Data\AssignedOrderCommentHistoryDetailExtensionInterface $extensionAttributes
) {
return $this->_setExtensionAttributes($extensionAttributes);
}
Explanation
Have a look at the save()
function in the Repository.
$data = $user->getData();
- The custom
getData()
filters information which I don't need and formats the array in the associative format Magento 2 expects for 'Data Models with Repositories'.
if ($user->isNewRow()) {
$data = $user->dataToArray();
}
- I call
isNewRow()
to find if the row is new (you can use setIsNewRow()
somewhere to set it as new if you can't determine automatically)
- The
dataToArray()
then removes the primary key when new row or not.
$userModel = $this->assignedOrderCommentHistoryFactory->create()->setData($data);
- Create the Factory
AssignedOrderCommentHistoryFactory
of the Model AssignedOrderCommentHistory
that you set in the constructor of the Repository (the name ends with Factory, with the same namespace as your Class, and Magento 2 creates the class automatically when you run bin/magento setup:di:compile
- for more information research further about Magento 2 Factories).
$this->resource->save($userModel);
- Then you can save to the Magento 2 resource, this will then create a new row or update the model if it exists in the database
$this->resource->save($userModel)
return $userModel->getDataModel();
- Finally: Make sure you return a different format Data Model inside the
save()
function which Magento 2 expects.
Summary
There is no summary, as this has been over 7 months ago, I cannot remember everything done here, I do remember the main things that had me struggling, and the explanation I gave above covers most.
TLDR
save()
on Repositories.