نوشتن برنامه دفترچه تلفن با استفاده از TMS Aurelius ORM در دلفی
برای اینکه بصورت عملی با Aurelius کار کرده باشیم، برنامه ای جهت مدیریت شماره تلفن ها(دفترچه تلفن) که در آن نام و نام خانوادگی ، شماره تلفن ثابت، شماره تلفن همراه و آدرس افراد ذخیره شده و امکان جستجو بر اساس فیلد نام و نام خانوادگی نیز در آن وجود داشته باشد را طراحی و پیاده سازی خواهیم نمود.
پیش فرض های برنامه:
- کامپوننت اتصال به پایگاه داده : FireDAC
- محل قرارگیری کامپوننت اتصال : DataModule
- نوع پایگاه داده : SQLite
ابتدا یک برنامه VCL Application ایجاد کرده و یک DataModule نیز به آن اضافه می نمائیم. برای اینکه از همین ابتدا خوانایی کدها را بالا برده و کار را بصورت مرتب و تمیز انجام داده باشیم، کدهای مربوط به تعریف موجودیت های برنامه (Entity ها) را در فایلی جداگانه ذخیره خواهیم کرد. لذا یک Unit جدید به برنامه اضافه کرده و نام آنرا Entities قرار می¬دهیم:
حالا در این Unit، کلاس های مورد نظر را تعریف می نمائیم.(قدم های پیاده سازی همانند آموزش قبل خواهد بود)
- [1]تعریف کلاس مدل
1. TContact = class
2. private
3. FFirstName: string;
4. FLastName: string;
5. FPhoneNumber: string;
6. FMobileNumber: string;
7. FAddress: string;
8. public
9. property FirstName: string read FFirstName write FFirstName;
10. property LastName: string read FLastName write FLastName;
11. property PhoneNumber: string read FPhoneNumber write FPhoneNumber;
12. property MobileNumber: string read FMobileNumber write FMobileNumber;
13. property Address: string read FAddress write FAddress;
14. end;
- [2]نگاشت کلاس
همانطور که در آموزش پیشین اشاره شد، برای آنکه Aurelius یک کلاس را به عنوان یک موجودیت در نظر گرفته و آنرا در دیتابیس ذخیره و یا بازیابی نماید، ابتدا باید ویژگی(Attribute) موجودیت ([Entity]) را به کلاس اضافه نمائیم و به اصطلاح کلاس را به این ویژگی آراسته نمائیم (Decorate). با توجه به هوشمند بودن Aurelius، سایر نگاشت ها (مواردی چون نام گذاری فیلدها، تعیین طول رشته ها، نوع فیلد ها و...) را به عهده Aurelius می گذاریم. لذا علاوه بر ویژگی [Automapping]، فیلدی بنام Id به پروپرتی های کلاس اضافه می نمائیم.(این فیلد با همین نام جهت عملکرد صحیح Automapping ضروری می¬باشد).
نکته : برای اینکه ویژگی های مورد اشاره توسط دلفی قابل شناسایی باشند و در زمان کامپایل خطای عدم شناسایی ندهد، می بایست یونیت Aurelius.Mapping.Attributes به قسمت Interface اضافه گردد.
1. uses
2. Aurelius.Mapping.Attributes;
3.
4. type
5.
6. [Entity]
7. [Automapping]
8. TContact = class
9. private
10. FId: Integer;
11. FFirstName: string;
12. FLastName: string;
13. FPhoneNumber: string;
14. FMobileNumber: string;
15. FAddress: string;
16. public
17. property Id: Integer read FId write FId;
18. property FirstName: string read FFirstName write FFirstName;
19. property LastName: string read FLastName write FLastName;
20. property PhoneNumber: string read FPhoneNumber write FPhoneNumber;
21. property MobileNumber: string read FMobileNumber write FMobileNumber;
22. property Address: string read FAddress write FAddress;
23. end;
- [3]تعریف اینترفیس IDBConnection
و
- [4]تعیین the SQL dialect
برای تسهیل در روند دسترسی به اینترفیس IDBConnection، در یونیت DataModule، تابعی تعریف می¬کنیم که خروجی آن IDBConnection باشد.(یونیت های Aurelius.Drivers.Interfaces و Aurelius.Drivers.FireDac و Aurelius.SQL.SQLite را به قسمت Uses اضافه می کنیم). حال هرگاه نیاز به IDBConnection داشتیم، این تابع را فراخوانی خواهیم نمود.
1. unit DM;
2.
3. interface
4.
5. uses
6. System.SysUtils, System.Classes, FireDAC.Stan.Intf, FireDAC.Stan.Option,
7. FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def,
8. FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys, Data.DB,
9. FireDAC.Comp.Client,
10. Aurelius.Drivers.Interfaces, Aurelius.Drivers.FireDAC, Aurelius.Sql.SQLite;
11.
12. type
13. TDataModule1 = class(TDataModule)
14. FDConnection1: TFDConnection;
15. private
16. { Private declarations }
17. public
18. { Public declarations }
19. function GetConnection: IDBConnection;
20. end;
21.
22. var
23. DataModule1: TDataModule1;
24.
25. implementation
26.
27. {$R *.dfm}
28.
29. function TDataModule1.GetConnection: IDBConnection;
30. begin
31. Result := TFireDacConnectionAdapter.Create(FDConnection1, False);
32. end;
33.
34. end.
- [5]آماده سازی اتصال به دیتابیس و ساخت آن
قبل از اینکه اقدام به ساخت جداول توسط Aurelius نمائیم، لازم است ابتدا فایل فیزیکی دیتابیس ساخته و سپس کدهای اتصال FDConnection را بنویسیم. همچنین درایور مورد نیاز دیتابیس نیز باید در دسترس فایل اجرایی باشد. لذا فایل فیزیکی دیتابیس و درایور مربوطه را در پوشه ای بنام Data در مجاورت فایل اجرایی قرار می دهیم.
نکته : برای ساخت فایل فیزیکی دیتابیس، از هر ابزاری که این توانایی را داشته باشد می توان استفاده نمود و محدودیت در آن نیست(حتی میتوان فایلی را که پیش از این برای برنامه های دیگر ساخته شده را کپی نمود). برای نمونه نرم افزار SQLite Expert بسیار کاربر پسند می باشد.
نکته : آدرس دانلود درایور های SQLite : https://sqlite.org/
نکته : آدرس دانلود SQLite Expert : http://soft98.ir/software/programmin...te-Expert.html
همانطور که در تصویر بالا مشخص است، در نرم افزار SQLite Expert صرفا فایل دیتا بیس را می سازیم و ساخت جداول و سایر موارد بر عهده Aurelius خواهد بود.
در رویداد Create دیتاماژول نیز کدهای مربوط به تنظیمات اتصال به دیتابیس را به صورت زیر می نویسیم :
1. procedure TDataModule1.DataModuleCreate(Sender: TObject);
2. begin
3. with FDConnection1 do
4. begin
5. Close;
6. Params.Clear;
7. Params.Add('DriverID=SQLite');
8. Params.Add('Database=' + ExtractFilePath(ParamStr(0)) + 'Data\PhoneBookDB.db')
9. end;
10. end;
حالا می توانیم دستورات مربوط به ساخت جداول را بنویسیم. عملیات ساخت جداول، از بین بردن جداول و نیز اعتبارسنجی صحت شمای دیتابیس(Schema Validation) با استفاده از شیء DatabaseManager انجام می شود. جهت ساخت این شیء میبایست یک IDBconnection به تابع Create آن پاس داده شود. برای این منظور از تابع ای که در مراحل قبل تهیه شد(GetConnection) استفاده خواهیم نمود.
یک MainMenu بر روی فرم اصلی برنامه قرار داده و منوی "تنظیمات" و سپس زیرمنوی "ساخت دیتایس" را به آن اضافه می کنیم(همانند تصویر زیر) :
بر روی منوی "ساخت دیتابیس" دوبار کلیک نموده و کدهای زیر را می نویسیم( اضافه نمودن یونیت Aurelius.Engine.DatabaseManager به قسمت Uses فراموش نشود) :
1. procedure TForm_Main.MnuItm_N2Click(Sender: TObject);
2. var
3. DBManager: TDatabaseManager;
4. begin
5. try
6. DBManager := TDatabaseManager.Create(DataModule1.GetConnection) ;
7. DBManager.DestroyDatabase;
8. DBManager.BuildDatabase;
9. finally
10. FreeAndNil(DBManager);
11. end;
12. end;
در دستورات بالا، ابتدا جداولی که پیش از این ساخته شده توسط دستور DestroyDatabase تخریب شده و سپس جداول جدید با توجه به کلاسی هایی که به عنوان Entity معرفی شده اند با استفاده از دستور BuildDatabase ساخته می شود(در برنامه های رسمی، این دستورات باید با توجه و احتیات کامل فراخوانی گردد).
اگر برنامه را اجرا کرده و بر روی دکمه کلیک نمائید، دیتابیس ساخته خواهد شد. برنامه را ببندید و نتیجه را در SQLite Express مشاهده نمائید.
نتیجه مایوس کننده خواهد بود! هیچ جدولی ساخته نشده است!
این بخاطر آن است که Aurelius تا زمانیکه از موجودیت ها درون کدهای برنامه استفاده ای نشود، هیچ چیز را بعنوان موجودیت نمی شناسد، لذا شیء DatabaseManager نیز موجودیتی را تبدیل به جدول نخواه نمود. برای این منظور از تابع RegisterEntity در قسمت initialization یونیت Entities استفاده مینمائیم. این تابع به Aurelius میفهماند که این کلاس در برنامه استفاده شده است.
1. unit Entities;
2.
3. interface
4.
5. uses
6. Aurelius.Mapping.Attributes;
7.
8. type
9.
10. [Entity]
11. [Automapping]
12. TContact = class
13. private
14. FId: Integer;
15. FFirstName: string;
16. FLastName: string;
17. FPhoneNumber: string;
18. FMobileNumber: string;
19. FAddress: string;
20. public
21. property Id: Integer read FId write FId;
22. property FirstName: string read FFirstName write FFirstName;
23. property LastName: string read FLastName write FLastName;
24. property PhoneNumber: string read FPhoneNumber write FPhoneNumber;
25. property MobileNumber: string read FMobileNumber write FMobileNumber;
26. property Address: string read FAddress write FAddress;
27. end;
28.
29. implementation
30.
31. initialization
32.
33. RegisterEntity(TContact);
34.
35. end.
مجددا برنامه را اجرا نموده و بعد از فشردن دکمه و بستن برنامه، نتیجه را در SQLite Express مشاهده می نمائیم:
همانطور که در تصویر بالا قابل مشاهده است، جدولی با نام Contact و فیلدهای مربوطه(بر اساس نام پروپرتی ها) ساخته شده است.
- [6]نمونه سازی از کلاس و ذخیره اطلاعات در دیتابیس
علاوه بر شیء DatabaseManager، شیء مهم دیگری نیز وجود دارد که عملیات ذخیره و بازیابی اطلاعات را بر عهده دارد. این شیء ObjectManager بوده که همانند DatabaseManager جهت انجام عملیات نیاز است در زمان ساخت، مقدار IDBConnection به آن پاس داده شود.
یک فرم جدید ساخته و با نام Editors.ContactForm ذخیره می نمائیم.(این نام فایل بوده و نام فرم را ContactForm تعیین میکنیم). برای هر یک از پروپرتی های کلاس Contact یک Label و یک Edit بر روی فرم قرار می دهیم:
اگر دقت کنیم، عملیات ایجاد یک مخاطب جدید همانند ویرایش آن بوده، با این تفاوت که در زمان ذخیره مخاطب جدید، مقدار فیلد Id خالی بوده و توسط Aurelius مشخص و مقداردهی می گردد اما در زمان ویرایش مقدار Id مشخص بوده و Aurelius بر اساس همین مقدار عملیات بروزرسانی را انجام می دهد.
برای اینکه از کدنویسی تکراری جلوگیری کنیم، از فرم بالا علاوه بر ایجاد مخاطب جدید، جهت ویرایش مخاطب نیز استفاده خواهیم نمود. لذا لازم است که از چند ویژگی خوب زبان شیرین دلفی استفاده کنیم.
در بخش Public فرم TcontactForm یک Class function بنام EditContact نوشته که وظیفه ایجاد یک نمونه از فرم TcontactForm و نمایش آن بشکل Modal را بر عهده خواهد داشت. همچنین یک Property بنام Contact نیز تعریف میکنیم که وظیفه نگهداری مقدار اطلاعات مخاطب جدید و یا مخاطب ویرایشی را بر عهده خواهد داشت(فراموش نشود که فرم ContactForm از حالت ساخت خودکار(AutoCreate) خارج شود):
1. type
2. TContactForm = class(TForm)
3. btn_Save: TBitBtn;
4. grp_1: TGroupBox;
5. lbl_1: TLabel;
6. lbl_11: TLabel;
7. lbl_12: TLabel;
8. lbl_13: TLabel;
9. lbl_14: TLabel;
10. edt_FirstName: TEdit;
11. edt_LastName: TEdit;
12. edt_PhoneNumber: TEdit;
13. edt_MobileNumber: TEdit;
14. edt_Address: TEdit;
15. btn_Cancel: TBitBtn;
16. procedure btn_SaveClick(Sender: TObject);
17. procedure btn_CancelClick(Sender: TObject);
18. private
19. FContact: TContact;
20. procedure SetContact(const Value: TContact);
21. { Private declarations }
22. public
23. { Public declarations }
24. property Contact: TContact read FContact write SetContact;
25. class function EditContact(Contact: TContact): Boolean;
26. end;
1. class function TContactForm.EditContact(Contact: TContact): Boolean;
2. var
3. AForm: TContactForm;
4. begin
5. try
6. AForm := TContactForm.Create(nil);
7. AForm.Contact := Contact;
8. Result := (AForm.ShowModal = mrOk);
9. finally
10. FreeAndNil(AForm);
11. end;
12. end;
1. procedure TContactForm.SetContact(const Value: TContact);
2. begin
3. FContact := Value;
4.
5. edt_FirstName.Text := FContact.FirstName;
6. edt_LastName.Text := FContact.LastName;
7. edt_PhoneNumber.Text := FContact.PhoneNumber;
8. edt_MobileNumber.Text := FContact.MobileNumber;
9. edt_Address.Text := FContact.Address;
10. end;
مقادیر کلاس Contact که به فرم پاس داده میشود(خواه جهت ویرایش و خواه جهت ایجاد) در متد SetContact، به Edit های متناظر بر روی فرم تخصیص داده می شود. واضح است که اگر مخاطب جدید باشد، فیلد های مربوطه خالی بوده و لذا مقدار خالی به Edit ها اختصاص می یابد و اگر مخاطب بمنظور ویرایش باشد، Edit ها خالی نبوده و مقادیر را نمایش خواهند داد.
در MainMenu مربوط به فرم اصلی، منوی "مخاطبین" و ذیل آن زیرمنوی "ایجاد مخاطب جدید" را ایجاد نموده و در رویداد Click آن کد نمایش فرم ContactForm را می نویسیم:
1. procedure TForm_Main.N3Click(Sender: TObject);
2. var
3. AContact: TContact;
4. ObjManager: TObjectManager;
5. begin
6. try
7. AContact := TContact.Create;
8. if TContactForm.EditContact(AContact) then
9. begin
10. try
11. ObjManager := TObjectManager.Create(DataModule1.GetConnection);
12. ObjManager.Save(AContact);
13. finally
14. FreeAndNil(ObjManager);
15. end;
16. end
17. else
18. begin
19. FreeAndNil(AContact);
20. end;
21. except
22. on E: Exception do
23. begin
24. FreeAndNil(AContact);
25. raise;
26. end;
27. end;
28. end;
در کد بالا، ابتدا یک نمونه از کلاس Tcontact ساخته و آنرا به تابع EditContact که بصورت Class function است پاس داده می شود. در ادامه فرم مذکور بصورت مودال نمایش داده شده و کاربر میتواند مقادیر را وارد نماید. در صورتیکه کاربر بر روی دکمه ذخیره کلیک نماید، مقادیر Edit ها به فیلدهای Contact تخصیص داده می شود و ModalResult نیز mrOk میشود، در نتیجه فرم بسته شده و ادامه کدهای منوی کلیک شده اجرا میشود.
یک نمونه از شیء ObjectManager ساخته شده و نمونه Acontact ذخیره می گردد.
مجددا برنامه را اجرا کرده و از منوی "مخاطبین"، بر روی زیرمنوی "مخاطب جدید" کلیک کرده و پس از ورود مقادیر بر روی دکمه "ذخیره" کلیک می نمائیم.
حال برنامه را بسته و نتیجه را در SQLite Express مشاهده می نمائیم!
اگر مقادیر رشته های ذخیره شده همانند تصویر بالا بصورت ؟؟؟؟ است جای هیچ نگران ای نیست. تنها کافیست دستور زیر به بخش initialization یونیت Entities اضافه گردد(یونیت Aurelius.Global.Config نیز باید Use گردد) :
1. TGlobalConfigs.GetInstance.MapStringToNationalChar := True;
موفق باشیم