Loading models with NotORM and Dependency Injection 30/01/2012
Since Nette 2.0.5 we can load models via Dependency Injection container (in config files ) and in presenter we can use autowire method inject. Pros of DI container approach is transparency.
This tutorial uses Nette 2.0.5 a NotORM. Are you looking for dibi or Nette Database (in Czech)?
Configuring environment
config.neon
First we set up required parameters and register database service. Then we can add particular models as services.
parameters:
# parameters to connect to database
database:
driver: mysql
hostn: local
username: root
password:
dbname: database
nette:
# required to activate debug panel for NotORM
database:
default:
dsn: "%database.driver%:host=%database.host%;dbname=%database.dbname%"
user: %database.username%
password: %database.password%
services:
dbcache: NotORM_Cache_Include("%tempDir%/notorm.cache")
database: NotORM(@\PDO, NULL, @dbcache)
# our models
userModel: Models\User
articleModel: Models\Article
Models
To preserve KISS approach and not to repeat same code in all models, we have to create abstract parent class. We define basic methods and database connection in it.
BaseModel.php
namespace Models;
use Nette;
use NotORM;
abstract class Base extends Nette\Object
{
/** @var NotORM */
protected $db;
/** @var string */
private $tableName;
/**
* @param NotORM
*/
public function __construct(NotORM $notorm)
{
$this->db = $notorm;
$this->tableName = $this->tableNameByClass(get_class($this));
}
/**
* Determine table by class name
* @param string
* @return string
* @result:Pages => pages, ArticleTag => article_tag
*/
private function tableNameByClass($className)
{
$tableName = explode("\\", $className);
$tableName = lcfirst(array_pop($tableName));
$replace = array(); // A => _a
foreach(range("A", "Z") as $letter) {
$replace[$letter] = "_".strtolower($letter);
}
return strtr($tableName, $replace);
}
// own methods: insert, update, delete, count,
// fetchSingle, fetchPairs, etc.
}
Particular model
Every model extending Models\Base
contains all it’s methods. We may use them and also add some specific for this model only.
namespace Models;
class User extends Base
{
/**
* Return user by name
* @param string
*/
public function getByName($name)
{
return $this->db->user("name", $name)->fetch();
}
}
Let’s inject
We have already registered models as services. To avoid $this->context
we have to inject to presenters.
To avaid “inject hell” we will load all frequently used models in BasePresenter
to properties with protected
access.
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
/** @var Models\User */
protected $userModel;
/**
* Inject all models that are expected to be used in most of BasePresenter's ancestors
* @param Models\User
*/
public function injectBaseModels(Models\User $userModel)
{
$this->userModel = $userModel;
}
}
Use in practice
class ArticlePresenter extends BasePresenter
{
/** @var Models\Article */
private $articleModel;
/**
* @param Models\Article
*/
public function injectModels(Models\Article $articleModel)
{
$this->articleModel = $articleModel;
}
/**
* @param int
*/
public function actionDetail($id)
{
$this->template->article = $article = $this->articleModel->fetch($id)
$this->template->articleAuthor = $this->userModel->fetch($article["user_id"]);
}
}