Мы предполагаем, что вы уже знакомы с процедурой создания каркаса приложения на базе пакета WEB_APP.
Краткие шаги:
Модифицируем немного файл shop/setup.php:
<?php [...] require_once('limb/core/common.inc.php'); require_once('limb/web_app/common.inc.php'); require_once('common.inc.php'); ?>
Создадим файл common.inc.php. Его мы будем использовать для подключения наиболее часто используемых классов.
Файл shop/common.inc.php:
<?php lmb_require('limb/web_app/src/controller/lmbController.class.php'); lmb_require('limb/active_record/src/lmbActiveRecord.class.php'); ?>
Это самые часто используемые классы в нашем приложении. lmb_require() использует отложенную загрузку классов, так что сильного влияния на производительность эти две строки не будут иметь.
Проанализировав требования к нашему приложению, можно сделать вывод, что нам потребуется хранить в базе данных следующие сущности:
Все сущности мы будем реализовывать при помощи класса lmbActiveRecord пакета ACTIVE_RECORD. Напомним, что условием использования ACTIVE_RECORD является наличие автоинкременстного поля id в таблице.
Таблица product:
CREATE TABLE `product` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `title` VARCHAR(255) DEFAULT NULL, `description` text, `is_available` tinyint(1) DEFAULT NULL, `price` FLOAT DEFAULT NULL, `image_name` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
Таблица user:
CREATE TABLE `user` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) DEFAULT NULL, `login` VARCHAR(30) DEFAULT NULL, `hashed_password` VARCHAR(32) DEFAULT NULL, `email` VARCHAR(255) DEFAULT NULL, `is_admin` tinyint(1) DEFAULT NULL, `address` text, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
Мы собираемся хранить всех наших пользователей в одной таблице. Наша система прав предполагается также очень простой, поэтому поля is_admin вполне хватит. Поле address будет использоваться для автоподстановки адреса доставки при отправке заказов покупателями.
Таблица order:
CREATE TABLE `order` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `user_id` BIGINT(20) NOT NULL DEFAULT '0', `date` BIGINT(20) NOT NULL DEFAULT '0', `summ` FLOAT DEFAULT NULL, `status` INT(11) DEFAULT NULL, `address` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
Таблица order_line:
CREATE TABLE `order_line` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT, `order_id` BIGINT(20) NOT NULL DEFAULT '0', `product_id` BIGINT(20) DEFAULT NULL, `quantity` INT(11) DEFAULT NULL, `price` INT(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `order_id` (`order_id`), KEY `product_id` (`product_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
Нам необходимо сохранить цену товара на момент покупки, поэтому таблица order_line имеет поле price.
Если вы выбрали копию готового примера с svn.limb-project.com/limb/3.x/examples/shop или с PEAR канала, тогда SQL-код структуры базы данных можно найти в файле shop/init/db.mysql
Создайте папку shop/www/product_images/. В этой папке мы будем хранить изображения для наших товаров.
Убедитесь, что веб-сервер имеет права на чтение и запись в эту папку.
Также скопируйте файл shop/www/images/no_image.gif в соответствующую папку своего проекта. Это изображение будет использоваться для товаров, у которых не будет изображения.
Теперь поправьте содержимое файла shop/setup.php следующим образом:
[...] set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path()); @define('LIMB_VAR_DIR', dirname(__FILE__) . '/var/'); @define('PRODUCT_IMAGES_DIR', dirname(__FILE__) . '/www/product_images/'); require_once('limb/core/common.inc.php'); [...]
Константа PRODUCT_IMAGES_DIR хранит абсолютный путь до папки с изображениями.
Мы начнем наше приложение с панели управления.
Для панели управления создадим также отдельный контроллер, AdminController (shop/src/controller/AdminController.class.php):
<?php class AdminController extends lmbController { }
Создадим шаблоны для главной страницы панели управления, а также основной шаблон, который будет базой (базовый шаблон - враппер) для всех страниц панели управления.
Файл shop/template/admin/display.html:
<core:set title='Main admin page'/> <core:wrap file='admin_page.html' as='content'> <b>Wellcome to Shop Example control panel!</b> </core:wrap>
Файл shop/template/admin_page.html
<html> <head> <title>{$title} :: Limb shop example application</title> <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/> <link rel=stylesheet type="text/css" href="/styles/main.css"/> </head> <body> <div id="header"> <div class="center"> <img src="/images/logo.limb.gif" width='153' height='58' alt='logo.limb' id='logo'/> <div id="title"> <span>Limb Shop Example</span><br /> control panel </div> </div> </div> <div id="center"> <div id="wrapper" > <div id="container"> <div id="content"> <h1>{$title}</h1> <core:placeholder id='content'/> </div> </div> <div id="sidebar"> <div id="navigation"> <ul> <li><a href="/admin_product">Products</a></li> <li><a href="/admin_order">Orders</a></li> <li><a href="/admin_user">Users</a></li> </ul> </div> <dl id="profile"> <dt>Profile</dt> <dd> Not yet. implemented! </dd> </dl> </div> </div> </div> </body> </html>
При помощи <core:set> тега в шаблоне shop/template/admin/display.html мы установили заголовок страницы.
Мы позволили себе сразу же немного приукрасить наши шаблоны. Необходимые стили и изображения можно найти в папках shop/www/styles/ и shop/www/images/ готового приложения.
Надеемся, что WACT-теги <core:wrap> и <core:placeholder> вам уже знакомы по первому примеру. Вы также можете подробнее прочитать про композицию WACT-шаблонов чтобы вспомнить, как в WACT шаблоны собираются в единое целое из различных частей.
Область, которая лежит в <dl id='profile'> мы пока оставили пустой. Профайлом пользователя мы займемся на шаге 4.
Теперь создадим шаблон для главной фронтальной страницы нашего приложения.
Файл shop/template/main_page/display.html:
<core:set title='Main page'/> <core:wrap file="page.html" in="content"> Welcome to our bookstore! </core:wrap>
Файл shop/template/page.html:
<html> <head> <title>{$title} :: Limb shop example application</title> <meta http-equiv='Content-Type' content='text/html; charset=utf-8'/> <link rel=stylesheet type="text/css" href="/styles/main.css"/> </head> <body> <div id="header"> <div class="center"> <img src="/images/logo.limb.gif" width='153' height='58' alt='logo.limb' id='logo'/> <div id="title"> <span>Limb Shop Example</span><br /> front page </div> </div> </div> <div id="center"> <div id="wrapper" > <div id="container"> <div id="content"> <h1>{$title}</h1> <core:placeholder id='content'/> </div> </div> <div id="sidebar"> <div id="navigation"> <ul> <li><a href="/product">Products</a></li> <li><a href="/cart">Your Cart</a></li> </ul> </div> <dl id="profile"> <dt>Profile</dt> <dd> Not yet implemented. </dd> </dl> </div> </div> </div> </body> </html>
Разница между page.html и admin_page.html у нас назначительная, но все равно решили сделать их отдельными файлами, так как различия будут добавляться по мере развития проекта. Также мы будем использовать одинаковые вспомогательные шаблоны:
Далее мы очишаем кеш в папке shop/var/ и пробуем зайти на страницу http://your_shop_example_domain/admin/ (далее просто /admin)
… и http://your_shop_example_domain/
Создадим также шаблон красиво отображающий 404 ошибку (страница не найдена):
Файл shop/template/not_found.html:
<core:set title='Not found' /> <core:wrap file="page.html" in="content"> <b>Error 404.</b> <p>Page not found.</p> </core:wrap>
Шаблон not_found.html используется по-умолчанию контроллером NotFoundController (который можно найти в папке limb/web_app/src/controller/).
Попробуйте зайти на несущесвующую страницу, например /no_such_page, и вы увидите как отработает именно этот шаблон (возможно вам придется еще раз почистить кеш shop/var/).
Следующий шаг - "Шаг3. Создание и отображение списка товаров для администраторов". Мы создадим для администраторов сайта список товаров с разделением на страницы, сделаем формы для создания нового товара и редактирования старых, реализуем действие для удаления товаров.
Большинство того, что будет сделано на 3-м шаге вам будет уже знакомо по первому примеру.
Обсуждение