I created a new table company_shipping_quote and new models by using https://mage2gen.com/.
But how can I create a new object and save it so that it appears in the database?
Models:
Company/Shipping/Model/Quote:
<?php
/**
 * Copyright © All rights reserved.
 * See COPYING.txt for license details.
 */
declare(strict_types=1);
namespace Company\Shipping\Model;
use Company\Shipping\Api\Data\QuoteInterface;
use Company\Shipping\Api\Data\QuoteInterfaceFactory;
use Magento\Framework\Api\DataObjectHelper;
class Quote extends \Magento\Framework\Model\AbstractModel
{
 protected $quoteDataFactory;
 protected $dataObjectHelper;
 protected $_eventPrefix = 'company_shipping_quote';
 /**
 * @param \Magento\Framework\Model\Context $context
 * @param \Magento\Framework\Registry $registry
 * @param QuoteInterfaceFactory $quoteDataFactory
 * @param DataObjectHelper $dataObjectHelper
 * @param \Company\Shipping\Model\ResourceModel\Quote $resource
 * @param \Company\Shipping\Model\ResourceModel\Quote\Collection $resourceCollection
 * @param array $data
 */
 public function __construct(
 \Magento\Framework\Model\Context $context,
 \Magento\Framework\Registry $registry,
 QuoteInterfaceFactory $quoteDataFactory,
 DataObjectHelper $dataObjectHelper,
 \Company\Shipping\Model\ResourceModel\Quote $resource,
 \Company\Shipping\Model\ResourceModel\Quote\Collection $resourceCollection,
 array $data = []
 ) {
 $this->quoteDataFactory = $quoteDataFactory;
 $this->dataObjectHelper = $dataObjectHelper;
 parent::__construct($context, $registry, $resource, $resourceCollection, $data);
 }
 /**
 * Retrieve quote model with quote data
 * @return QuoteInterface
 */
 public function getDataModel()
 {
 $quoteData = $this->getData();
 $quoteDataObject = $this->quoteDataFactory->create();
 $this->dataObjectHelper->populateWithArray(
 $quoteDataObject,
 $quoteData,
 QuoteInterface::class
 );
 return $quoteDataObject;
 }
}
Company/Shipping/Model/QuoteRepository:
<?php
/**
 * Copyright © All rights reserved.
 * See COPYING.txt for license details.
 */
declare(strict_types=1);
namespace Company\Shipping\Model;
use Company\Shipping\Api\Data\QuoteInterfaceFactory;
use Company\Shipping\Api\Data\QuoteSearchResultsInterfaceFactory;
use Company\Shipping\Api\QuoteRepositoryInterface;
use Company\Shipping\Model\ResourceModel\Quote as ResourceQuote;
use Company\Shipping\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory;
use Magento\Framework\Api\DataObjectHelper;
use Magento\Framework\Api\ExtensibleDataObjectConverter;
use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Reflection\DataObjectProcessor;
use Magento\Store\Model\StoreManagerInterface;
class QuoteRepository implements QuoteRepositoryInterface
{
 private $collectionProcessor;
 protected $resource;
 protected $quoteFactory;
 protected $dataQuoteFactory;
 protected $extensibleDataObjectConverter;
 protected $searchResultsFactory;
 protected $dataObjectProcessor;
 private $storeManager;
 protected $quoteCollectionFactory;
 protected $extensionAttributesJoinProcessor;
 protected $dataObjectHelper;
 /**
 * @param ResourceQuote $resource
 * @param QuoteFactory $quoteFactory
 * @param QuoteInterfaceFactory $dataQuoteFactory
 * @param QuoteCollectionFactory $quoteCollectionFactory
 * @param QuoteSearchResultsInterfaceFactory $searchResultsFactory
 * @param DataObjectHelper $dataObjectHelper
 * @param DataObjectProcessor $dataObjectProcessor
 * @param StoreManagerInterface $storeManager
 * @param CollectionProcessorInterface $collectionProcessor
 * @param JoinProcessorInterface $extensionAttributesJoinProcessor
 * @param ExtensibleDataObjectConverter $extensibleDataObjectConverter
 */
 public function __construct(
 ResourceQuote $resource,
 QuoteFactory $quoteFactory,
 QuoteInterfaceFactory $dataQuoteFactory,
 QuoteCollectionFactory $quoteCollectionFactory,
 QuoteSearchResultsInterfaceFactory $searchResultsFactory,
 DataObjectHelper $dataObjectHelper,
 DataObjectProcessor $dataObjectProcessor,
 StoreManagerInterface $storeManager,
 CollectionProcessorInterface $collectionProcessor,
 JoinProcessorInterface $extensionAttributesJoinProcessor,
 ExtensibleDataObjectConverter $extensibleDataObjectConverter
 ) {
 $this->resource = $resource;
 $this->quoteFactory = $quoteFactory;
 $this->quoteCollectionFactory = $quoteCollectionFactory;
 $this->searchResultsFactory = $searchResultsFactory;
 $this->dataObjectHelper = $dataObjectHelper;
 $this->dataQuoteFactory = $dataQuoteFactory;
 $this->dataObjectProcessor = $dataObjectProcessor;
 $this->storeManager = $storeManager;
 $this->collectionProcessor = $collectionProcessor;
 $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor;
 $this->extensibleDataObjectConverter = $extensibleDataObjectConverter;
 }
 /**
 * {@inheritdoc}
 */
 public function save(
 \Company\Shipping\Api\Data\QuoteInterface $quote
 ) {
 /* if (empty($quote->getStoreId())) {
 $storeId = $this->storeManager->getStore()->getId();
 $quote->setStoreId($storeId);
 } */
 $quoteData = $this->extensibleDataObjectConverter->toNestedArray(
 $quote,
 [],
 \Company\Shipping\Api\Data\QuoteInterface::class
 );
 $quoteModel = $this->quoteFactory->create()->setData($quoteData);
 try {
 $this->resource->save($quoteModel);
 } catch (\Exception $exception) {
 throw new CouldNotSaveException(__(
 'Could not save the quote: %1',
 $exception->getMessage()
 ));
 }
 return $quoteModel->getDataModel();
 }
 /**
 * {@inheritdoc}
 */
 public function get($quoteId)
 {
 $quote = $this->quoteFactory->create();
 $this->resource->load($quote, $quoteId);
 if (!$quote->getId()) {
 throw new NoSuchEntityException(__('Quote with id "%1" does not exist.', $quoteId));
 }
 return $quote->getDataModel();
 }
 /**
 * {@inheritdoc}
 */
 public function getList(
 \Magento\Framework\Api\SearchCriteriaInterface $criteria
 ) {
 $collection = $this->quoteCollectionFactory->create();
 $this->extensionAttributesJoinProcessor->process(
 $collection,
 \Company\Shipping\Api\Data\QuoteInterface::class
 );
 $this->collectionProcessor->process($criteria, $collection);
 $searchResults = $this->searchResultsFactory->create();
 $searchResults->setSearchCriteria($criteria);
 $items = [];
 foreach ($collection as $model) {
 $items[] = $model->getDataModel();
 }
 $searchResults->setItems($items);
 $searchResults->setTotalCount($collection->getSize());
 return $searchResults;
 }
 /**
 * {@inheritdoc}
 */
 public function delete(
 \Company\Shipping\Api\Data\QuoteInterface $quote
 ) {
 try {
 $quoteModel = $this->quoteFactory->create();
 $this->resource->load($quoteModel, $quote->getQuoteId());
 $this->resource->delete($quoteModel);
 } catch (\Exception $exception) {
 throw new CouldNotDeleteException(__(
 'Could not delete the Quote: %1',
 $exception->getMessage()
 ));
 }
 return true;
 }
 /**
 * {@inheritdoc}
 */
 public function deleteById($quoteId)
 {
 return $this->delete($this->get($quoteId));
 }
}
Company/Shipping/Model/Data/Quote.php:
<?php
/**
 * Copyright © All rights reserved.
 * See COPYING.txt for license details.
 */
declare(strict_types=1);
namespace Company\Shipping\Model\Data;
use Company\Shipping\Api\Data\QuoteInterface;
class Quote extends \Magento\Framework\Api\AbstractExtensibleObject implements QuoteInterface
{
 /**
 * Get quote_id
 * @return string|null
 */
 public function getQuoteId()
 {
 return $this->_get(self::QUOTE_ID);
 }
 /**
 * Set quote_id
 * @param string $quoteId
 * @return \Company\Shipping\Api\Data\QuoteInterface
 */
 public function setQuoteId($quoteId)
 {
 return $this->setData(self::QUOTE_ID, $quoteId);
 }
 /**
 * Get id
 * @return string|null
 */
 public function getId()
 {
 return $this->_get(self::ID);
 }
 /**
 * Set id
 * @param string $id
 * @return \Company\Shipping\Api\Data\QuoteInterface
 */
 public function setId($id)
 {
 return $this->setData(self::ID, $id);
 }
 /**
 * Retrieve existing extension attributes object or create a new one.
 * @return \Company\Shipping\Api\Data\QuoteExtensionInterface|null
 */
 public function getExtensionAttributes()
 {
 return $this->_getExtensionAttributes();
 }
 /**
 * Set an extension attributes object.
 * @param \Company\Shipping\Api\Data\QuoteExtensionInterface $extensionAttributes
 * @return $this
 */
 public function setExtensionAttributes(
 \Company\Shipping\Api\Data\QuoteExtensionInterface $extensionAttributes
 ) {
 return $this->_setExtensionAttributes($extensionAttributes);
 }
 /**
 * Get express_note
 * @return string|null
 */
 public function getExpressNote()
 {
 return $this->_get(self::EXPRESS_NOTE);
 }
 /**
 * Set express_note
 * @param string $expressNote
 * @return \Company\Shipping\Api\Data\QuoteInterface
 */
 public function setExpressNote($expressNote)
 {
 return $this->setData(self::EXPRESS_NOTE, $expressNote);
 }
}
Company/Shipping/Model/ResourceModel/Quote.php:
<?php
/**
 * Copyright © All rights reserved.
 * See COPYING.txt for license details.
 */
declare(strict_types=1);
namespace Company\Shipping\Model\ResourceModel;
class Quote extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
 /**
 * Define resource model
 *
 * @return void
 */
 protected function _construct()
 {
 $this->_init('company_shipping_quote', 'quote_id');
 }
}
Company/Shipping/Model/ResourceModel/Quote/Collection.php:
<?php
/**
 * Copyright © All rights reserved.
 * See COPYING.txt for license details.
 */
declare(strict_types=1);
namespace Company\Shipping\Model\ResourceModel\Quote;
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
 /**
 * @var string
 */
 protected $_idFieldName = 'quote_id';
 /**
 * Define resource model
 *
 * @return void
 */
 protected function _construct()
 {
 $this->_init(
 \Company\Shipping\Model\Quote::class,
 \Company\Shipping\Model\ResourceModel\Quote::class
 );
 }
}
Database
Company/Shipping/etc/db_schema.xml:
<?xml version="1.0"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
 <table name="company_shipping_quote" resource="default">
 <column xsi:type="int" name="id" nullable="false" unsigned="true" comment="ID"/>
 <constraint xsi:type="primary" referenceId="PRIMARY">
 <column name="id"/>
 </constraint>
 <column xsi:type="int" name="quote_id" nullable="false" unsigned="true" comment="Quote ID"/>
<!-- <constraint xsi:type="foreign"
 referenceId="FK_QUOTE_ENTITIY_ID"
 table="company_shipping_quote"
 column="quote_id"
 referenceTable="quote"
 referenceColumn="entity_id"
 onDelete="CASCADE"/>-->
 <column xsi:type="text" name="express_note" nullable="true" comment="Express Shipping Note with Additional Information"/>
 </table>
</schema>
Of course I executed bin/magento setup:upgrade && bin/magento setup:di:compile
Attempt:
Now inside another class I tried to create an object of the model and save it:
protected $_companyShippingQuoteFactory;
public function __construct(
 ...
 \Company\Shipping\Model\QuoteFactory $companyShippingQuoteFactory
) {
 ...
 $this->_companyShippingQuoteFactory = $companyShippingQuoteFactory;
}
...
$quoteModel = $this->_companyShippingQuoteFactory->create();
$quoteModel->setQuoteId(123);
$quoteModel->setExpressNote("foo_bar");
$quoteModel->save();
But it does not save. The database table is still empty:
2 Answers 2
It might be related to your: db_schema.xml
Try to add:
 <column xsi:type="int" name="id" nullable="false" unsigned="true" comment="ID" identity="true"/>
Adding identity="true"
If you check e.g. this example you see they have it on their primary key:
https://devdocs.magento.com/guides/v2.4/extension-dev-guide/declarative-schema/db-schema.html
I figured it out. It was not saving because the mage2gen generator added wrong values for the ID in the files, because my table name ends with _quote and it took it to make quote_id to the ID key..., but I am already using quote_id as a field, so it does not work out...
- Company/Shipping/Model/ResourceModel/Quote/Collection.php
- Company/Shipping/Model/ResourceModel/Quote.php
I also have set identity='true' like @Kristoffer suggested.
- 
 I double checked it, and it was indeed the reason. I also setidentity='true'like @Kristoffer suggested.Black– Black2020年10月21日 10:55:48 +00:00Commented Oct 21, 2020 at 10:55