Этот урок связан с проектом Строим свою CMS на PHP и MySQL

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

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

demosourse

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

  • Изменяем методы insert() и update()

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

 

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

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: www.elated.com/articles/add-article-categories-to-your-cms/
Перевел: Сергей Фастунов
Урок создан: 12 Декабря 2012
Просмотров: 30954
Правила перепечатки


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 сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.

или авторизуйтесь, чтобы добавлять комментарии, оценивать уроки и сохранять их в личном кабинете
  • 13 Декабря 2012 05:17
    SerJik67
    ИЗВИНИТЕ НЕ ПО ТЕМЕ, но очень нужна помощь. Есть БД и есть в ней коментарии, вопрос такой: "Как отсортировать вывод кометариев на страницу? Мне нужно первое, это чтобы сортировка начиналась по ID ASC, при этом с LIMIT 20. НО, ЧТОБЫ ЭТОТ ЛИМИТ ОТСЧИТЫВАЛ ID С КОНЦА". Допустим есть 100 коментариев, нужно вывести 20 кометариев начиная от 80 к 100 коментарию в порядке возростания (тоесть: начиная с верхнего 81 коментария и заканчивая конечным - 100 коментарием). Естественно числа эти не константа сегодня их 100 а завтра будет больше, ведь люди коментируют.
    • 13 Декабря 2012 10:27
      noganno
      Противоречишь самому себе. Если ты хочешь вывести последние 20 комментариев, то применяй ORDER by ID DESC LIMIT 20.
      • 13 Декабря 2012 11:55
        SerJik67
        Я хочу их вывести не наоборот а по порядку но только последние 20
        • 14 Декабря 2012 13:59
          Dalaylama14
          ORDER by ID DESC LIMIT 20 Потом отсортируй массив http://php.net/manual/ru/array.sorting.php
          • 14 Декабря 2012 17:34
            webivan
            Выведи колличество записей в переменную $count далее простой запрос ORDER BY id LIMIT $count-20,20
          • 19 Декабря 2012 18:42
            biohazardo
            select * from (select * from table order by id desc limit 20) order by id asc
  • 8 Февраля 2013 22:55
    proctoLeha
    В предыдущем уроке создавали (модифицировали) таблицы в БД для категорий и статей. Среди прочего говорилось о том, что
    Поле, которое связывает одну
    таблицу с другой называется
    как внешний ключ (foreign key)
    Ну и где этот foreign key? Это я к тому, что если все таки его использовать, то при удалении категории, все связанные записи удалятся автоматом, и прописывать лишний метод delete у класса Article не придется. Вроде мелочь, а впечатление о коде и логике разрабов портит.
  • 13 Апреля 2016 15:50
    sense
    Отрышка а не статья. Кто каждый раз делает $conn = new PDO( DB_DSN, DB_USERNAME, DB_PASSWORD );
^ Наверх ^