Реализация «Уровня моделей» является значительной частью любой MVC системы. Она представляет данные из приложения, а большинство приложений без данных бесполезны. Magento Модели играют еще большую роль, поскольку они, как правило, содержат «бизнес-логику», которую часто (правильно или неправильно) относят к методам Контроллера или Хелперов в других PHP MVC-фреймворках.
Если определение MVC нечеткое, то определение модели (Model) является еще более размытым. До широкого внедрения MVC подхода, доступ к данным, как правило, происходил через сырой SQL запрос и/или SQL-абстракцию. Разработчики писали запросы и особо не задумывались об объектах, которые были моделированы.
В наши дни сырой SQL, в основном, не одобряется, но многие PHP фреймворки так и остаются SQL центрированными. Модели будут объектами, которые будут предоставлять определенный уровень абстракции, но некоторые разработчики до сих пор пишут SQL и/или вызывают SQL в качестве методов абстракции для чтения и записи своих данных.
Другие фреймворки избегают сырого SQL и предпочитают подход объектно-реляционного отображения (ORM – Object Relational Mapping). В этом случае разработчик имеет дело только с Объектами. Свойства являются набором, и когда метод сохранения вызывается в объекте, данные автоматически записываются в базу данных. Некоторые ORM пытаются угадать свойства объекта из базы данных, другие же требуют от пользователя указывать их определенным образом (обычно с использованием абстрактного языка данных, такого как YAML). Одним из самых известных и популярных реализаций этого подхода является ActiveRecord.
Надеюсь, что этого определения ORM сейчас будет достаточно для понимания его сути.
Не удивляйтесь, что Magento отдало предпочтение ORM подходу. В то время как абстракции Zend Framework SQL доступны, наибольший процент доступа к данным будет происходить вами через встроенные в Magento Модели и те Модели, которые вы построите сами. Не должно также быть неожиданностью, что Magento имеет высокую гибкость и высокую абстрактность, что иногда может размывать само понятие Модели.
Строение Magento Модели
Большинство Magento Моделей можно классифицировать двумя путями. Первый – это по типу ActiveRecord Модели (один-объект-одна-таблица), и второй – это EAV (Entity Attribute Value) Модель. Каждая Модель также получает коллекцию моделей. Коллекции – это PHP объекты, используемые для хранения определенного числа экземпляров отдельных моделей Magento. Команда Мадженто реализовала стандартную библиотеку PHP интерфейсов IteratorAggregate и Countable, что позволяет каждому типу модели иметь свой собственный тип коллекции. Если вы не знакомы со стандартной библиотекой PHP, думайте о коллекции моделей как о массивах, которые также имеют прикрепленные методы.
Модели Magento не содержат кода для подключения к базе данных. Зато каждая модель использует класс modelResource
, используемый для доступа к серверу базы данных (через одно чтение и одну запись в адаптере объекта). Отделив логическую модель и код, который общается с базой данных, теоретически возможно написать новые классы ресурсов для различных платформ и схем баз данных держа при этом, свои модели нетронутыми.
Следующий шаг мы будем делать только на период разработки, и никогда не на работающем сайте – это включение режима разработчика Magento. Этот режим, среди прочего, показывает ошибки кода в браузере, что будет полезным при отладке кода.
Включите режим разработчика с помощью редактирования файла .htaccess
в корневой папке сайта. Добавьте строку в начале этого файла:
SetEnv MAGE_IS_DEVELOPER_MODE "true"
Для начала мы создадим базовую модель Magento. Традиция PHP MVC настаивает, чтобы мы моделировали пост веб-блога. Для этого нам нужно реализовать следующие шаги:
Из предыдущих статей этой серии вы можете освежить память по созданию каркаса модулей, поэтому я пропущу подробности и буду считать, что вы уже в состоянии создать пустой модуль под названием Weblog
. После того как вы это сделали, мы настроим маршрут для index экшен контроллера с названием для действия (экшен) «testModel
«. Как всегда, следующие примеры будут принадлежать пакету модулей с именем «Alanstormdotcom
» (вы же должны использовать собственное пространство имен).
В файле конфига Alanstormdotcom/Weblog/etc/config.xml
устанавливаем следующую маршрутизацию:
<frontend> <routers> <weblog> <use>standard</use> <args> <module>Alanstormdotcom_Weblog</module> <frontName>weblog</frontName> </args> </weblog> </routers> </frontend>
и затем добавьте index контроллер в файле Alanstormdotcom/Weblog/controllers/IndexController.php
:
class Alanstormdotcom_Weblog_IndexController extends Mage_Core_Controller_Front_Action { public function testModelAction() { echo 'Setup!'; } }
Очистите кэш Magento и загрузите следующий адрес, чтобы убедиться, что все это было настроено корректно:
VASH-DOMEN/weblog/index/testModel
Вы должны увидеть слово «Setup!» на белом фоне.
Magento имеет систему автоматического создания и изменения схемы вашей базы данных но, на данный момент, мы просто вручную создадим таблицу для нашей модели.
Используя командную строку, или свое любимое приложение по работе с MySQL, создадим таблицу со следующей схемой:
CREATE TABLE `blog_posts` ( `blogpost_id` int(11) NOT NULL auto_increment, `title` text, `post` text, `date` datetime default NULL, `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP, PRIMARY KEY (`blogpost_id`) )
и потом заполним эту новую таблицу некоторыми данными:
INSERT INTO `blog_posts` VALUES (1,'Заголовок','Это новая запись для блога','2017-09-01 00:00:00','2017-09-02 23:10:00');
В нашем config.xml
есть три отдельные вещи, которые мы должны настроить для модели:
entity
) настройки таблицы к нашему Ресурсу модели.
При создании экземпляра модели в Magento, вы делаете обращение вроде этого:
$model = Mage::getModel('weblog/blogpost');
Первая часть URI передает в getModel
групповое название модели (задается в config.xml). Поскольку следование конвенции является хорошей идеей, групповое название должно быть названием вашего модуля (в нижнем регистре), или же для защиты от конфликтов – и название пакета модулей и название модуля (также в нижнем регистре). Вторая часть URI – это версия имени Модели в нижнем регистре.
Итак, давайте добавим нижеследующий XML в файл config.xml
нашего модуля:
<global> <!-- ... --> <models> <weblog> <class>Alanstormdotcom_Weblog_Model</class> <!-- нужно создать свой собственный ресурс, нельзя просто использовать core_resource --> <resourceModel>weblog_resource</resourceModel> </weblog> </models> <!-- ... --> </global>
Внешний тег <weblog/>
— это групповое название модели, должно соответствовать имени вашего модуля. <class/>
— это базовое имя всех Моделей, которые будут в группе weblog (также называется префиксом класса). Тег <resourceModel/>
указывает, какой Ресурс Модели эта группа Моделей weblog должна использовать. Ниже об этом будет больше сказано, но пока будем знать, что название ресурса модели состоит из названия группы, затем идет нижнее подчеркивание и дальше символьная строка «resource
«.
Мы еще не завершили, но давайте посмотрим, что произойдет, если мы очистим кэш Magento и попробуем создать экземпляр Модели blogpost. В методе testModelActio
n контроллера используйте следующий код:
public function testModelAction(){ $blogpost = Mage::getModel('weblog/blogpost'); echo get_class($blogpost); }
и перезагрузите страницу. Вы должны увидеть исключение, которое выглядит примерно так (если вы включили режим разработчика в начале урока):
Warning: include(Alanstormdotcom/Weblog/Model/Blogpost.php) [function.include]: failed to open stream: No such file or directory
Пытаясь получить модель weblog/blogpost
, вы говорите Magento создать экземпляр класса с именем
Alanstormdotcom_Weblog_Model_Blogpost
Magento пытается включить в __autoload
эту модель, но не может найти подходящий файл класса. Значит давайте создадим его! Создайте класс по следующему адресу:
Файл: app/code/local/Alanstormdotcom/Weblog/Model/Blogpost.php class Alanstormdotcom_Weblog_Model_Blogpost extends Mage_Core_Model_Abstract { protected function _construct(){ $this->_init('weblog/blogpost'); } }
Перезагрузите страницу и исключение должно быть заменено именем вашего класса.
Все основные модели, которые взаимодействуют с базой данных, должны расширять класс Mage_Core_Model_Abstract
. Этот абстрактный класс заставляет вас реализовать один метод с именем _construct
(ПРИМЕЧАНИЕ: это не конструктор PHP __construct
). Этот метод должен вызывать метод класса _init
с тем же идентифицирующим URI, который вы будете использовать при вызове метода Mage::getModel
.
Итак, мы настроили нашу модель. Далее нам нужно установить наш Ресурс модели. Ресурсы Модели содержат код, который непосредственно общается с нашей базой данных. В последнем разделе конфигурации мы включили следующее:
<resourceModel>weblog_resource</resourceModel>
Значение в <resourceModel/>
будет использоваться для создания экземпляра класса Ресурса модели. Хотя мы никогда не будем вызывать его самостоятельно, когда любая модель в группе weblog нуждается в общении с базой данных, Magento будет обрабатывать следующий метод для получения Ресурса модели.
Mage::getResourceModel('weblog/blogpost');
Опять же, weblog это имя Группы, а blogpost – это Модель. Метод Mage::getResourceModel
будет использовать weblog/blogpost
URI, чтобы осмотреть глобальную конфигурацию и получить значение <resourceModel>
(в данном случае weblog_resource
). Затем класс модели станет экземпляром с последующим URI:
weblog_resource/blogpost
Итак, если вы внимательно следовали за уроком, то поняли, что ресурсные модели настраиваются в том же разделе config.xml, что и обычные модели. Это может ввести в заблуждение как новичков, так и опытных пользователей.
Поэтому, помня это, давайте настроим наш ресурс. В секции <models>
добавим
<global> <!-- ... --> <models> <!-- ... --> <weblog_resource> <class>Alanstormdotcom_Weblog_Model_Resource</class> </weblog_resource> </models> </global>
Мы добавляем тег <weblog_resource/>
, который является значением тега <resourceModel/>
, который мы только что установили. Значение тега <class/>
— это базовое имя всех ваших ресурсов моделей, и должен быть именован в следующем формате:
Packagename_Modulename_Model_Resource
Итак, мы имеем настроенный ресурс, давайте попробуем загрузить некоторые данные модели. Измените экшен метод в контроллере следующим образом:
public function testModelAction() { $params = $this->getRequest()->getParams(); $blogpost = Mage::getModel('weblog/blogpost'); echo("Loading the blogpost with an ID of ".$params['id']); $blogpost->load($params['id']); $data = $blogpost->getData(); var_dump($data); }
а затем загрузите следующий адрес в браузере (после очистки кэша Magento):
VASH-DOMEN/weblog/index/testModel/id/1
Вы должны увидеть исключение вида:
Warning: include(Alanstormdotcom\Weblog\Model\Resource\Blogpost.php) [function.include]: failed to open stream: No such file or directory in …
Вы, наверное, интуитивно поняли, что мы должны добавить класс ресурсов для нашей модели. Каждая Модель имеет свой собственный класс ресурсов. Добавьте такой класс по следующему адресу:
Файл: app/code/local/Alanstormdotcom/Weblog/Model/Resource/Blogpost.php class Alanstormdotcom_Weblog_Model_Resource_Blogpost extends Mage_Core_Model_Resource_Db_Abstract{ protected function _construct() { $this->_init('weblog/blogpost', 'blogpost_id'); } }
Опять же, первый параметр init
метода это URL, используемый для идентификации Модели. Второй параметр – это поле базы данных, которое однозначно идентифицирует любую конкретную колонку. В большинстве случаев это должно быть первичным ключом (primary key). Очистите кэш, перезагрузите страницу, и вы увидите:
Can't retrieve entity config: weblog/blogpost
Еще одно исключение! Когда мы используем weblog/blogpost
URI Модели, мы говорим Magento, что нам нужна Группа Моделей weblog
и сущность blogpost
. В контексте простых Моделей, расширяющих класс Mage_Core_Model_Resource_Db_Abstract
, сущность соответствует таблице в базе данных. В этом случае таблица называется blog_post
, которую мы создали выше. Добавим эту сущность в наш XML-файл конфигурации.
<models> <!-- ... ---> <weblog_resource> <class>Alanstormdotcom_Weblog_Model_Resource</class> <entities> <blogpost> <table>blog_posts</table> </blogpost> </entities> </weblog_resource> </models>
Мы добавили новый раздел <entities/>
к разделу ресурса Модели в файл конфигурации модуля. А в этом разделе есть другой раздел, названный именем нашей сущности (<blogpost/>
), который определяет название таблицы базы данных, которую мы хотим использовать для этой модели.
Очистите кэш Magento, скрестите пальцы, перезагрузите страницу и …
Loading the blogpost with an ID of 1 array(5) { ["blogpost_id"]=> string(1) "1" ["title"]=> string(18) "Заголовок" ["post"]=> string(44) "Это новая запись для блога" ["date"]=> string(19) "2017-09-01 00:00:00" ["timestamp"]=> string(19) "2017-09-02 23:10:00" }
Эврика! Нам удалось извлечь наши данные и, что более важно, полностью настроить Magento Модель.
Все модели Magento унаследованы от класса Varien_Object
. Этот класс является частью системной библиотеки Magento и не частью любого модуля ядра (core) Magento. Вы можете найти этот объект в
lib/Varien/Object.php
Magento Модели сохраняют свои данные в защищенных _data
свойствах. Класс Varian_Object
дает нам несколько методов, которые мы можем использовать для доступа к этим данным. Вы уже видели метод getData
, который возвращает массив пары key/value
. Этому методу можно передать строковое значение ключа, чтобы получить значение конкретного поля.
$model->getData(); $model->getData('title');
Также есть метод getOrigData
, который будет возвращать данные модели в том состоянии, когда объект только был инициирован (обращается к защищенному методу _origData
).
$model->getOrigData(); $model->getOrigData('title');
Varien_Object
также реализует некоторые специальные методы через магический php-метод __call
. Так, вы можете получать (get), устанавливать (set), исключать (unset) или проверять (has) наличие каких-либо свойств, используя методы, начинающиеся со слова get
, set
, unset
или has
, и которые сопровождаются названием свойства в верблюжьем регистре.
$model->getBlogpostId(); $model->setBlogpostId(25); $model->unsetBlogpostId(); if($model->hasBlogpostId()){...}
По этой причине в будущем вы захотите именовать все столбцы в базе данных только маленькими буквами (не прописными) и будете использовать подчеркивание для разделения слов в названии.
Модели Magento поддерживают основную функциональность CRUD (создание, чтение, обновление и удаление) методами load
, save
и delete
. Вы уже видели загрузки этих методов в действиях (action) контроллера. Когда передается один параметр, метод load
вернет запись с таким id
поля (устанавливается в ресурсе Модели), который сравнивается со значением.
$blogpost->load(1);
Метод save
позволит вам или вставлять (INSERT
) новую модель в базу данных, или обновлять (UPDATE
) уже существующую. Добавьте к вашему контроллеру следующий метод:
public function createNewPostAction() { $blogpost = Mage::getModel('weblog/blogpost'); $blogpost->setTitle('Code Post!'); $blogpost->setPost('This post was created from code!'); $blogpost->save(); echo 'post with ID ' . $blogpost->getId() . ' created'; }
а дальше выполняем этот метод, загружая в браузере следующий URL:
VASH-DOMEN/weblog/index/createNewPost
После этого вы должны увидеть новый, добавленный в таблицу blog_posts
базы данных, пост. Далее попробуйте следующий код в контроллере для редактирования первого поста:
public function editFirstPostAction() { $blogpost = Mage::getModel('weblog/blogpost'); $blogpost->load(1); $blogpost->setTitle("The First post!"); $blogpost->save(); echo 'post edited'; }
И, наконец, вы можете удалить пост, используя очень похожий синтаксис:
public function deleteFirstPostAction() { $blogpost = Mage::getModel('weblog/blogpost'); $blogpost->load(1); $blogpost->delete(); echo 'post removed'; }
Так, иметь одну модель полезно, но иногда нам нужно работать с несколькими подобными моделями. Вместо того, чтобы возвращать обычный массив моделей, каждый тип модели Magento имеет уникальный, связанный с ним объект коллекции. Эти объекты реализуются php-интерфейсами IteratorAggregate и Countable. Это означает, что они могут быть переданы count
функции, и использоваться в конструкциях foreach
.
В следующих статьях мы раскроем Коллекции более полно. А теперь давайте посмотрим на типичные настройки и использование. Добавьте следующий метод к контроллеру и загрузите его в своем браузере
public function showAllBlogPostsAction() { $posts = Mage::getModel('weblog/blogpost')->getCollection(); foreach($posts as $blogpost){ echo '<h3>'.$blogpost->getTitle().'</h3>'; echo nl2br($blogpost->getPost()); } }
открыв ссылку:
VASH-DOMEN/weblog/index/showAllBlogPosts
Вы должны увидеть знакомое исключения (exception):
Warning: include(Alanstormdotcom\Weblog\Model\Resource\Blogpost\Collection.php) [function.include]: failed to open stream…
Вы же не удивились, увидев исключения, не так ли? Нам нужно добавить php-файл класса, который определит нашу коллекцию Blogpost. Каждая модель имеет защищенное (protected) свойство с именем _resourceCollectionName
, которое содержит URI, что используется для идентификации нашей коллекции.
protected '_resourceCollectionName' => string 'weblog/blogpost_collection'
По умолчанию, это же URI используется для идентификации Ресурса Модели, со строкой _collection
добавленной в конец. Magento считает Коллекции частью Ресурса, следовательно этот URI конвертируется в имя класса:
Alanstormdotcom_Weblog_Model_Resource_Blogpost_Collection
Поэтому добавьте следующий PHP класс по адресу:
Файл: app/code/local/Magentotutorial/Weblog/Model/Resource/Blogpost/Collection.php class Alanstormdotcom_Weblog_Model_Resource_Blogpost_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract { protected function _construct() { $this->_init('weblog/blogpost'); } }
Так же, как и с другими нашими классами, мы должны инициировать нашу Коллекцию URI Модели (weblog/blogpost
). Обновите страницу и вы должны увидеть информацию всех своих постов.
Поздравляем, вы создали и настроили вашу первую Модель Magento. В следующей статье мы сделаем обзор Entity Attribute Value (EAV) Модели Magento, что расширит уже нами изученное.
Автор: Alan Storm (http://alanstorm.com/magento_models_orm/)
Перевод на русский: SebWeo
Выбор мастера для ремонта и перетяжки мебели — задача, которая требует вдумчивого подхода. Ведь от…
Выбор идеального хостинга под свой сайт может быть довольно запутанным делом, особенно когда существует так…
Чтобы избегать ошибок, нужно набираться опыта; чтобы набираться опыта, надо делать ошибки Лоуренс Питер
Краткое определение Черного SEO Черное СЕО (или Черная оптимизация) — это любая практика, целью которой…
Получение водительских прав категории C открывает двери к профессиональной деятельности, связанной с управлением грузовыми автомобилями.…