|
Software Development Kit to the Delphi-Win32 and Free Pascal compilers |
| Home > Wiki > PhonebookProject/Article2 |
PhonebookProject/Article2english (en) | português (pt)
On this article will be made the implementation of project's logic.
[edit] Business ObjectsThe application business classes will be declared together, separated by group. Each subfolder within the folder will only have a Core unit with all business classes of that group. [edit] CustomBOCreate a unit (without form) {phonebook}\Source\Core\Custom\CustomBO.pas and place the following contents: unit CustomBO; interface uses PressSubject, PressAttributes; type TCustomObject = class(TPressObject) end; TCustomQuery = class(TPressQuery) end; TCustomParts = class(TPressParts) end; TCustomReferences = class(TPressReferences) end; implementation initialization TCustomObject.RegisterClass; TCustomQuery.RegisterClass; TCustomParts.RegisterAttribute; TCustomReferences.RegisterAttribute; finalization TCustomObject.UnregisterClass; TCustomQuery.UnregisterClass; TCustomParts.UnregisterAttribute; TCustomReferences.UnregisterAttribute; end. The aim of these classes is to take place between PressObjects and application business classes, in order to add or change features in a general way, without need to change every class. [edit] ContactBOCreate another unit (also without form) {phonebook}\Source\Core\Contato\ContactBO.pas and place the following contents:
unit ContactBO;
interface
uses
PressSubject, PressAttributes, CustomBO;
type
{$M+} // necessary in the FPC
TContatoFoneParts = class;
{$M-} // necessary in the FPC
TContact = class(TCustomObject)
_Name: TPressAnsiString;
_Address: TPressAnsiString;
_Phones: TContatoFoneParts;
_City: TPressReference;
protected
class function InternalMetadataStr: string; override;
end;
TContactQuery = class(TCustomQuery)
_Name: TPressAnsiString;
_City: TPressReference;
protected
class function InternalMetadataStr: string; override;
end;
TPhoneType = (tfFixed, tfMobile, tfFax);
TContactPhone = class(TCustomObject)
_PhoneType: TPressEnum;
_Number: TPressPlainString;
protected
class function InternalMetadataStr: string; override;
end;
TContactPhoneParts = class(TCustomParts)
public
class function ValidObjectClass: TPressObjectClass; override;
end;
TCity = class(TCustomObject)
_Name: TPressAnsiString;
_State: TPressAnsiString;
protected
class function InternalMetadataStr: string; override;
end;
TCityQuery = class(TCustomQuery)
_Name: TPressAnsiString;
protected
class function InternalMetadataStr: string; override;
end;
implementation
initialization
PressModel.RegisterEnumMetadata(TypeInfo(TPhoneType), 'TPhoneType',
['Phone fixed', 'Mobile', 'Fax']);
TContact.RegisterClass;
TContactQuery.RegisterClass;
TContactFone.RegisterClass;
TContactFoneParts.RegisterAttribute;
TCity.RegisterClass;
TCityQuery.RegisterClass;
finalization
TContact.UnregisterClass;
TContactQuery.UnregisterClass;
TContactPhone.UnregisterClass;
TContactPhoneParts.UnregisterAttribute;
TCity.UnregisterClass;
TCityQuery.UnregisterClass;
end.
To implement business classes metadata, position the cursor within class declaration text and press Shift + Ctrl + C. On method created by the IDE, type the following block, to TContact class:
class function TContact.InternalMetadataStr: string;
begin
Result := 'TContact IsPersistent (' +
'Name: AnsiString(60);' +
'Address: AnsiString(80);' +
'Phones: TContactPhoneParts;' +
'City: Reference(TCity);' +
')';
end;
This is the most important part of the class declaration. The PressObjects will interpret this string and create attributes accordingly. Class members starting with "_" will address these attributes when a object is instantiated. Now just do the same for the other classes:
begin
Result := 'TContactQuery(TContact) (' +
'Name: AnsiString(60);' +
'City: Reference(TCity);' +
')';
end;
begin
Result := 'TContactPhone IsPersistent (' +
'PhoneType: Enum(TPhoneType);' +
'Number: PlainString(15);' +
')';
end;
class function TContactPhoneParts.ValidObjectClass: TPressObjectClass; begin Result := TContactPhone; end;
begin
Result := 'TCity IsPersistent (' +
'Name: AnsiString(60);' +
'State: AnsiString(2);' +
')';
end;
begin
Result := 'TCityQuery(TCity) (' +
'Name: AnsiString(60);' +
')';
end;
[edit] MVPThe MVP classes also will be grouped, each subfolder in the folder will only have a Core unit with all its MVP classes. [edit] MainMVPCreate a unit (without form) {phonebook}\Source\Core\Main\MainMVP.pas and place the following contents:
unit MainMVP;
interface
uses
PressMVPPresenter;
type
TMainFormPresenter = class(TPressMVPMainFormPresenter)
protected
procedure InitPresenter; override;
end;
implementation
end.
Using the same trick the unit ContactBO, position the text cursor in front of the TMainFormPresenter class declaration, and press Shift + Ctrl + C. Within the statement InitPresenter, implement:
procedure TMainFormPresenter.InitPresenter;
begin
inherited;
BindPresenter(TContactEditPresenter, 'NewContactButton');
BindPresenter(TContactQueryPresenter, 'SearchContactButton');
BindPresenter(TCityQueryPresenter, 'SearchCityButton');
BindCommand(TPressMVPCloseApplicationCommand, 'CloseButton');
PressUserData.Logon('', '');
end;
BindPresenter is a method that makes the connection between a presenter class and a component, and BindCommand method and does the same with a command. When the event OnClick of these components are triggered, The Presenter runs and shows a form, or a command is executed. The Logon method creates a user with privilege to use all the commands, and will be replaced once the access control is implemented. These statements require some units: implementation uses PressUser, PressMVPCommand, ContactMVP; This Presenter should run on startup of the application, it starts some internal services of PressObjects. Open the main project - Project | View Source (Delphi and Lazarus) - and include the presenter call: begin Application.Initialize; Application.CreateForm(TMainForm, MainForm); TMainFormPresenter.Run; end. The statement TMainFormPresenter.Run is instead of the traditional Application.Run. Save all files in the project. [edit] CustomMVPCreate a unit (without form) {phonebook}\Source\Core\Custom\CustomMVP.pas and place the following contents:
unit CustomMVP;
interface
uses
PressMVPPresenter;
type
TCustomEditPresenter = class(TPressMVPFormPresenter)
protected
procedure InitPresenter; override;
end;
TCustomQueryPresenter = class(TPressMVPQueryPresenter)
protected
procedure InitPresenter; override;
end;
implementation
initialization
TCustomEditPresenter.RegisterBO(TCustomObject);
TCustomQueryPresenter.RegisterBO(TCustomQuery);
end.
Using the same trick from previous units, position the text cursor in front of the TCustomEditPresenter class declaration and press Shift + Ctrl + C. Within the statement InitPresenter, implement: procedure TCustomEditPresenter.InitPresenter; begin inherited; BindCommand(TPressMVPSaveObjectCommand, 'SaveButton'); BindCommand(TPressMVPCancelConfirmObjectCommand, 'CancelButton'); end; These instructions bind a command to a form's component. Pressing that button, will executed the command, save the object in the database and close the form. Or reverse the changes made to the object in memory, and close the form. Next class, TCustomQueryPresenter. Position the cursor, press Shift + Ctrl + C and implement:
procedure TCustomQueryPresenter.InitPresenter;
begin
inherited;
BindCommand(TPressMVPExecuteQueryCommand, 'SearchButton');
CreateQueryItemsPresenter('QueryStringGrid');
end;
The first statement connects a default command of Press to search button. The second is a special of Presenters of searchs, and indicates to the MVP framework which is the component that will present the outcome of the query. These implementations depends of statements made in other units. The uses clause implementation should be this: uses PressMVPCommand, CustomBO; [edit] ContactMVPCreate a unit (without form) {phonebook}\Source\Core\Contato\ContactMVP.pas and place the following contents:
unit ContactMVP;
interface
uses
CustomMVP;
type
TContactEditPresenter = class(TCustomEditPresenter)
protected
procedure InitPresenter; override;
end;
TContactQueryPresenter = class(TCustomQueryPresenter)
protected
procedure InitPresenter; override;
function InternalQueryItemsDisplayNames: string; override;
end;
TContactPhoneEditPresenter = class(TCustomEditPresenter)
protected
procedure InitPresenter; override;
end;
TCityEditPresenter = class(TCustomEditPresenter)
protected
procedure InitPresenter; override;
end;
TCityQueryPresenter = class(TCustomQueryPresenter)
protected
procedure InitPresenter; override;
function InternalQueryItemsDisplayNames: string; override;
end;
implementation
uses
ContactBO;
initialization
TContactEditPresenter.RegisterBO(TContact);
TContactQueryPresenter.RegisterBO(TContactQuery);
TContactPhoneEditPresenter.RegisterBO(TContactPhone);
TCityEditPresenter.RegisterBO(TCity);
TCityQueryPresenter.RegisterBO(TCityQuery);
end.
Now, is missing the mapping between classes and business forms. With help of Shift + Ctrl + C, implement the following codes:
procedure TContactEditPresenter.InitPresenter;
begin
inherited;
CreateSubPresenter('Name', 'NameEdit');
CreateSubPresenter('Address', 'AddressEdit');
CreateSubPresenter('Phones', 'PhonesStringGrid', 'PhoneType;Number');
CreateSubPresenter('City', 'CityComboBox', 'Name');
end;
The method CreateSubPresenter creates a presenter and their views and models. The parameter indicates respectively the name of the attribute in the business class and the name of the form's component. The third parameter, used in compositions and aggregations, indicates which attribute or attributes of the other class must be presented in control.
procedure TContactQueryPresenter.InitPresenter;
begin
inherited;
CreateSubPresenter('Name', 'NameEdit');
CreateSubPresenter('City', 'CityComboBox', 'Name');
end;
function TContactQueryPresenter.InternalQueryItemsDisplayNames: string; begin Result := 'Name;City.Name'; end;
procedure TContactPhoneEditPresenter.InitPresenter;
begin
inherited;
CreateSubPresenter('PhoneType', 'PhoneTypeComboBox');
CreateSubPresenter('Number', 'NumberEdit');
end;
procedure TCityEditPresenter.InitPresenter;
begin
inherited;
CreateSubPresenter('Name', 'NameEdit');
CreateSubPresenter('State', 'StateEdit');
end;
procedure TCityQueryPresenter.InitPresenter;
begin
inherited;
CreateSubPresenter('Name', 'NameEdit');
end;
function TCityQueryPresenter.InternalQueryItemsDisplayNames: string; begin Result := 'Name;State'; end; [edit] Testing the projectGive a Save All and Build All the project to correct the errors of compilation. Run the project, press Shift + Alt + 9 to generate the metadata from the database and open all the forms to test the bindings, but do not try to write anything yet. Note: forms and controls have the context menu (popup) driven with the right mouse button. This menu holds shortcuts used to include, change, delete and etc. the informations of that component. If all goes well, go to the next article. In case of errors, review the implementation, names of the forms and units, and the parameters of bindings in the error message.
|
Personal tools |