Because it was hard for me to find the right way, below you could find the best practice I made mine. Enjoy, correct my English if needed and say me I'm wrong if I am. :)
Edit : ... and I found out I was wrong on some aspect. So I updated the original post after Raphael's answers helped me to understand more. Thanks to him !
Concept used below :
It will be easier for you to understand codes and explanations below if you are comfortable with these concepts :
- Injection Dependency (as every
$this->variable
variables in codes are injected) - Service Contract and Repository
- Factory
Context :
Just to have more context, imagine we have a module correctly construct with :
- a block class CustomBlock containing a method
getCustomModel($id)
, - this method returns a CustomModel object based on the id passed in param,
- CustomModel type correspond to the model in
\Vendor\Module\Model\CustomModel
- This model comes with its resource model (in
\Vendor\Module\Model\ResourceModel\CustomModel
) - and with its repository (in
\Vendor\Module\Model\CustomModelRepository
).
Question :
- What's the best practice to let the all thing loading a CustomModel object ?
You can't use the load()
from a CustomModel object since this method is deprecated.
The good practice says that you have to use the CustomModel Service Contract. Service contracts are data interfaces (e.g. CustomModelInterface) and service interfaces (e.g. CustomModelRepositoryInterface). So my block looks like this :
/** @var SlideRepositoryInterface */ protected $slideRepository; /** * CustomBlock constructor * ... * @param CustomModelRepositoryInterface $customModelRepository * ... */ public function __construct( ... CustomModelRepositoryInterface $customModelRepository ... ) { $this->customModelRepository = $customModelRepository; } public function getCustomModel($id) { return $this->customModelRepository->get($id); }
First of all, we inject the CustomModelRepositoryInterface
object in the constructor and we use it in our getCustomModel()
method.
In the class Api\CustomModelRepositoryInterface
there is not a lot. Generally (but nothing prevent you to do differently) you will declare basic methods : get
, getList
, save
, delete
, deleteById
. For the purpose of this topic, below is just the get
method declaration :
/**
* Get info by id
*
* @param int $id
* @return Data\CustomModelInterface
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function get($id);
Ok, but if my CustomModel Interface is called by dependency injection in my block constructor, where is the code ? For answer to this question you have to explain to Magento where find the class implementing this interface. In the etc/di.xml file of the module, you have to add :
<preference for="Vendor\Module\Api\CustomModelRepositoryInterface" type="Vendor\Module\Model\CustomModelRepository" />
So CustomModelRepositoryInterface
class is a service interface. In implementing it you will have to implementing also data interfaces (at least Vendor\Module\Api\Data\CustomModelInterface
and Vendor\Module\Api\Data\CustomModelSearchResultsInterface
). Your model will have to implement Vendor\Module\Api\Data\CustomModelInterface
and add <preference ... />
lines for each one of your interfaces. Finally at anytime you use service contract, think in mySomethingInterface
not anymore in mySomething
: let magento use the di.xml
preferences mechanism.
Ok, what comes next ? As we inject CustomModelRepositoryInterface
in the block constructor, we get an CustomModelRepository
object. CustomModelRepository
has to implement the method declare in CustomModelRepositoryInterface
. So we have this in Vendor\Module\Model\CustomModelRepository
:
public function get($id) { $customModel = $this->customModelFactory->create(); $customModel->load($id); if (!$customModel->getId()) { throw new NoSuchEntityException(__('CustomModel with id "%1" does not exist.', $id)); } return $customModel; }
What we are doing ? We create an empty CustomModel
object thanks to the factory. Next we load data in the CustomModel
using the load model method. Next we return a NoSuchEntityException
if we failed to load the CustomModel
with the id in params. But if everything's ok, we return the model object and life continue.
But wow...! In this example what is that ?
$customModel->load($id);
Isn't the same deprecated load
method than at the beginning ? Yes, it is. I think it's a shame, but you have to use it since in this load() method there are some events dispatched and developer could listening for them (see Raphael's answer below).
In future, we will be save by Entity Manager. It's another story as a new Magento 2 concept, but if you want to drop an eye on, Entity Manager is already implemented in the Resource Model of CMS Page (v2.1) :
public function load(AbstractModel $object, $value, $field = null)
{
$pageId = $this->getPageId($object, $value, $field);
if ($pageId) {
$this->entityManager->load($object, $pageId);
}
return $this;
}