Строим свою CMS на PHP и MySQL. Часть 7
В предыдущем уроке серии мы начали процесс добавления функционала категорий в нашу простую CMS. Рассмотрим, что нужно поменять в коде классов для реализации задуманных изменений.
Создание класса Category
Также как ранее мы создавали класс Article
для управления хранением и выводом статей, нам нужно построить класс Category
для решения таких же задач с категориями.
В папке cms
имеется каталог classes
. В каталоге classes
создаем новый файл Category.php
и копируем в него следующий код:
<?php /** * Класс для обработки категорий статей */ class Category { // Свойства /** * @var int ID категории из базы данных */ public $id = null; /** * @var string Название категории */ public $name = null; /** * @var string Короткое описание категории */ public $description = null; /** * Устанавливаем свойства объекта с использованием значений в передаваемом массиве * * @param assoc Значения свойств */ public function __construct( $data=array() ) { if ( isset( $data['id'] ) ) $this->id = (int) $data['id']; if ( isset( $data['name'] ) ) $this->name = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['name'] ); if ( isset( $data['description'] ) ) $this->description = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['description'] ); } /** * Устанавливаем свойства объекта с использованием значений из формы редактирования * * @param assoc Значения из формы редактирования */ public function storeFormValues ( $params ) { // Store all the parameters $this->__construct( $params ); } /** * Возвращаем объект Category, соответствующий заданному ID * * @param int ID категории * @return Category|false Объект Category object или false, если запись не была найдена или в случае другой ошибки */ public static function getById( $id ) { $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $sql = "SELECT * FROM categories WHERE id = :id"; $st = $conn->prepare( $sql ); $st->bindValue( ":id", $id, PDO::PARAM_INT ); $st->execute(); $row = $st->fetch(); $conn = null; if ( $row ) return new Category( $row ); } /** * Возвращаем все (или диапазон) объектов Category из базы данных * * @param int Optional Количество возвращаемых строк (по умолчаниюt = all) * @param string Optional Столбец, по которому сортируются категории(по умолчанию = "name ASC") * @return Array|false Двух элементный массив: results => массив с объектами Category; totalRows => общее количество категорий */ public static function getList( $numRows=1000000, $order="name ASC" ) { $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $sql = "SELECT SQL_CALC_FOUND_ROWS * FROM categories ORDER BY " . mysql_escape_string($order) . " LIMIT :numRows"; $st = $conn->prepare( $sql ); $st->bindValue( ":numRows", $numRows, PDO::PARAM_INT ); $st->execute(); $list = array(); while ( $row = $st->fetch() ) { $category = new Category( $row ); $list[] = $category; } // Получаем общее количество категорий, которые соответствуют критериям $sql = "SELECT FOUND_ROWS() AS totalRows"; $totalRows = $conn->query( $sql )->fetch(); $conn = null; return ( array ( "results" => $list, "totalRows" => $totalRows[0] ) ); } /** * Вставляем текущий объект Category в базу данных и устанавливаем его свойство ID. */ public function insert() { // У объекта Category уже есть ID? if ( !is_null( $this->id ) ) trigger_error ( "Category::insert(): Attempt to insert a Category object that already has its ID property set (to $this->id).", E_USER_ERROR ); // Вставляем категорию $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $sql = "INSERT INTO categories ( name, description ) VALUES ( :name, :description )"; $st = $conn->prepare ( $sql ); $st->bindValue( ":name", $this->name, PDO::PARAM_STR ); $st->bindValue( ":description", $this->description, PDO::PARAM_STR ); $st->execute(); $this->id = $conn->lastInsertId(); $conn = null; } /** * Обновляем текущий объект Category в базе данных. */ public function update() { // У объекта Category есть ID? if ( is_null( $this->id ) ) trigger_error ( "Category::update(): Attempt to update a Category object that does not have its ID property set.", E_USER_ERROR ); // Обновляем категорию $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $sql = "UPDATE categories SET name=:name, description=:description WHERE id = :id"; $st = $conn->prepare ( $sql ); $st->bindValue( ":name", $this->name, PDO::PARAM_STR ); $st->bindValue( ":description", $this->description, PDO::PARAM_STR ); $st->bindValue( ":id", $this->id, PDO::PARAM_INT ); $st->execute(); $conn = null; } /** * Удаляем текущий объект Category из базы данных. */ public function delete() { // У объекта Category есть ID? if ( is_null( $this->id ) ) trigger_error ( "Category::delete(): Attempt to delete a Category object that does not have its ID property set.", E_USER_ERROR ); // Удаляем категорию $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $st = $conn->prepare ( "DELETE FROM categories WHERE id = :id LIMIT 1" ); $st->bindValue( ":id", $this->id, PDO::PARAM_INT ); $st->execute(); $conn = null; } } ?>
Данный класс очень похож на класс Article
и немного проще. Он содержит три свойства, которые соответствуют полям в таблице categories
— id
, name
и description,
конструктор __construct()
, который создает новый объект Category
. Также класс имеет метод storeFormValues()
для сохранения данных из формы редактирования, методы для получения одной категории по ID и списка категорий, а также методов для вставки, обновления и удаления категории в базе данных.
Нам нужно включить новый класс Category
в наши файлы кода, чтобы CMS могла получить доступ к нему. Поэтому включаем файл с классом в файл config.php
в папке cms
, также как мы включали Article.php
:
<?php ini_set( "display_errors", true ); date_default_timezone_set( "Australia/Sydney" ); // http://www.php.net/manual/en/timezones.php define( "DB_DSN", "mysql:host=localhost;dbname=cms" ); define( "DB_USERNAME", "username" ); define( "DB_PASSWORD", "password" ); define( "CLASS_PATH", "classes" ); define( "TEMPLATE_PATH", "templates" ); define( "HOMEPAGE_NUM_ARTICLES", 5 ); define( "ADMIN_USERNAME", "admin" ); define( "ADMIN_PASSWORD", "mypass" ); require( CLASS_PATH . "/Article.php" ); require( CLASS_PATH . "/Category.php" ); function handleException( $exception ) { echo "Sorry, a problem occurred. Please try later."; error_log( $exception->getMessage() ); } set_exception_handler( 'handleException' ); ?>
Модифицируем Article
Кроме создания нового класса Category
нужно также модифицировать класс Article
для работы с категориями. Ниже приводится код файла Article.php
:
<?php /** * Класс для управления статьями */ class Article { // Свойства /** * @var int ID статьи из базы данных */ public $id = null; /** * @var int Дата публикации статьи */ public $publicationDate = null; /** * @var int ID категории статьи */ public $categoryId = null; /** * @var string Полное название статьи */ public $title = null; /** * @var string Резюме статьи */ public $summary = null; /** * @var string Содержание HTML статьи */ public $content = null; /** * Устанавливаем свойства объекта с использованием значений из массива * * @param assoc Значения свойств */ public function __construct( $data=array() ) { if ( isset( $data['id'] ) ) $this->id = (int) $data['id']; if ( isset( $data['publicationDate'] ) ) $this->publicationDate = (int) $data['publicationDate']; if ( isset( $data['categoryId'] ) ) $this->categoryId = (int) $data['categoryId']; if ( isset( $data['title'] ) ) $this->title = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['title'] ); if ( isset( $data['summary'] ) ) $this->summary = preg_replace ( "/[^\.\,\-\_\'\"\@\?\!\:\$ a-zA-Z0-9()]/", "", $data['summary'] ); if ( isset( $data['content'] ) ) $this->content = $data['content']; } /** * Устанавливаем свойства объекта с использованием значений из формы * * @param assoc Значения из формы */ public function storeFormValues ( $params ) { // Сохраняем все параметры $this->__construct( $params ); // Разбираем и сохраняем дату публикации if ( isset($params['publicationDate']) ) { $publicationDate = explode ( '-', $params['publicationDate'] ); if ( count($publicationDate) == 3 ) { list ( $y, $m, $d ) = $publicationDate; $this->publicationDate = mktime ( 0, 0, 0, $m, $d, $y ); } } } /** * Возвращаем объект Article соответствующий заданному ID * * @param int ID статьи * @return Article|false Объект Article или false, если запись не найдена или в случае другой ошибки */ public static function getById( $id ) { $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $sql = "SELECT *, UNIX_TIMESTAMP(publicationDate) AS publicationDate FROM articles WHERE id = :id"; $st = $conn->prepare( $sql ); $st->bindValue( ":id", $id, PDO::PARAM_INT ); $st->execute(); $row = $st->fetch(); $conn = null; if ( $row ) return new Article( $row ); } /** * Возвращает все (или диапазон) объекты Article из базы данных * * @param int Optional Количество возвращаемых строк (по умолчанию = all) * @param int Optional Вернуть статьи только из категории с указанным ID * @param string Optional Столбц, по которому выполняется сортировка статей (по умолчанию = "publicationDate DESC") * @return Array|false Двух элементный массив: results => массив объектов Article; totalRows => общее количество строк */ public static function getList( $numRows=1000000, $categoryId=null, $order="publicationDate DESC" ) { $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $categoryClause = $categoryId ? "WHERE categoryId = :categoryId" : ""; $sql = "SELECT SQL_CALC_FOUND_ROWS *, UNIX_TIMESTAMP(publicationDate) AS publicationDate FROM articles $categoryClause ORDER BY " . mysql_escape_string($order) . " LIMIT :numRows"; $st = $conn->prepare( $sql ); $st->bindValue( ":numRows", $numRows, PDO::PARAM_INT ); if ( $categoryId ) $st->bindValue( ":categoryId", $categoryId, PDO::PARAM_INT ); $st->execute(); $list = array(); while ( $row = $st->fetch() ) { $article = new Article( $row ); $list[] = $article; } // Получаем общее количество статей, которые соответствуют критерию $sql = "SELECT FOUND_ROWS() AS totalRows"; $totalRows = $conn->query( $sql )->fetch(); $conn = null; return ( array ( "results" => $list, "totalRows" => $totalRows[0] ) ); } /** * Вставляем текущий объек Article в базу данных, устанавливаем его ID. */ public function insert() { // Есть уже у объекта Article ID? if ( !is_null( $this->id ) ) trigger_error ( "Article::insert(): Attempt to insert an Article object that already has its ID property set (to $this->id).", E_USER_ERROR ); // Вставляем статью $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $sql = "INSERT INTO articles ( publicationDate, categoryId, title, summary, content ) VALUES ( FROM_UNIXTIME(:publicationDate), :categoryId, :title, :summary, :content )"; $st = $conn->prepare ( $sql ); $st->bindValue( ":publicationDate", $this->publicationDate, PDO::PARAM_INT ); $st->bindValue( ":categoryId", $this->categoryId, PDO::PARAM_INT ); $st->bindValue( ":title", $this->title, PDO::PARAM_STR ); $st->bindValue( ":summary", $this->summary, PDO::PARAM_STR ); $st->bindValue( ":content", $this->content, PDO::PARAM_STR ); $st->execute(); $this->id = $conn->lastInsertId(); $conn = null; } /** * Обновляем текущий объект Article в базе данных. */ public function update() { // У объекта Article есть ID? if ( is_null( $this->id ) ) trigger_error ( "Article::update(): Attempt to update an Article object that does not have its ID property set.", E_USER_ERROR ); // Обновляем статью $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $sql = "UPDATE articles SET publicationDate=FROM_UNIXTIME(:publicationDate), categoryId=:categoryId, title=:title, summary=:summary, content=:content WHERE id = :id"; $st = $conn->prepare ( $sql ); $st->bindValue( ":publicationDate", $this->publicationDate, PDO::PARAM_INT ); $st->bindValue( ":categoryId", $this->categoryId, PDO::PARAM_INT ); $st->bindValue( ":title", $this->title, PDO::PARAM_STR ); $st->bindValue( ":summary", $this->summary, PDO::PARAM_STR ); $st->bindValue( ":content", $this->content, PDO::PARAM_STR ); $st->bindValue( ":id", $this->id, PDO::PARAM_INT ); $st->execute(); $conn = null; } /** * Удаляем текущий объект Article из базы данных */ public function delete() { // У объекта Article есть ID? if ( is_null( $this->id ) ) trigger_error ( "Article::delete(): Attempt to delete an Article object that does not have its ID property set.", E_USER_ERROR ); // Удаляем объект Article $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD ); $st = $conn->prepare ( "DELETE FROM articles WHERE id = :id LIMIT 1" ); $st->bindValue( ":id", $this->id, PDO::PARAM_INT ); $st->execute(); $conn = null; } } ?>
Рассмотрим изменения в классе Article
подробно:
- Новое свойство
categoryId
Чтобы установить соответствие статьи и категории мы добавили свойство
categoryId
для хранения ID категории, которой принадлежит статья. Также мы модифицировали конструктор__construct()
для сохранения нового свойстваcategoryId
в только что созданных объектахArticle
. - Изменение метода
getList()
Первоначальный вариант метода
getList()
возвращал все статьи в базе данных (или заданное количество записей). Так как нам нужно, чтобы CMS выводила список статей в определенной категории, модифицируем методgetList()
для принятия опционального аргумента$categoryId
. Если он задан, то возвращаются статьи только из указанной категории.Если задан
$categoryId
, создаем строку$categoryClause
для выраженияWHERE
, чтобы вернуть только те статьи, в которых значение поляcategoryId
соответствует значениюcategoryId
. Затем модифицируем выражения SQLSELECT
для включения переменной$categoryClause
. Также добавляем новый вызов к$st->bindValue()
для привязки заданного$categoryId.
- Изменяем методы
insert()
иupdate()
Также мы модифицировали методы класса
Article
insert()
иupdate()
для работы с новым полемcategoryId
. Изменили запросы SQLINSERT
иUPDATE
в каждом методе, и добавили новые вызовы кbindValue()
для передачи свойства$categoryId
в выражения SQL.
В следующем уроке серии мы изменим шаблоны клиентской и серверной частей для работы с категориями.
Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: www.elated.com/articles/add-article-categories-to-your-cms/
Перевел: Сергей Фастунов
Урок создан: 12 Декабря 2012
Просмотров: 53409
Правила перепечатки
5 последних уроков рубрики "PHP"
-
Фильтрация данных с помощью zend-filter
Когда речь идёт о безопасности веб-сайта, то фраза "фильтруйте всё, экранируйте всё" всегда будет актуальна. Сегодня поговорим о фильтрации данных.
-
Контекстное экранирование с помощью zend-escaper
Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.
-
Подключение Zend модулей к Expressive
Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.
-
Совет: отправка информации в Google Analytics через API
Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.
-
Подборка PHP песочниц
Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.