Как работать с базами данных или знакомство с компонентом Zend_Db

Редко какое веб-приложение обходится без работы с базой данных. Компонент Zend_Db предоставляет удобный интерфейс доступа к SQL базам данных. Он использует ряд адаптеров для соединения с различными

базами данных. На полноценную ORM рассчитывать не приходится, да это и не нужно в большинстве случаев, Zend_Db является своего рода конструктором запросов. Разводить холивара на тему того, использовать

ли plain sql или ORM, не буду. Лично я сложные запросы предпочитаю записывать в явном виде, а простые можно генерировать, используя тот же самый Zend_Db.

Итак, вспомним наш каталог книг, который мы делали при помощи компонента Zend_Session_Namespace. Сессии — это, конечно, хорошо, но все-таки хочется иметь возможность сохранить информацию, чтобы потом

в любой момент иметь к ней доступ, а не только во время текущего сеанса 😉

Сначала нам необходимо настроить соединение с базой данных. Идем в application/configs/application.ini и прописываем такие строчки:

;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"

В качестве адаптера я выбрал pdo_mysql, имя пользователя — zf, пароль — s3cr3t, имя базы данных — books.

Далее необходимо создать базу данных books и выставить привилегии

mysqladmin create books -uroot -p

mysql -uroot -p -e "grant all privileges on books.* to zf@localhost identified by 's3cr3t'"

Создаем таблицу

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;

Опишем модель books, которая будет манипулировать книгами.

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);
}

}

Для книг имеется три свойства — это название книги (name), автор книги (author) и описание (description). Для них мы определили свои сетеры и гетеры. Еще есть два метода — save и find, которые используют класс маппера для доступа к данным. Используя шаблон Data Mapper, мы как бы абстрагируемся от работы с базой данных и скрываем от модели все взаимодействие с БД. В случае же изменения структуры таблицы, нам просто необходимо будет создать соответствующие свойства в модели и указать для них get и set методы доступа. Класс BookMapper определим пока следующим образом:

class Model_BookMapper
{

 public function save(Model_Book $book)
 {

 }

 public function find($id, Model_Book $book)
 {

 }

}

Перейдем пока в контроллер BookController.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;
 }

}

Мы получаем данные из формы и передаем их в конструктор объекта Книга, затем просто сохраняем объект. Красиво получается и совсем мало кода, не правда ли?) Но теперь нам нужно определить конструктор объекта, чтобы двумя строчками кода создавать новый объект.

// 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();
 }

Теперь осталось реализовать методы в mapper классе.

// 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);
 }

Ах да, я забыл про идентификатор записи. Так что в модели Book необходимо добавить:

protected $id;

public function getId()
 {
return $this->id;
 }

 public function setId($id)
 {
$this->id = $id;
return $this;
 }

В маппер классе мы используем шлюз Model_DbTable_Book для манипуляции с данными таблицы. В папке models необходимо создать папку DbTable, внутри которой  — файл Book.php.

class Model_DbTable_Book extends Zend_Db_Table_Abstract
{
protected $_name = 'books';
}

Класс Model_DbTable_Book является наследником класса Zend_Db_Table_Abstract, так что методы типа find, insert, update, которые используются в маппер классе уже реализованы.

Можно теперь попробовать добавить книгу в базу данных, должно получиться =). Настало время для отображения всех книг, откроем indexAction контроллера Книги.

public function indexAction()
 {
$mdlBook = new Model_BookMapper();
$this->view->books = $mdlBook->fetchAll();
 }

Добавим метод fetchAll в mapper класс

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;
 }

Создадим view для отображения всех книг

<!-- 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>&nbsp;
 <a href="/book/delete/id/<?=$book->id?>">delete</a>
 </dt>
 <dd>
 <?=$book->description?>
 </dd>
 <?php
 }
 ?>
 </dl>
 <?php
}

Можно насладиться результатом, открыв страницу /book. На этом, пожалуй, я сегодня закончу.

Реализацию методов update и delete оставлю вам в качестве упражнения 😉 Если не получится, задавайте вопросы, с радостью отвечу.

На всякий случай прикрепил исходники проекта.

Ссылки:

Одна мысль о “Как работать с базами данных или знакомство с компонентом Zend_Db

  • 29 мая 2012 в 7:08
    Permalink

    Кто и где отредактировать этим летом в отпуск , поделитесь информацией.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *