Решила как-то одна небольшая контора написать небольшой сервис, который сразу лег посильной ношей на мои плечи. Так как проект предполагал работу с четырмя видами контента очень схожими на уровне абстракции, было принято решение использовать всю силу ООП со стороны PHP, и хотелось добиться наследования на стороне БД, как это позволяет сделать PostgreSQL. Но в ходе длительных и горячих споров было принято решение в пользу MySQL — дескать устонавливать его проще, и вообще… После нескольких доводов я сдался.
Конечно можно сделать совсем просто — в одной таблице объявить ВСЕ поля ВСЕХ контентов и добавить поле отвечающее за его класс, но это, на мой взгляд, не совсем удачная схема. Но при добавлении нового типа контента в систему могут возникнуть разнообразные проблемы. Как же сделать удобнее?
Что же такого хорошего в наследовании таблиц? А вот что: например у нас имеется 2 вида контента (их было 4): видео и книги. При достаточном размышлении, мы можем придти к мысли что такие параметры как заголовок, описание, рейтинг в системе, количество просмотров/скачиваний и прочую информацию мы можем вынести в базовый класс (в PHP), а вот такие параметры как напрмер количество страниц, продолжительность видео — присущи уже потомкам. Да и операции добавления/удаления и даже комментарии — вполне общие для всех типов. Однозначно — без ООП ни ногой.
Ну а что в базе данных? Мы могли бы ввести «базовую» таблицу content, в ней указать id, rating, votes, comments… а в наследуемых таблицах добавить pages/duration и остальные поля, присущие исключительно типам контента.
Так же внешний ключ таблицы с комментариями привязать к id базовой таблицы, так же повесить триггер, который будет изменять поле количества комментариев при добавлении. Без наследования само собой такие прелести недоступны, в плоть до того, что придется даже создавать 2 одинаковый таблицы комментариев. Жуть, джамшутинг и копипастинг.
Прикинув в уме как бы все-таки не писать 2 разных кода для одних операций, мой мозг посетила светлая мысль, которая сразу же вылиась в код.
Итак, этап первый: создаем базовую таблицу контента content:
Добавляем таблицу комментариев comments:
Добавляем внешние ключи (comments.content_id => content.id ON DELETE CASCADE), добавляем триггеры (на добавление/удаление комментария +- content.comments)
так же создаем функцию vote(cid, uid, rating) — для изменения рейтинга контента.
Еще можно много чего сделать, но думаю этого достаточно, что бы показать идею.
Создаем таблицу books. Как многие догадались — это таблица для контента типа «книга», одна будет «наследовать» таблицу content, со всеми ее (контента) вкусностями.
Но если говорить честнее (и что бы было понятнее), мы всего лишь введем такой подход, при котором одной строке в таблице книг будет однозначно ставиться в соответствие одна запись из таблицы «контент». И вот как этого добиться:
Воодим таблицу books:
создаем внешний ключ (books.cid => content.id ON DELETE CASCADE), и создаем триггер, который при добавлении строки в таблицу books, создает и ставит ей в соответствие строку в таблице content:
Ну и для удаления:
Добавляем строку в таблице books, а работаем с таблицей content: в зависимости от типа сервиса мы выполняем JOIN к нужной таблице (в данном случае books):
Получаем всю информацию.
Очень важно понимать, что для системы нет книг или фильмов — в ней есть контент, который «тянет» за собой дополнительную инфу из наследуемой таблицы.
Так же создаем таблицу films, с двумя триггерами. И наслаждаемся тем, что все реализованные функции уже доступны для фильма как «контента».
Content:
Books:
Films:
Я нахожу многовероятным, что кому-то такой подход покажеться не лучшим или вообще плохим, однако же хочу заверить, что при большом числе операций над «контентом» и разнообразием контентов в 4 вида такой подход оправдывает себя. в частности на создание движка и одного типа контента ушло примерно месяц, на добавление каждого последующего типа уходило 2-4 дня. Могу уверенно сказать, что такой подход себя оправдал.
Ну а если есть необходимость и возможность — используйте СУБД которые подерживают наследование, и всем будет легче :)
Размышления:
Конечно можно сделать совсем просто — в одной таблице объявить ВСЕ поля ВСЕХ контентов и добавить поле отвечающее за его класс, но это, на мой взгляд, не совсем удачная схема. Но при добавлении нового типа контента в систему могут возникнуть разнообразные проблемы. Как же сделать удобнее?
Что же такого хорошего в наследовании таблиц? А вот что: например у нас имеется 2 вида контента (их было 4): видео и книги. При достаточном размышлении, мы можем придти к мысли что такие параметры как заголовок, описание, рейтинг в системе, количество просмотров/скачиваний и прочую информацию мы можем вынести в базовый класс (в PHP), а вот такие параметры как напрмер количество страниц, продолжительность видео — присущи уже потомкам. Да и операции добавления/удаления и даже комментарии — вполне общие для всех типов. Однозначно — без ООП ни ногой.
Ну а что в базе данных? Мы могли бы ввести «базовую» таблицу content, в ней указать id, rating, votes, comments… а в наследуемых таблицах добавить pages/duration и остальные поля, присущие исключительно типам контента.
Так же внешний ключ таблицы с комментариями привязать к id базовой таблицы, так же повесить триггер, который будет изменять поле количества комментариев при добавлении. Без наследования само собой такие прелести недоступны, в плоть до того, что придется даже создавать 2 одинаковый таблицы комментариев. Жуть, джамшутинг и копипастинг.
Прикинув в уме как бы все-таки не писать 2 разных кода для одних операций, мой мозг посетила светлая мысль, которая сразу же вылиась в код.
Разрешение:
Итак, этап первый: создаем базовую таблицу контента content:
- id
- rating
- votes
- comments
Добавляем таблицу комментариев comments:
- id
- content_id
- text
- author_id
Добавляем внешние ключи (comments.content_id => content.id ON DELETE CASCADE), добавляем триггеры (на добавление/удаление комментария +- content.comments)
так же создаем функцию vote(cid, uid, rating) — для изменения рейтинга контента.
Еще можно много чего сделать, но думаю этого достаточно, что бы показать идею.
Кульминация:
Создаем таблицу books. Как многие догадались — это таблица для контента типа «книга», одна будет «наследовать» таблицу content, со всеми ее (контента) вкусностями.
Но если говорить честнее (и что бы было понятнее), мы всего лишь введем такой подход, при котором одной строке в таблице книг будет однозначно ставиться в соответствие одна запись из таблицы «контент». И вот как этого добиться:
Воодим таблицу books:
- id
- cid
- title
- descr
- pages
создаем внешний ключ (books.cid => content.id ON DELETE CASCADE), и создаем триггер, который при добавлении строки в таблицу books, создает и ставит ей в соответствие строку в таблице content:
DROP TRIGGER IF EXISTS `t_books_insert`//
CREATE TRIGGER `t_films_insert` BEFORE INSERT ON `books`
FOR EACH ROW BEGIN INSERT INTO content SET service=1; SET NEW.content_id = LAST_INSERT_ID(); END
//
Ну и для удаления:
DROP TRIGGER IF EXISTS `t_books_delete`//
CREATE TRIGGER `t_books_delete` BEFORE DELETE ON `books`
FOR EACH ROW DELETE FROM content WHERE id=OLD.content_id
//
Как это работает:
Добавляем строку в таблице books, а работаем с таблицей content: в зависимости от типа сервиса мы выполняем JOIN к нужной таблице (в данном случае books):
SELECT * FROM content AS c
JOIN books AS b ON b.cid=c.id WHERE c.id={$cid}
Получаем всю информацию.
Очень важно понимать, что для системы нет книг или фильмов — в ней есть контент, который «тянет» за собой дополнительную инфу из наследуемой таблицы.
Так же создаем таблицу films, с двумя триггерами. И наслаждаемся тем, что все реализованные функции уже доступны для фильма как «контента».
PHP:
Content:
class Content{
public $sTableName = '';
public void getInfo($cid){
return $dbsl->fetchAll("SELECT * FROM content AS c, {$this->sTableName} AS t WHERE t.cid=c.id AND c.id={$cid}");
}
public vote($cid, $uid, $rating){
return $db->Query("SELECT vote({$cid}, {$uid}, {$rating});");
}
}
Books:
class Books{
public $sTableName = 'books';
}
Films:
class Films {
public $sTableName = 'films';
}
Заключение:
Я нахожу многовероятным, что кому-то такой подход покажеться не лучшим или вообще плохим, однако же хочу заверить, что при большом числе операций над «контентом» и разнообразием контентов в 4 вида такой подход оправдывает себя. в частности на создание движка и одного типа контента ушло примерно месяц, на добавление каждого последующего типа уходило 2-4 дня. Могу уверенно сказать, что такой подход себя оправдал.
P.S.:
Ну а если есть необходимость и возможность — используйте СУБД которые подерживают наследование, и всем будет легче :)