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

Общее описание

За всю историю человечества были изобретены тысячи рецептов, и этот процесс продолжается и сейчас. Меняется мода, меняются представления о здоровье, но кулинария – это чудесное устройство, которое может перенести вас в другую страну или даже эпоху.

Поэтому иметь под рукой свой собственный проверенный справочник блюд – это круто!

Бизнес модель

Добавление и хранение рецептов, включая состав, изображение и технологии приготовления.

Поиск рецептов по названию, по имеющимся ингредиентам, по видам ингредиентов, по категориям рецептов.

Модель данных

Что будем хранить:
Рецепт

  • Название
  • Описание
  • Картинка
  • Описание технологии
  • Общее время приготовления
  • Автор

Состав рецепта

  • Ингредиент
  • Рецепт
  • Количество
  • Мера

Мера

  • Название

Категории рецепта

  • Рецепт
  • Категория

Категория

  • Название

Ингредиент

  • Название
  • Вид ингредиента
  • Картинка
  • Описание

Вид ингредиента

  • Название

Автор

  • Имя
  • Фото
  • Описание
  • Контактные данные

Представление данных

На главной форме будет размещен список рецептов, система поиска (фильтрации). Просмотр/редактирование рецепта через форму редактирования.

Справочники отображаются в модальных окнах. Открытие окна осуществляется кнопкой рядом с полем выбора данных из справочника.

Просмотр/редактирования справочника осуществляется через форму редактирования.

Минимальный размер главной формы – 1280х720 (HD)

Реализация проекта

База данных

Схема данных

Главная форма

На главной форме расположены: таблица для отображения рецептов (1), панель инструментов (2), панель фильтрации (3). На панели инструментов находятся три кнопки: для добавления (3), редактирования (4) и удаления (5) рецептов. На панели фильтрации расположены поле для поиска по названию (7), многостраничник с двумя вкладками (8) и невидимая кнопка получения данных для отображения (9). На каждой вкладке многостраничника находится таблица (10) для отображения опций фильтрации (категория рецепта и состав рецепта).

Фильтрация данных осуществляется по названию рецепта (поиск по вхождению), а так же с использованием вспомогательных таблиц: категории и ингредиенты. Это позволит находить рецепты, отвечающие заданным критериям. Например: показать все вегетарианские десерты (2 категории), в состав которых входит банан, яблоко и персик (3 ингредиента).

Для этого поиск осуществляется через SQL-запрос, а сам запрос строится программно в обработчике нажатия кнопки btnSearch, с учётом того, какого рода фильтрация требуется – по названию, по категориям, по ингредиентам или всё сразу.

procedure frmMain_btnSearch_OnClick (Sender: TObject; var Cancel: boolean);
var
  tmpSQL : string;
begin
  tmpSQL := '';
  // если для поиска не используются ингредиенты и категории, то создаём простой запрос с поиском по имени
  if (IngredientIDs.Count = 0) and (CategoryIDs.Count = 0) then
  begin
    tmpSQL := tmpSQL + 'SELECT recipe.name, recipe.id FROM recipe ';
    tmpSQL := tmpSQL + 'WHERE (1=1) ';
    if frmMain.edtName.Text <> '' then
      tmpSQL := tmpSQL + 'and (name LIKE ''%'+frmMain.edtName.Text+'%'')';
  end
  else // запрос сложный
  begin
    // данные по ингредиентам
    if (IngredientIDs.Count > 0) then
    begin
      tmpSQL := tmpSQL +' SELECT name, id FROM ( ';
      tmpSQL := tmpSQL + 'SELECT recipe.name, recipe.id, count(recipe.id) as rcount FROM recipe ';
      tmpSQL := tmpSQL + 'LEFT JOIN recipeIngredient ON recipeIngredient.id_recipe = recipe.id ';
      tmpSQL := tmpSQL + 'WHERE (1=1) ';
      if frmMain.edtName.Text <> '' then
        tmpSQL := tmpSQL + 'and (name LIKE ''%'+frmMain.edtName.Text+'%'')';
      tmpSQL := tmpSQL + 'and ( recipeIngredient.id_ingredient in ('+IngredientIDs.CommaText+') ) ';
      tmpSQL := tmpSQL + 'GROUP BY recipe.name, recipe.id ';
      tmpSQL := tmpSQL + 'HAVING rcount = '+IntToStr(IngredientIDs.Count);
      tmpSQL := tmpSQL +' ) ';
    end;
    // если фильтруем по ингредиентам и категориям, то потребуется пересечение данных
    if (IngredientIDs.Count > 0) and (CategoryIDs.Count > 0) then
      tmpSQL := tmpSQL + ' INTERSECT ';
    // данные по категориям
    if CategoryIDs.Count > 0 then
    begin
      tmpSQL := tmpSQL +' SELECT name, id FROM ( ';
      tmpSQL := tmpSQL + 'SELECT recipe.name, recipe.id, count(recipe.id) as rcount FROM recipe ';
      tmpSQL := tmpSQL + 'LEFT JOIN recipeCategory ON recipeCategory.id_recipe = recipe.id ';
      tmpSQL := tmpSQL + 'WHERE (1=1) ';
      if frmMain.edtName.Text <> '' then
        tmpSQL := tmpSQL + 'and (name LIKE ''%'+frmMain.edtName.Text+'%'')';
      tmpSQL := tmpSQL + 'and ( recipeCategory.id_category in ('+CategoryIDs.CommaText+') )';
      tmpSQL := tmpSQL + 'GROUP BY recipe.name, recipe.id ';
      tmpSQL := tmpSQL + 'HAVING rcount = '+IntToStr(CategoryIDs.Count);
      tmpSQL := tmpSQL +' ) ';
    end;
  end;
  frmMain.btnSearch.dbSQL := tmpSQL;
end;

Форма редактирования рецепта

На форме редактирования, кроме обычных элементов ввода данных, расположены специальные дополнительные элементы.

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

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

В данном случае на форме редактирования проекта размещаются две формы: ингредиенты рецепта и категории рецепта. Формы размещается на панелях программно в момент запуска приложения.

Ингредиенты рецепта

Эта форма, как и все формы отображения табличных данных, содержит таблицу (1) и панель инструментов с кнопками управления (2). Так как в данной таблице производится фильтрация данных по конкретному рецепту, то для вывода данных используется кнопка с функцией «Поиск» (4) и невидимое поле для хранения id_recipe (3), которое задействовано в фильтре.

Использование поля ввода вместо выпадающего списка делает программу немного быстрее, так как каждый выпадающий список обновляется как при запуске программы, так и при редактировании соответствующей ему таблицы. Поэтому там, где данный элемент невидимый, я рекомендую использовать TdbEdit вместо TdbCombobox. При этом название нужного в данном случае поля id_recipe необходимо будет набрать вручную, так как его не будет в списке полей.

А нужное значение в этот компонент будет помещаться при открытии формы редактирования рецепта, в обработчике onShow.

procedure frmRecipeEdit_OnShow (Sender: TObject; Action: string);
begin
  // настроить отображение ингредиентов
  frmRecipeIngredient.edtIDRecipe.Text := IntToStr( frmRecipeEdit.btnSave.dbGeneralTableId );
  frmRecipeIngredient.btnSearch.Click;
  // настроить отображение категорий
  frmRecipeCategory.edtIDRecipe.Text := IntToStr( frmRecipeEdit.btnSave.dbGeneralTableId );
  frmRecipeCategory.btnSearch.Click;
end;

Категории рецепта

Данная форма отличается от всех остальных форм отображения табличных данных тем, что служит только для отображения данных, а её редактирование осуществляется программно. Пользователь отмечает чекбоксы в справочнике категорий, и выбранные данные переносятся в категории рецепта

Категории

У таблицы на данной форме добавлены чекбоксы, которые используются для массового редактирования таблицы RecipeCategory.

При отображении формы чекбоксы устанавливаются согласно полученным сведениям из таблицы на форме редактирования рецепта, а при нажатии кнопки «Выбрать» происходит замена данных в БД.

procedure frmCategory_OnShow (Sender: TObject; Action: string);
// отображение формы
var
  i,j: integer;
begin
  // отображение данных
  for i := 0 to frmCategory.tgrMain.RowCount - 1 do
  begin
    frmCategory.tgrMain.cell[0,i].asBoolean := False;
    for j := 0 to frmRecipeCategory.tgrMain.RowCount - 1 do
    begin
      if frmCategory.tgrMain.cells[1,i] = frmRecipeCategory.tgrMain.cells[1,j] then
      begin
        frmCategory.tgrMain.cell[0,i].asBoolean := True;
        Break;
      end;
    end;
  end;
end;
procedure frmCategory_btnSelect_OnClick (Sender: TObject; var Cancel: boolean);
var
  tmpSQL: string;
  i: integer;
begin
  // удалить старые
  tmpSQL := 'DELETE FROM RecipeCategory WHERE id_recipe = '+frmRecipeCategory.edtIDRecipe.Text;
  SQLExecute(tmpSQL);
  // добавить новые
  for i := 0 to frmCategory.tgrMain.RowCount - 1 do
  begin
    if frmCategory.tgrMain.cell[0,i].asBoolean then
    begin
      tmpSQL := 'INSERT INTO RecipeCategory (id_recipe,id_category) VALUES ('+frmRecipeCategory.edtIDRecipe.Text+','+IntToStr(frmCategory.tgrMain.dbIndexToID(i))+') ';
      SQLExecute(tmpSQL);
    end;
  end;
  frmRecipeCategory.btnSearch.Click;
end;

Результат

Для достижения лучшего визуального эффекта в проекте был активирован стиль «Luna», увеличен размер шрифта, а у таблиц включена опция отображения заголовка HeaderStyle = hsOffice2007

Доступна ссылка для скачивания архива с приложением.

Для читателей Библиотеки My Visual Database доступен проект со всеми скриптами.

2 комментария к «Кулинарная книга»

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *