Обучение Zend Framework после Magento: модели
Я работал более года с Magento и узнал его достаточно хорошо. Теперь я хочу изучать Зенд, и я застрял с моделями.
Я привык иметь сущности и коллекцию сущностей в Magento, и, вероятно, я захочу использовать Zend_Db_Table
, Zend_Db_Table_Row
и/или Zend_Db_Table_Rowset
. Что меня смущает, так это роль каждого класса.
Я знаю, что могу расширить каждый класс, и я понимаю, что в моем Product_Table
класс (который расширяет Zend_Db_Table_Abstract
) возможно иметь частное методы, которые скажут Zend, какие классы использовать для строк и наборов строк, однако я не чувствую себя комфортно с ним.
имея этот код в Magento:
Пример 1
// I understand that maybe I'll use the `new` keyword instead
// Mage::getModel() is only for exemplification
$product = Mage::getModel('catalog/product');
$product->setName('product name');
$product->setPrice(20);
$product->save();
if($id = $product->getId()){
echo 'Product saved with id' . $id;
}
else{
echo 'Error saving product';
}
Пример 2
$collection = Mage::getModel('catalog/product')->getCollection();
// this is the limit, I'm ok with other method's name
$collection->setPageSize(10);
$collection->load()
foreach($collection as $product){
echo $product->getName() . ' costs ' . $product->getPrice() . PHP_EOL;
}
как я могу реализовать что-то подобное в Zend Framework? Альтернативно, если это действительно плохая идея, каковы лучшие практики для реализации модели в Zend Рамки?
спасибо
4 ответов
на Zend
команда, как упоминалось в другом месте, думает иначе о слое модели, чем большинство других создателей PHP Framework. Их текущие мысли о "лучшем" способе использования их необработанных инструментов для предоставления модели сущности, поддерживаемой базой данных, можно найти в быстрый старт руководство.
тем не менее, решение большинства людей для моделей в Zend Framework-это bootstrapping доктрина.
вот как я, лично, реализации моделей. Я буду использовать реальный пример жизни: my User
модель.
всякий раз, когда я создаю модель, я использую два файла и два класса: сама модель (например,Application_Model_User
) и объекта сопоставления (например,Application_Model_UserMapper
). Очевидно, что сама модель содержит данные, методы сохранения, удаления, изменения и т. д. Объект маппера содержит методы для извлечения модельных объектов, поиск объектов и т. д.
вот первые несколько строк пользователя модель:
class Application_Model_User {
protected $_id;
protected $_name;
protected $_passHash;
protected $_role;
protected $_fullName;
protected $_email;
protected $_created;
protected $_salt;
// End protected properties
для каждого свойства у меня есть метод getter и setter. Пример id
:
/* id */
public function getId() {
return $this->_id;
}
public function setId($value) {
$this->_id = (int) $value;
return $this;
}
Я также использую некоторые стандартные "магические методы" для разоблачения публичных геттеров и сеттеров (внизу каждой модели):
public function __set($name, $value) {
$method = 'set' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid user property');
}
$this->$method($value);
}
public function __get($name) {
$method = 'get' . $name;
if (('mapper' == $name) || !method_exists($this, $method)) {
throw new Exception('Invalid user property');
}
return $this->$method();
}
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;
}
пример save
способ:
Я проверяю внутри save()
метод, используя исключения, когда информация не проходит проверку.
public function save() {
// Validate username
if (preg_match("/^[a-zA-Z](\w{6,15})$/", $this->_name) === 0) {
throw new Application_Exception_UserInfoInvalid();
}
// etc.
$db = Zend_Registry::get("db");
// Below, I would check if $this->_id is null. If it is, then we need to "insert" the data into the database. If it isn't, we need to "update" the data. Use $db->insert() or $db->update(). If $this->_id is null, I might also initialize some fields like 'created' or 'salt'.
}
для объекта mapper у меня есть по крайней мере два методы: метод, который возвращает объект запроса для выбора объектов, и тот, который выполняет запрос, инициализирует и возвращает объекты. Я использую это, чтобы я мог манипулировать запросом в моем контроллере для сортировки и фильтрации.
редактировать
как я сказал в своих комментариях, этот пост:http://weierophinney.net/matthew/archives/202-Model-Infrastructure.html было вдохновением для моей текущей реализации модели.
Дополнительные параметры
вы также можете использовать Zend_Form
чтобы сделать проверку, вместо того, чтобы катить свой собственный:http://weierophinney.net/matthew/archives/200-Using-Zend_Form-in-Your-Models.html. Мне лично не нравится этот вариант, так как я думаю, что Zend_Form
- это неудобно и трудно контролировать.
когда большинство людей впервые изучают Zend Framework, они учатся подклассу Zend_Db
связанных классов. Вот статья, которая демонстрирует это: http://akrabat.com/zend-framework/on-models-in-a-zend-framework-application/
Я упомянул, что мне это не нравится. Вот несколько причин почему:
- трудно создавать модели, которые включают производные/вычисляемые поля (т. е. данных из других таблиц)
- я обнаружил, что невозможно включить контроль доступа (заполненный из моей базы данных)
- мне нравится иметь полный контроль над моей модели
правка 2
для вашего второго примера: вы можете использовать фабричный метод zend_paginator для этого. Я упомянул, что в вашей оболочке вы создаете метод, который возвращает объект запроса базы данных для выбора объектов. Вот мой упрощенный, но рабочий пользователь mapper:
class Application_Model_UserMapper {
public function generateSelect() {
$db = Zend_Registry::get("db");
$selectWhat = array(
"users_id",
"name",
"role",
"full_name",
"email",
"DATE_FORMAT(created, '%M %e, %Y at %l:%i:%s %p') as created",
"salt",
"passhash"
);
return $db->select()->from(array("u" => "users"), $selectWhat);
}
public function fetchFromSelect($select) {
$rows = $select->query()->fetchAll();
$results = array();
foreach ($rows as $row) {
$user = new Application_Model_User();
$user->setOptions(array(
"id" => $row["users_id"],
"name" => $row["name"],
"role" => $row["role"],
"fullName" => $row["full_name"],
"email" => $row["email"],
"created" => $row["created"],
"salt" => $row["salt"],
"passHash" => $row["passhash"]
));
$results[] = $user;
}
return $results;
}
}
чтобы обработать paginator, я пишу пользовательский плагин Paginator и сохраняю его в library/Application/Paginator/Adapter/Users.php
. Убедитесь, что у вас есть appnamespace
и autoloaderNamespaces[]
настройки правильно в приложение.ini. Вот плагин:
class Application_Paginator_Adapter_Users extends Zend_Paginator_Adapter_DbSelect {
public function getItems($offset, $itemCountPerPage) {
// Simply inject the limit clause and return the result set
$this->_select->limit($itemCountPerPage, $offset);
$userMapper = new Application_Model_UserMapper();
return $userMapper->fetchFromSelect($this->_select);
}
}
в моем контроллере:
// Get the base select statement
$userMapper = new Application_Model_UserMapper();
$select = $userMapper->generateSelect();
// Create our custom paginator instance
$paginator = new Zend_Paginator(new Application_Paginator_Adapter_Users($select));
// Set the current page of results and per page count
$paginator->setCurrentPageNumber($this->_request->getParam("page"));
$paginator->setItemCountPerPage(25);
$this->view->usersPaginator = $paginator;
затем визуализируйте пагинатор в скрипте просмотра.
Я делаю что-то похожее на способ SimpleCode. Мой стиль происходит от Pádraic Brady. У него есть несколько сообщений в блоге, но лучшим и самым быстрым ресурсом его является онлайн-книга, которую он написал:выжить в глубоком конце!. Эта ссылка должна привести вас прямо к его главе о моделях, картах данных и других интересных лакомствах, таких как ленивая загрузка. Идея заключается в следующем:
у вас есть сущности, такие как пользователь со свойствами, определенными в массиве. Все ваши сущности расширяют абстрактный класс с помощью magic getter / setters, которые получают или обновляют этот массив.
class User extends Entity
{
protected $_data = array(
'user_id' => 0,
'first_name' => null,
'last_name' => null
);
}
class Car extends Entity
{
protected $_data = array(
'car_id' => 0,
'make' => null,
'model' => null
);
}
class Entity
{
public function __construct($data)
{
if(is_array($data))
{
$this->setOptions($data);
}
}
public function __get($key)
{
if(array_key_exists($key, $this->_data)
{
return $this->_data[$key];
}
throw new Exception("Key {$key} not found.");
}
public function __set($key, $value)
{
if(array_key_exists($key, $this->_data))
{
$this->_data[$key] = $value;
}
throw new Exception("Key {$key} not found.");
}
public function setOptions($data)
{
if(is_array($data))
{
foreach($data as $key => $value)
{
$this->__set($key, $value);
}
}
}
public function toArray()
{
return $this->_data;
}
}
$user = new User();
$user->first_name = 'Joey';
$user->last_name = 'Rivera';
echo $user->first_name; // Joey
$car = new Car(array('make' => 'chevy', 'model' => 'corvette'));
echo $car->model; // corvette
Сопоставители данных для меня отделены от сущностей, их задача-сделать CRUD (создать, прочитать, обновить и удалить) в БД. Итак, если нам нужно загрузить объект из БД, я вызываю сопоставитель, специфичный для этого объекта, чтобы загрузить его. Например:
<?php
class UserMapper
{
$_db_table_name = 'UserTable';
$_model_name = 'User';
public function find($id)
{
// validate id first
$table = new $this->_db_table_name();
$rows = $table->find($id);
// make sure you get data
$row = $rows[0]; // pretty sure it returns a collection even if you search for one id
$user = new $this->_model_name($row); // this works if the naming convention matches the user and db table
//else
$user = new $this->_model_name();
foreach($row as $key => $value)
{
$user->$key = $value;
}
return $user;
}
}
$mapper = new UserMapper();
$user = $mapper->find(1); // assuming the user in the previous example was id 1
echo $user->first_name; // Joey
этот код должен дать представление о том, как создать код таким образом. Я не тестировал это, поэтому я, возможно, создал некоторые опечатки / синтаксические ошибки, как я написал. Как и другие упоминали, Zend позволяет вам делать то, что вы хотите с моделями, нет правильного и неправильного, это действительно зависит от вас. Обычно я создаю класс таблицы для каждой таблицы в БД, с которой хочу работать. Поэтому, если у меня есть таблица пользователей, у меня обычно есть объект User, User Mapper и класс User Table. UserTable расширит Zend_Db_Table_Abstract и в зависимости от того, что я делаю, не будет иметь никаких методов внутри или иногда я буду перезаписывать методы, такие как вставить или удалить в зависимости от моих потребностей. Я заканчиваю с большим количеством файлов, но я считаю, что разделение кода делает его намного проще быстро добраться туда, где мне нужно, чтобы добавить больше функциональности или исправить ошибку, так как я знаю, где все части кода будут.
надеюсь, что это помогает.
Структура Папок
application
--models
----DbTable
------User.php
--controllers
----IndexController.php
--forms
----User.php
--views
----scripts
------index
--------index.phtml
применение / модели/DbTable / потребитель.в PHP
class Application_Model_DbTable_User extends Zend_Db_Table_Abstract
{
protected $_name = 'users';
protected $_primary = 'user_id';
}
приложения/формы/пользователей.в PHP
class Form_User extends Zend_Form
{
public function init()
{
$this->setAction('')
->setMethod('post');
$user_name = new Zend_Form_Element_Text('user_name');
$user_name->setLabel("Name")->setRequired(true);
$user_password = new Zend_Form_Element_Text('user_password');
$user_password->setLabel("Password")->setRequired(true);
$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Save');
$this->addElements(array(
$user_name,
$user_password,
$submit
));
}
}
приложение/контроллеры/IndexController.в PHP
class IndexController extends Zend_Controller_Action
{
public function init()
{
}
public function indexAction()
{
$form = new Form_User();
if($this->getRequest()->isPost() && $form->isValid($this->getRequest()->getPost()))
{
$post = $this->getRequest()->getPost();
unlink($post['submit']);
$ut = new Application_Model_DbTable_User();
if($id = $ut->insert($post))
{
$this->view->message = "User added with id {$id}";
} else {
$this->view->message = "Sorry! Failed to add user";
}
}
$this->view->form = $form;
}
}
application/views/scripts/index / index.phtml, который
echo $this->message;
echo $this->form;