Редко какое веб-приложение обходится без работы с базой данных. Компонент Zend_Db предоставляет удобный интерфейс доступа к SQL базам данных. Он использует ряд адаптеров для соединения с различными
базами данных. На полноценную ORM рассчитывать не приходится, да это и не нужно в большинстве случаев, Zend_Db является своего рода конструктором запросов. Разводить холивара на тему того, использовать
ли plain sql или ORM, не буду. Лично я сложные запросы предпочитаю записывать в явном виде, а простые можно генерировать, используя тот же самый Zend_Db.
Итак, вспомним наш каталог книг, который мы делали при помощи компонента Zend_Session_Namespace. Сессии – это, конечно, хорошо, но все-таки хочется иметь возможность сохранить информацию, чтобы потом
в любой момент иметь к ней доступ, а не только во время текущего сеанса 😉
Сначала нам необходимо настроить соединение с базой данных. Идем в application/configs/application.ini и прописываем такие строчки:
[code language=”php”]
;DB settings
resources.db.adapter = “pdo_mysql”
resources.db.params.host = “localhost”
resources.db.params.username = “zf”
resources.db.params.password = “s3cr3t”
resources.db.params.dbname = “books”
resources.db.isDefaultTableAdapter = true
resources.db.params.driver_options.1002 = “SET NAMES utf8”
[/code]
В качестве адаптера я выбрал pdo_mysql, имя пользователя – zf, пароль – s3cr3t, имя базы данных – books.
Далее необходимо создать базу данных books и выставить привилегии
[code language=”bash”]
mysqladmin create books -uroot -p
mysql -uroot -p -e “grant all privileges on books.* to [email protected] identified by ‘s3cr3t'”
[/code]
Создаем таблицу
[code language=”sql”]
CREATE TABLE `books`.`books` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 255 ) NOT NULL ,
`author` VARCHAR( 100 ) NOT NULL ,
`description` VARCHAR( 255 ) NOT NULL
) ENGINE = InnoDB;
[/code]
Опишем модель books, которая будет манипулировать книгами.
[code language=”php”]
class Model_Book
{
protected $name;
protected $author;
protected $description;
protected $mapper;
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getAuthor()
{
return $this->author;
}
public function setAuthor($author)
{
$this->author = $author;
return $this;
}
public function getDescription()
{
return $this->description;
}
public function setDescription($desc)
{
$this->description = $desc;
return $this;
}
public function getMapper()
{
if( null === $this->mapper) {
$this->mapper = new Model_BookMapper();
}
return $this->mapper;
}
public function save()
{
$this->getMapper()->save($this);
}
public function find($id)
{
return $this->getMapper()->find($id, $this);
}
}
[/code]
Для книг имеется три свойства – это название книги (name), автор книги (author) и описание (description). Для них мы определили свои сетеры и гетеры. Еще есть два метода – save и find, которые используют класс маппера для доступа к данным. Используя шаблон Data Mapper, мы как бы абстрагируемся от работы с базой данных и скрываем от модели все взаимодействие с БД. В случае же изменения структуры таблицы, нам просто необходимо будет создать соответствующие свойства в модели и указать для них get и set методы доступа. Класс BookMapper определим пока следующим образом:
[code language=”php”]
class Model_BookMapper
{
public function save(Model_Book $book)
{
}
public function find($id, Model_Book $book)
{
}
}
[/code]
Перейдем пока в контроллер BookController.php
[code language=”php”]
class BookController extends Zend_Controller_Action
{
public function createAction()
{
$frmBook = new Form_Book();
$frmBook->setMethod(‘post’);
if($this->_request->isPost()) {
if($frmBook->isValid($this->_request->getPost())) {
$mdlBook = new Model_Book($frmBook->getValues());
$mdlBook->save();
}
}
$this->view->form = $frmBook;
}
}
[/code]
Мы получаем данные из формы и передаем их в конструктор объекта Книга, затем просто сохраняем объект. Красиво получается и совсем мало кода, не правда ли?) Но теперь нам нужно определить конструктор объекта, чтобы двумя строчками кода создавать новый объект.
[code language=”php”]
// application/models/Book.php
public function setOptions(array $options)
{
$methods = get_class_methods($this);
foreach ($options as $key => $value) {
$method = ‘set’ . ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
public function __construct(array $options = null)
{
if (is_array($options)) {
$this->setOptions($options);
}
}
public function __set($name, $value)
{
$method = ‘set’ . $name;
if ((‘mapper’ == $name) || !method_exists($this, $method)) {
throw new Exception(‘Invalid book property’);
}
$this->$method($value);
}
public function __get($name)
{
$method = ‘get’ . $name;
if ((‘mapper’ == $name) || !method_exists($this, $method)) {
throw new Exception(‘Invalid book property’);
}
return $this->$method();
}
[/code]
Теперь осталось реализовать методы в mapper классе.
[code language=”php”]
// application/models/BookMapper.php
protected $_dbTable;
public function setDbTable($dbTable)
{
if (is_string($dbTable)) {
$dbTable = new $dbTable();
}
if (!$dbTable instanceof Zend_Db_Table_Abstract) {
throw new Exception(‘Invalid table data gateway provided’);
}
$this->_dbTable = $dbTable;
return $this;
}
public function getDbTable()
{
if (null === $this->_dbTable) {
$this->setDbTable(‘Model_DbTable_Book’);
}
return $this->_dbTable;
}
public function save(Model_Book $book)
{
$data = array(
‘name’ => $book->getName(),
‘author’ => $book->getAuthor(),
‘description’ => $book->getDescription()
);
if (null === ($id = $book->getId())) {
unset($data[‘id’]);
$this->getDbTable()->insert($data);
} else {
$this->getDbTable()->update($data, array(‘id = ?’ => $id));
}
}
public function find($id, Model_Book $book)
{
$result = $this->getDbTable()->find($id);
if (0 == count($result)) {
return;
}
$row = $result->current();
$book->setId($row->id)
->setName($row->name)
->setAuthor($row->author)
->setDescription($row->description);
}
[/code]
Ах да, я забыл про идентификатор записи. Так что в модели Book необходимо добавить:
[code language=”php”]
protected $id;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
return $this;
}
[/code]
В маппер классе мы используем шлюз Model_DbTable_Book для манипуляции с данными таблицы. В папке models необходимо создать папку DbTable, внутри которой – файл Book.php.
[code language=”php”]
class Model_DbTable_Book extends Zend_Db_Table_Abstract
{
protected $_name = ‘books’;
}
[/code]
Класс Model_DbTable_Book является наследником класса Zend_Db_Table_Abstract, так что методы типа find, insert, update, которые используются в маппер классе уже реализованы.
Можно теперь попробовать добавить книгу в базу данных, должно получиться =). Настало время для отображения всех книг, откроем indexAction контроллера Книги.
[code language=”php”]
public function indexAction()
{
$mdlBook = new Model_BookMapper();
$this->view->books = $mdlBook->fetchAll();
}
[/code]
Добавим метод fetchAll в mapper класс
[code language=”php”]
public function fetchAll()
{
$resultSet = $this->getDbTable()->fetchAll();
$entries = array();
foreach ($resultSet as $row) {
$book = new Model_Book();
$book->setId($row->id)
->setName($row->name)
->setAuthor($row->author)
->setDescription($row->description);
$entries[] = $book;
}
return $entries;
}
[/code]
Создадим view для отображения всех книг
[code language=”html”]
<!– application/views/scripts/book/index.phtml –>
<h2>List of all books</h2>
<?php
if(count($this->books)) {
?>
<dl>
<?php
foreach($this->books as $book) {
?>
<dt>
<?=$book->name?> | <small>author: <?=$book->author?></small> |
<a href=”/book/update/id/<?=$book->id?>”>update</a>
<a href=”/book/delete/id/<?=$book->id?>”>delete</a>
</dt>
<dd>
<?=$book->description?>
</dd>
<?php
}
?>
</dl>
<?php
}
[/code]
Можно насладиться результатом, открыв страницу /book. На этом, пожалуй, я сегодня закончу.
Реализацию методов update и delete оставлю вам в качестве упражнения 😉 Если не получится, задавайте вопросы, с радостью отвечу.
На всякий случай прикрепил исходники проекта.
Ссылки:
Кто и где отредактировать этим летом в отпуск , поделитесь информацией.