|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#16
|
||||
|
||||
Книг не знаю, к сожалению. Могу только посоветовать поискать темы с аналогичными вопросами по проектированию на этом и других форумах, где не посылают в Google, а дают конкретные ответы. Благодаря нашей теме теперь хоть понятно, как это выглядит.
|
#17
|
||||
|
||||
Я продолжил поиски по теме:
Процедура вызова окна, где заполняются данные о документе. Код:
procedure TfMain.barbutNewTicketClick(Sender: TObject); begin Tickets.Insert; fNewTicket.ShowModal; end; Три процедуры для заполнение подчинённых данных. 1. Insert в Detail таблицу -> Items.Insert; 2. Post в Detail таблицу (есть обработчик BeforePost, в котором проверяется адекватность введённых данных) 3. Cancel изменений в Detail таблице ->Items.Cancel; Далее код для сохранения документа: Код:
procedure TfNewTicket.butSaveClick(Sender: TObject); begin // ... некое взаимодействие с пользователем fMain.Tickets.Post; // вносим изменения в билет fMain.Tickets.UpdateBatch; // отправляем изменения в БД (тем самым получаем ID записи, на которой сейчас стоит курсор (последняя добавленная) fMain.Items.First; // обработка всех добавленных записей в Detail таблице while not fMain.Items.Eof do begin fMain.Items.Edit; fMain.Items.FieldByName('IDTicket').AsInteger := fMain.Tickets.FieldByName('ID').AsInteger; fMain.Items.Post; fMain.Items.Next; // Next вызывает метод Post? я не нашёл в реализации явного вызова, но судя по тому, что строка сохраняется делаю вывод что вызывается end; fMain.Items.UpdateBatch; // фиксируем изменения в Items. end; В итоге всё супер работает. Только пришлось отказаться от: Код:
Items.MasterSource := dsTickets; Items.MasterFields := ID; Items.IndexFieldNames := IDTicket; И пришлось добавить: Код:
procedure TfMain.ItemsAfterOpen(DataSet: TDataSet); begin if not Items.Filtered then begin Items.FilterGroup := fgAffectedRecords; Items.Filtered := true; end; end; Что скажете? Последний раз редактировалось Uniq!, 08.10.2013 в 19:03. |
#18
|
||||
|
||||
Уже больше похоже на нормальное решение, но это не совсем то, что я говорил. Логика по-прежнему сидит в обработчиках кнопок и по-прежнему есть управление компонентами другой (главной) формы с обращением через глобальную переменную.
Если это решение корректно работает, этап освоения DB-компонентов можно считать состоявшимся. Вынос логики в отдельные процедуры и отказ от глобальных переменных не является самоцелью, вначале нужно понять их смысл. Мои прошлые советы, где говорится про интерактивные и неинтерактивные процессы, по-прежнему в силе. Пусть пройдет месяц, ощущение достижения уляжется, и, перечитав их, вполне вероятно в них увидится новый смысл. Этот смысл по-любому станет понятен, если в проект придется вносить согласованные изменения или появится второй программист: выгода независимых друг от друга частей кода будет видна на практике. По вопросу из комментария: TDataSet.Next вроде бы вызывает CheckBrowseMode, которая и делает Post или Cancel в зависимости от наличия или отсутствия изменений. Не стоит путать форумы с богадельнями. © Bargest |
Этот пользователь сказал Спасибо Freeman за это полезное сообщение: | ||
Mrak (11.10.2013)
|
#19
|
||||
|
||||
Т.е. вот всё, что начинается с
Код:
fMain. Тогда возникает вопрос: как правильно организовывать доступ к ADOConnection ADOTable и DataSource со всех форм? (с учётом того, что DataModule тоже будет являться глобальной и его использование ничего не изменит) Само решение не является идеальным. Работает как часы, даже с учётом того, что сервер отваливается время от времени, из-за "временных работ". Сейчас перешёл на стадию синхронизации оффлайн и онлайн режимов, но это уже другая история. |
#20
|
||||
|
||||
Цитата:
Если тебе удастся описать архитектуру на неком общем уровне, попробую дать совет. Не стоит путать форумы с богадельнями. © Bargest |
#21
|
||||
|
||||
Как это сейчас: индекс f относится к формам. ВашКеп
fMain - общая таблица всех документов. В таблице только самая важная информация для быстрого взгляда и оценки этих документов. на ней же Connection(ADOConnection) Clients,Items,Tickets(ADOTable) fNewTicket - Вызывается по нажатию кнопки с fMain. Тут три кнопки для заполнения данных (см. выше) и конечная кнопка bTicketSave по которой вызывается fMain.Tickets.UpdateBatch; fSplash - обсудили и сделали уже. Splash-скрин для отображения загрузки данных из БД. fSetClient - форма, которая вызывается для присоединения Человека к документу (кнопка на форме fNewTicket) На форме таблица Паспортных данных и быстрый поиск (фильтрация средствами cxGrid) fAddClient - форма вызывается для добавления в справочник нового клиента (кнопка вызова на форме fSetClient) fSettings - форма с настройками соединения с БД и базовые вещи, относящиеся к нумерации документов и всякая сторонняя информация. Последний раз редактировалось Uniq!, 09.10.2013 в 23:42. |
#22
|
||||
|
||||
Фух, вроде форум наконец-то заработал, а я понял, что писать, чтобы не получилась отписка.
Цитата:
Цитата:
Все предыдущие советы я писал, исходя из классического интерфейса, когда основу главной формы составляет грид со списком объектов, над которым красуется классическая панель инструментов: вставка, редактирование, удаление, проводка, списание, печать и т. п. Действия, требующие отображения содержимого и/или ввода данных, реализуются как "действие диалогом", -- например, "редактирование диалогом". Диалог во всех случаях -- самый обычный, с кнопками "OK" и "Отмена". Нашкодил пользователь в диалоге, нажал "OK", -- действие диалога зафиксировалось (вызвался метод Post), а если нашкодил и нажал "Отмену" -- действие отменилось (вызвался метод Cancel). "Действие диалогом" хорошо абстрагируется и обычно реализуется в диалоге-предке, от которого наследуются диалоги конкретных действий. Само собой, панелей инструментов в таком диалоге нет, а если есть -- то только для деталей, повторяющих шаблон "форма с данными -- действие диалогом". С этой точки зрения сохранение результатов редактирования кнопкой "Сохранить" в диалоге ввода -- нарушение шаблона "действие диалогом", то есть нарушение декомпозиции. Форма-владелец данных не должна вызывать "волшебный диалог", делающий с ее данными не пойми что, а наоборот, на правах хозяйки она должна самостоятельно редактировать данные, по мере надобности призывая себе помощников-диалогов. Я не совсем в курсах насчет "паттернов" (шаблонов проектирования) и их терминологии, поэтому может оказаться, что описанное тут "действие диалогом" по-умному называется как-то по-другому. Если узнаешь, отпишись. Не стоит путать форумы с богадельнями. © Bargest |
#23
|
||||
|
||||
Очень хочется разобраться до конца.
На главной форме: Код:
procedure TfMain.barbutNewTicketClick(Sender: TObject); begin Tickets.Insert; end; Код:
procedure TfMain.TicketsAfterInsert(DataSet: TDataSet); begin fNewTicket.ShowModal; end; ADOTable который содержит в себе информацию из Detail таблицы я перенёс на форму диалога. (по-моему это неправильно) Но если оставить её на FormMain то получится, что мне нужно будет вызывать Insert Post Cancel для этой таблицы из диалогового окна. (Как правильно?) Далее как вы и посоветовали: Cancel & Ok; OK: Код:
procedure TfNewTicket.butSaveClick(Sender: TObject); begin fMain.Tickets.Post; end; Далее AfterPost Код:
procedure TfMain.TicketsAfterPost(DataSet: TDataSet); begin Tickets.UpdateBatch; Items.First; while not Items.Eof do begin Items.Edit; Items.FieldByName('IDTicket').AsInteger := Tickets.FieldByName('ID') .AsInteger; Items.Next; end; Items.UpdateBatch; end; Я слабо, очень слабо представляю как избавиться от глобальной переменной fMain в коде из Формы-Диалога. Про индекс F, я помню Просто важнее сейчас исправить основные ошибки. |
#24
|
||||
|
||||
Очень интересная и актуальная тема
Подскажите, не понимаю, здесь Код:
procedure TfNewTicket.butSaveClick(Sender: TObject); begin // ... некое взаимодействие с пользователем fMain.Tickets.Post; // вносим изменения в билет fMain.Tickets.UpdateBatch; // отправляем изменения в БД (тем самым получаем ID записи, на которой сейчас стоит курсор (последняя добавленная) fMain.Items.First; // обработка всех добавленных записей в Detail таблице while not fMain.Items.Eof do begin fMain.Items.Edit; fMain.Items.FieldByName('IDTicket').AsInteger := fMain.Tickets.FieldByName('ID').AsInteger; fMain.Items.Post; fMain.Items.Next; // Next вызывает метод Post? я не нашёл в реализации явного вызова, но судя по тому, что строка сохраняется делаю вывод что вызывается end; fMain.Items.UpdateBatch; // фиксируем изменения в Items. end; И еще небольшой вопросик для Freeman, использую DataModule и обращаюсь к Query на нем как через глобальную переменную Код:
DataModule.ADOQuery1.SQL.Text:=... Спасибо Я за здоровый экстрим! Спасибо за "спасибо") |
#25
|
||||
|
||||
Идея такая:
1) Сначала отправить на сервер запись из Master таблицы. 2) Она получит свой ID. (Его можно было бы и руками самому проставить, но как оказалось это "костыль") 3) Далее считываю этот ID (да, он последний т.к. используется Insert и курсор прыгает именно на последнюю запись) и присваиваю его всем подчинённым (Detail) записям. И только после этого отправляю их на сервер. Последний раз редактировалось Uniq!, 14.10.2013 в 17:22. |
#26
|
||||
|
||||
Цитата:
у меня же сделано все на основе LAST_INSERT_ID, когда заморочился с номерами заказов, спросил на форуме MySQL, посоветовали так... Сейчас понимаю, что не очень правильно. Была идея засунуть два запроса в одну транзакцию, пока не увидел как у вас Я за здоровый экстрим! Спасибо за "спасибо") |
#27
|
||||
|
||||
Моё тоже не идеально. Но LAST_INSERT_ID - слишком стрёмный вариант. Я от него отказался сразу же.
для Freeman прилепил два файла с GUI. fMain(Главная форма) и fNewTicket(диалог) С диалогом ещё дорабатываю. Жду, когда на фирме поставят devExpress c dxWizard. Можно будет оформление документа организовать подобно установке любой программы (поэтапное заполнение) |
#28
|
||||
|
||||
Цитата:
Весь смысл деления программы на интерактивные и пакетные потоки -- в том, чтобы не перемешивать "видимый" и "невидимый" код. Событие AfterInsert -- неинтерактивное, помещать в него вызов диалога -- нарушение декомпозиции. "Действие диалогом" должно выглядеть примерно так: Код:
type TDataSetDialog = class(TForm) ... EditingSource: TDataSource; // назначен всем DB-компонентам // и как MasterDataSet деталям ... public function Execute(DataSet: TDataSet): Boolean; end; function TDataSetDialg.Execute(DataSet: TDataSet): Boolean; begin EditingSource.DataSet := DataSet; Result := ShowModal = mrOK; if Result then DataSet.Post // тут срабатывает AfterPost, делающий UpdateBatch деталей else DataSet.Cancel; end; // вызов begin Tickets.Insert; // или Tickets.Edit with TTicketsDialog.Create do try Execute(Tickets); finally Free; end; end; Если редактирование деталей или доступ к справочникам требует какой-то дополнительной инициализации, вызов Post или Edit можно внести прямо в Execute, передавая ей вторым параметром команду: Код:
type TDataSetCommand = (cmInsert, cmEdit); TDataSetDialog = class(TForm) public function Execute(DataSet: TDataSet; Command: TDataSetCommand): Boolean; end; function TDataSetDialg.Execute(DataSet: TDataSet; Command: TDataSetCommand): Boolean; begin EditingSource.DataSet := DataSet; // куча дополнительных инициализаций, в том числе через виртуальный метод в потомках if Command = cmInsert then DataSet.Insert else DataSet.Edit; ... end; Цитата:
Цитата:
Не стоит путать форумы с богадельнями. © Bargest |
#29
|
||||
|
||||
До меня вчера это всё дошло.) Но я боялся, что это слишком сложный вариант. А меня в институте приучили, что чем проще, тем лучше. И ещё должно быть правильно.
1) Значит я создаю форму - диалог. (исключаю её из автосоздаваемых) ВОТ это лишнее телодвижение почему-то не надо удалять. 2) Дальше на главной форме создаю этот диалог и вызываю его метод Execute передавая туда Tickets, оууу ес, который лежит в данный момент на главной форме (скажем нет глобальным переменным!) 3) Profit. Осталось качественно задизайнить мой диалог. Погуглил. Нужно было определиться в понятиях. Вот диалог, который я представлял себе, а вот пример диалога, который должен был представлять. Я вот только с этим никогда не сталкивался: присвоение с проверкой на равенство какому-то значению. Код:
Result := ShowModal = mrOK; Ответ: в свойствах кнопки есть property: ModalResult. Freeman, это реально круто. Остаётся только один вопрос. Где вы вычитали, что нужно делать именно так? Хотя подсознательно я понимаю, что это основные идеи любого ОО-языка. Это оказывается шаблонизированная структура Последний раз редактировалось Uniq!, 16.10.2013 в 11:55. |
Этот пользователь сказал Спасибо Uniq! за это полезное сообщение: | ||
Mrak (17.10.2013)
|
#30
|
||||
|
||||
Цитата:
Я за здоровый экстрим! Спасибо за "спасибо") |