Строим свою CMS на PHP и MySQL. Часть 7

В предыдущем уроке серии мы начали процесс добавления функционала категорий в нашу простую CMS. Рассмотрим, что нужно поменять в коде классов для реализации задуманных изменений.

Создание класса Category


Также как ранее мы создавали класс Article для управления хранением и выводом статей, нам нужно построить класс Category для решения таких же задач с категориями.

В папке cms имеется каталог classes. В каталоге classes создаем новый файл Category.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:

Код

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:

Код

   
/**
  * Класс для управления статьями
  */
   
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. Затем модифицируем выражения SQL SELECT для включения переменной $categoryClause. Также добавляем новый вызов к $st->bindValue() для привязки заданного $categoryId.

- Изменяем методы insert() и update()
Также мы модифицировали методы класса Article insert() и update() для работы с новым полем categoryId. Изменили запросы SQL INSERT и UPDATE в каждом методе, и добавили новые вызовы к bindValue() для передачи свойства $categoryId в выражения SQL.

В следующем уроке серии мы изменим шаблоны клиентской и серверной частей для работы с категориями.

  • FalleN

  • 2285

  • 1

  • 204
Теги: php, cms, MySQL

Ссылки на статью:

Похожие статьи: