#4 Практическое задание №4. Создание подкатегории и логическая связь "один ко многим"

Необходимо реализовать следующее:

  1. Реализовать сущность "Подкатегория", которая помимо технического id содержит:
    • Название подкатегории
    • "Ссылку" (внешний ключ = id категории) на таблицу "Категория" -- так как каждая подкатегория должна относится к какой-то категории (таким образом между Категорией и Подкатегорией установится связь "один ко многим").
  2. Реализовать CRUD операции для сущности "Подкатегория" в админке (аналогично тому, что мы уже делали для пользователей).
  3. Добавить в сущность "Статья" аналогичную связь с Подкатегорией, чтобы каждая статья относилась не только к Категории, но и к какой-то Подкатегории. (аналогично -- появится связь один-ко-многим, т.к. к одной подкатегории может относится множество статей)
  4. При выводе статьи в списке статьей или на отдельной страницу (в пользовательском разделе) добавить помимо ссылки на категорию (уже ведёт на список всех статей с этой категорией), ссылку "подкатегрия" (так чтобы она вела на отдельную страницу со всеми статьями этой подкатегории)

То же самое задание с пояснением на видео: https://youtu.be/pTl5yZTCiRI

Совместимость категории и подкатегорий (валидация на бэкэнде)

  1. Для вывода списка подкатегорий в селектбоксе используйте группировку их по категориям, чтобы пользователю было понятно что к чем относится
  2. При сохранении статьи напишите проверку (в скрипте, ответственно за обработку формы), что выбранная подкатегория относится к выбранной категории, если этого не так, то переведите пользователя назад на форму редактирования (или создания) с отображением тех же данных, которые пользователь отправил на сохранение ранее, но дополнительно выведете сообщение обо ошибке

    "Ошибка: Выбранная категория не соответствует подкатегории!"

    .

  3. Т.е. теперь код, отвечающий за отображение формы, должно быть способнен отображать ошибки (если их массив не пуст).
    Для универсальности лучше использовать именно массив, ведь в теории на бэкэнде в дальнейшем могут быть добавлены новые правили проверки полей.
sid's picture

Прошу проверить, правильно ли я движусь,
пока только сущность подкатегория и (CRUD) над ней вам сюда.

vedro-compota's picture

вроде да, продолжайте разработку. При копировании не забывайте проверять имена таблиц (SQL код) и имена вообще.

_____________
матфак вгу и остальная классика =)

sid's picture

Ошибка при добавление внешнего ключа
в таблицу articles для связи с таблицей subcategory

Проделанные действия:

ALTER TABLE articles 
ADD COLUMN subCategory_id SMALLINT (5) UNSIGNED NOT NULL;

Далее устанавливаем внешний ключ

ALTER TABLE articles
ADD CONSTRAINT FK_articles_subcategory
FOREIGN KEY (subCategory_id) REFERENCES subcategory (id);

Или так:

ALTER TABLE articles
ADD  FOREIGN KEY (subCategory_id) REFERENCES subcategory (id);

Выдает ошибку:

ERROR 1452 (23000): 
Cannot add or update a child row: a foreign key constraint fails 
(`cms`.`#sql-3f5_7a`, CONSTRAINT `FK_articles_subcategory`
FOREIGN KEY (`subCategory_id`) REFERENCES `subcategory` (`id`))
vedro-compota's picture

а что такое:

#sql-3f5_7a

?

_____________
матфак вгу и остальная классика =)

vedro-compota's picture

вы добавляете колонку. а потом уже делаете её внешним ключом -- а нельзя ли это эсделать в одно действие?
Скорее всего тут причина вот в чем:
дело в том, что когда вы обновляете схему таблицы и при этом в ней есть какие данные -- происходит проверка допустимости применения этой новой схемы к уже имеющимся данным.
По сути у вас имеются два противоречения, но первое ваш режим работы СУБД пропускает, а вот второе уже нет, вот они:

  1. Сначала вы задаёте NOT NULL колонку (т.е. обязаетельно непустую):
    ALTER TABLE articles 
    ADD COLUMN subCategory_id SMALLINT (5) UNSIGNED NOT NULL;
    

    -- этот запрос, судя по вашему комментарию проходит без проблем, но он уже не совсем корректен, ведь для имеющихся данных вы не назначили значение по умолчанию, хотя требуете NOT NULL. Но что по факту окажется в этом столбце для уже имеющихся кортежей? (проверьте -- не null ли? если так -- то это значит что режим sql на вашей машине просто пропускает такое несоответствие -- дело в том, что сервер можно запускать в разных режимах. По-умолчанию стоит "средняя" строгость)

  2. второе: далее вы заставляете mysql считать этот столбец внешним ключом:
    ALTER TABLE articles
    ADD  FOREIGN KEY (subCategory_id) 
    REFERENCES subcategory (id);

    Ведь получается, что вы пытаетесь установить связь для столбца, где по-идее (это надо проверить) находится null -- а такого PRIMARY KEY ключа в таблице подкатегорий точно нет.
    -- именно этому в ошибке речь идёт не про смену схемы -- а про update строк:

    Cannot add or update a child row....

Вариант решения:

  1. сразу после создания таблицы подкатегорий запросом вставить туда подкатегорию "без подкатегории" с id = 1 (например)
  2. далее: назначаем именно этот id -- значением по умолчанию для внешнего ключа (при этом рекомендую написать запрос для таблицы "Статьи" который сразу и добавляет столбец со значением по умолчанию и делает его внешним ключом)

_____________
матфак вгу и остальная классика =)

sid's picture

Измененный sql запрос на добавление внешнего ключа subCategoryId
в таблицу articles для связи с таблицей subcategory:

ALTER TABLE articles
ADD COLUMN subCategoryId SMALLINT (5) UNSIGNED NOT NULL
DEFAULT 1 COMMENT 'Внешний ключ для таблицы subcategory',
ADD FOREIGN KEY (subCategoryId) REFERENCES subcategory (id);
vedro-compota's picture

  • сразу после создания таблицы подкатегорий перед командой модификации таблицы статей, надо добавить INSERT запрос на добавление этой самой категории по-умолчанию -- явно указав её PRIMARY KEY -- т.е. id (например = 1)
  • лучше все же не DEFAULT 13, а DEFAULT 1 -- у вас 13, так как вы видимо до этого уже добавляли что-то в таблицу, просто создайте таблицу подкатегорий заново

_____________
матфак вгу и остальная классика =)

sid's picture

Значение по умолчанию для id таблицы subcategory

INSERT INTO subcategory (id, categories_id, name, description)
VALUES (1, 1, 'Отладка', 'id по умолчанию');
vedro-compota's picture

да, но в вашем скрипте разворота это должно идти сразу после создания таблицы подкатегории но до модификации таблицы статей.

'Отладка', 'id по умолчанию'

-- это не верно, у вас должно быть написано что-то типа

  • "Подкатегория не установлена"
  • описание: "Подкатегория, для старых статей -- её следует переназначить"

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

_____________
матфак вгу и остальная классика =)

sid's picture

Изменено

INSERT INTO subcategory (id, categories_id, name, description)
VALUES (1, 1, 'Подкатегория не установлена', 
'Подкатегория, для старых статей -- её следует переназначить');
sid's picture

scql для создания таблицы subcategory

CREATE TABLE subcategory (
id SMALLINT(5) UNSIGNED NOT NULL AUTO_INCREMENT 
COMMENT 'ID подкатегории',
categories_id SMALLINT(5) NOT NULL COMMENT 'Внешний ключ для 
связи с таблицей categories',
name VARCHAR(255) NOT NULL COMMENT 'Имя подкатегории',
description text NOT NULL COMMENT 'Содержание категории', 
PRIMARY KEY (id), 
FOREIGN KEY (categories_id) REFERENCES categories(id`) 
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
vedro-compota's picture

не хватает пробелов кое-где
обновлено: теперь ок. исправили.

_____________
матфак вгу и остальная классика =)

sid's picture

Добавлена к статье связь с подкатегорией здесь

JinJim's picture

https://github.com/kdn2517/my-first-cms-...
Все сделал, все работает.
Заморочился только с проверкой - если подкатегория не соответствует категории, то по идее надо бы вернуть на ту же страницу и с введенными/исправленными уже данными, чтобы заново все не вводить. Я это сделал, только слишком громоздко получилось (проверка - https://github.com/kdn2517/my-first-cms-..., для того, чтобы вывести уже введенные данные https://github.com/kdn2517/my-first-cms-... ).
А так все вроде просто, посмотрите.

Pavel1989's picture

Ссылка на коммит:
Коммит

По аналогии с категориями созданы файлы:

subcategory.php, editSubcategory.php, listSubcategories.php
добавлена таблица subcategories.

dimmkan's picture

Ссылка на коммит

Также сделал отбор при выборе категории, чтобы выводились только те подкатегории, которые к ней относятся (не нужно делать дополнительной проверки)