← Назад к статьям

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

M
miholeus
1 ноября 2010 г. · 5 мин чтения

Редко какое веб-приложение обходится без работы с базой данных. Компонент 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 zf@localhost 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>&nbsp; <a href="/book/delete/id/<?=$book->id?>">delete</a> </dt> <dd> <?=$book->description?> </dd> <?php } ?> </dl> <?php }

[/code]

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

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

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

Ссылки:

Комментарии 1

Комментарии проходят модерацию
Загрузка комментариев...