You are not logged in.
But, it is a many (TDoctor) to one (TLocArea) relation, not a m:n.
In this case, I would write it as follows:
type
TLocationArea = class(TOrm)
...
TLocationAreaID = type TID;
TDoctor = class(TOrm)
...
published
property LocationAreaID: TLocationAreaID
read fLocationAreaID write fLocationAreaID;
var
dataArr: TArray<TDoctor>;
begin
...RetrieveListObjArray(dataArr, TDoctor, 'LocationAreaID=?', [1234]);Or if no automatism is desired, then like this: "property LocationAreaID: TID".
With best regards
Thomas
where I can find Mormot 1 SynDbremote in new Mormot2?
If you change from mORMot version 1 to 2, a simple grep over the new source code is often helpful. When I search for "dbremote" I get hits that don't look bad. Can't say more, never used these classes myself.
With best regards
Thomas
Create with parameters and CreateAndFiilPrepare don’t work anymore. Any recommendations how to use TCollection and TPersistance from now on?
You make the same mistake again. If you do your own initializations in the constructor of an ORM object, you can NEVER take the overloaded constructors as long as Arnaud solved the problem with InternalCreate function. The overridden constructor is not called and thus the own variable classes are not initialized and all calls go to nothing. I think that this change of Arnaud was very unfortunate, but currently it is as it is. Change it as follows:
procedure TTestData.TestCreateAndFillPrepare;
...
Order := TOrmOrderBook.Create; // AndFillPrepare(TestClient.Orm, '', []);
try
Order.FillPrepare(TestClient.Orm, '', []);
procedure TTestData.TestCreate;
...
Order := TOrmOrderBook.Create; // (TestClient.Orm, 'OrderNo = ?', ['Order1']);
try
TestClient.Orm.Retrieve(FormatUtf8('OrderNo = ?', [], ['Order1']), Order);I think if you want changes, you need to push Arnaud a little bit. ![]()
With best regards
Thomas
Does mormot2 not support delphi7?
As described in the documentation: "Note that before Delphi 2006, you will need to download and install FastMM4 heap memory manager".
With best regards
Thomas
I updated the gist. Please check again https://gist.github.com/martin-doyle/00 … ceac4698fc.
Delphi 11.3, mORMot 2.1.5499
I have only tested case TTestReplicationSimple "AddCase([TTestReplicationSimple])" for the first time. I get this error reported:
EInOutError {Message:"E/A-Fehler 6"} [Main] at 6c74b3When I run all three test cases, I get this error reported:
...
fail #4064 SameRecord ID Master: 107 Slave: 107
fail SynReplicationTests.TTestReplicationBatch(02fea050) Replication batch - Test batch update [SameRecord ID Master: 107 Slave: 107]
EXC EInOutError {Message:"E/A-Fehler 6"} [Main] at 6b84b3I think every test case should pass without exception. Sorry, have no time to take a closer look in the next few days.
With best regards
Thomas
I created some tests in https://gist.github.com/martin-doyle/00 … ceac4698fc.
I think your upload is not complete. The file Test1.json is missing.
With best regards
Thomas
Here is a working example with results in csv: https://gist.github.com/avavdoshin/fd1d … f8f0638691
Record versions in this example doesn't changes for updates, so this changes will not be replicated to slave database.
I think you are using Batch incorrectly in your example. In mORMot "ex" and "test" directory, or in this forum post, you can find examples of how to use TRestBatch. Just search for "TRestBatch.Create", "BatchStart" or "BatchSend". Use TRestBatch in your example exactly as they are used in mORMot. Then compare the results and report it here. Or someone else from the forum can help. Maybe I have some time on the weekend, then I can also look again. Otherwise you have to wait until Arnaud is back.
With best regards
Thomas
Does anybody else using TRecordVersion in mormot2?
I use TRecordVersion. Since I haven't noticed anything negative yet, I haven't looked for a bug either. Can you give us any more information, such as:
Delphi 11.3, mORMot2 GitHub commit 5186Or a whole example, as Martin Doyle has done here. Then it is easier to search for the error.
With best regards
Thomas
By the way, I like the idea of embedding detail data within the record (sharding). Would be interesting to know what other people use, just schemaless TDocVariant?
I use it in conjunction with SOA and WebApps (TMcvApplication) when records are mostly just read, and can recommend it. This saves many JOINs when combining the data from different tables of the DB. As an example, you have events with presentations. In the overview are displayed: Date, topic, location, room, speaker, etc. Only the date comes from the schedule table. Everything else are foreign keys into other tables. Having an extra field here that contains all the data from other tables needed for display reduces the effort considerably and makes viewing fast. And because the data composition changes very rarely and everything is handled via SOA functions, it is practically no effort to maintain this field.
With best regards
Thomas
How could I solve this?
As described in the documentation:
// - on success, returns the new RowID value; on error, returns 0
// - on success, Value.ID is updated with the new RowIDIn service you can write the following:
type
TOrmArticle = class(TOrm);
function TArticleService.Add(const pmcItem: TOrmArticle): TID;
var
restServer: TRestServerDB;
begin
Result := -1;
if pmcItem <> Nil then
begin
restServer := GetDBRestServer;
if restServer <> Nil then
Result := restServer.Server.Add(pmcItem, True, False);
end;
end;In client then the following:
var
data: TOrmArticle;
begin
...
data.IDValue := service.Add(data);
if data.IDValue > 0 thenOr have a look into function AddOrUpdate.
With best regards
Thomas
Could you add the skippedFile(s) functionality also to the AddFile() function?
This is not necessary because AddFile() does not include a locked file. See following example:
var fileStream: THandleStream := Nil;
try
var testFile: TFileName := 'c:\mORMot2\src\lib\mormot.lib.win7zip.pas';
fileStream := TFileStreamEx.Create(testFile, fmOpenRead);
var skippedFiles: TFileNameDynArray;
var libWriter: I7zWriter := New7zWriter(fh7z);
libWriter.SetCompressionLevel(3);
if libWriter.AddFile(testFile, StringToUtf8(ExtractFileName(testFile))) then
libWriter.SaveToFile(MakePath([Executable.ProgramFilePath, 'TestData.7z']))
else
AddString(TStringDynArray(skippedFiles), testFile);
for var fileName: TFileName in libWriter.AddFiles('c:\mORMot2\src\', '', '*.pas;*.inc', True) do
AddString(TStringDynArray(skippedFiles), fileName);
ShowMessage(Format('File(s) could not be added! Count: %d', [Length(skippedFiles)]));
finally
fileStream.Free;
end;With best regards
Thomas
Is TCollection still supported and what is the recommended way of using it?
You are right, now you have to write the following:
procedure TTestData.TestRetreiveOrmWithCollection;
...
TestClient := TRestClientDB.Create(FModel, nil, DataFile, TRestServerDB, false, '');
try
Order := TOrmOrderBook.Create;
try
if Order.FillPrepare(TestClient.Orm, '', []) then
beginThe documentation is also not entirely clear at this point. Let's see what Arnaud says when he gets back.
With best regards
Thomas
In case I don't want a calculated field to be actually created as a db table field, do I have to use a public calculation function instead (e.g. GetFullName), and not a published property?
Yes, you can write it like this:
TPerson = class(TOrm)
private
...
function GetFullName: RawUTF8;
public
property FullName: RawUTF8 read GetFullName;
published
property FirstName: RawUTF8 index 50 read fFirstName write fFirstName;
property SurName: RawUTF8 index 50 read fSurName write fSurName;
end;But for clarity, I would write it this way:
TCustomPerson = class(TOrmBaseRecord)
private
fFirstName, fSurName: RawUTF8;
published
property FirstName: RawUTF8 index 50 read fFirstName write fFirstName;
property SurName: RawUTF8 index 50 read fSurName write fSurName;
end;
TPerson = class(TCustomPerson)
private
function GetFullName: RawUTF8;
public
property FullName: RawUTF8 read GetFullName;
end;Then, if necessary, you can derive the ORM object TOrmBaseRecord on the server or client from different base objects using a compiler switch. On the server derived from the class TOrm and on the client from the class TObjectWithID. All TCustom... classes could be shared between server and client. The specializations are done only in the final classes. I have already described the technique here in the forum.
With best regards
Thomas
I' understand that I do something really wrong but how should I do?
For me, the following looks clearer:
procedure TSubProject.AddArticle(pmArticle: TArticle);
begin
if pmArticle <> Nil then
ObjArrayAdd(FArticles, pmArticle);
end;
function TDHSSyncRepository.AddArticleToSubProject(pmSubProject: TSubProject; out pmoArticle: TArticle): TSyncRepoError;
begin
Result := srNotFound;
if pmSubProject = Nil then Exit; //=>
if RetrieveArticle(pmoArticle) <> srSuccess then Exit; //=>
pmSubProject.AddArticle(pmoArticle);
Result := srFound;
end;With best regards
Thomas
Please try https://github.com/synopse/mORMot2/commit/9674e7ee
- the exception should be intercepted for locked files during the process
- AddFiles() will return the list of locked files which were not marked to be added
Commit 5441 (9674e7e) works as expected. Thanks for this. The following test was passed:
var fileStream: THandleStream := Nil;
try
fileStream := TFileStreamEx.Create('c:\mORMot2\src\lib\mormot.lib.win7zip.pas', fmOpenRead);
var libWriter: I7zWriter := New7zWriter(fh7z);
var skippedFiles: TFileNameDynArray := libWriter.AddFiles('c:\mORMot2\src\', '', '*.pas;*.inc', True);
if Length(skippedFiles) > 0 then
ShowMessage(Format('Skipped Files: %d', [Length(skippedFiles)]));
libWriter.SaveToFile(MakePath([Executable.ProgramFilePath, 'TestData.7z']));
finally
fileStream.Free;
end;IsFileReadable function added in AddFiles makes no measurable difference in timing.
With best regards
Thomas
Maybe the ProgressCallback could result/send something once a file is done compressing or once a file has not been compressed due to an error. Like this in ProgressCallback we could catch these errors for logging.
Or an additional wish, the function AddFiles would have as return a list of files that could not be packed.
With best regards
Thomas
1) The files are opened as fmOpenReadDenyNone = fmOpenRead or fmShareDenyNone
in T7zWriter.GetStream() so they should be opened once currently opened by another program.
If you want to save a folder with function I7zWriter.AddFiles and open a file of this folder before, an exception is thrown:
var fileStream: THandleStream := TFileStreamEx.Create('c:\mORMot2\src\lib\mormot.lib.win7zip.pas', fmOpenRead);
try
var libWriter: I7zWriter := New7zWriter(fh7z);
libWriter.AddFiles('c:\mORMot2\src\', 'mORMot2_2023-05-01', '*.pas;*.inc', True);
libWriter.SaveToFile(MakePath([Executable.ProgramFilePath, 'TestMultiData.7z']));
finally
fileStream.Free;
end;The two exceptions are as follows:
ExceptionMessage="TFileStreamEx.Create(c:\mORMot2\src\lib\mormot.lib.win7zip.pas) failed as ERROR_SHARING_VIOLATION"
ExceptionName="EOSException"
ExceptionAddress=763A8FC2
---------------------------
ExceptionMessage="T7zWriter.SaveToStream error 8007000E (Für diesen Vorgang sind nicht genügend Speicherressourcen verfügbar)"
ExceptionName="E7Zip"
ExceptionAddress=763A8FC2The behavior is normal, unless the file would have been opened with fmOpenRead or fmShareDenyWrite/ or fmShareDenyNone. The question would be rather, couldn't you skip this file without canceling the whole process?
With best regards
Thomas
Commit 5434 (e4a5ff6) works as expected. All my tests passed. Thanks for this.
With best regards
Thomas
It seems to require too much effort to be worth solving ?
In the client program I work with Spring4D. Here there is also an IList. For this I have connections for various components. The bridge between the two looks like this:
type
TOrmArticleList = class(Spring.Collections.Lists.TObjectList<TOrmArticle>);
TOrmArticleObjArray = array of TOrmArticle;
function ...CreateArticleList(const pmcSectionID: TID): TOrmArticleList;
var
service: IArticle;
dataArr: TOrmArticleObjArray;
begin
if not dmDB.RestServer.Resolve(IArticle, service) then Exit(Nil); //=>
service.GetAllItems(pmcSectionID, [asActive], dataArr);
Result := TOrmArticleList.Create(dataArr, True);
end;mORMot provides an ObjArray that fills the list. With the Spring4D classes I have many functions that I don't have to implement myself. If you only need the IList interface, you can write like this:
Result := TCollections.CreateObjectList<TOrmArticle>(dataArr, True);With best regards
Thomas
I am not sure it is possible to modify an archive in place, to be honest.
With my modification for function SaveToFile, it works for this test:
procedure SaveFolderTo7Zip(const pmcFolder, pmc7ZipFileName: TFileName;
const pmc7ZipInternalDir: TFileName = ''; const pmcFileMask: TFileName = '*';
pmNew7Zip: Boolean = False; const pmOnProgress: T7zProgressCallback = Nil);
var
libWriter: I7zWriter;
begin
if not pmNew7Zip
and FileExists(pmc7ZipFileName) then
begin
libWriter := New7zWriter(pmc7ZipFileName, fh7z)
end
else
libWriter := New7zWriter(fh7z);
libWriter.SetCompressionLevel(3);
if Assigned(pmOnProgress) then
libWriter.SetProgressCallback(pmOnProgress);
libWriter.AddFiles(pmcFolder, pmc7ZipInternalDir, pmcFileMask, True);
libWriter.SaveToFile(pmc7ZipFileName);
end;Call then as follows:
var
zipFileName: TFileName;
begin
zipFileName := MakePath([Executable.ProgramFilePath, 'TestMultiData.7z']);
SaveFolderTo7Zip('c:\mORMot2\src\', zipFileName, 'mORMot_2023-05-01', '*.pas;*.inc', {pmNew7Zip=}False, ProgressCallback);
SaveFolderTo7Zip('c:\mORMot2\src\', zipFileName, 'mORMot_2023-05-02', '*.pas;*.inc', {pmNew7Zip=}False, ProgressCallback);
SaveFolderTo7Zip('c:\mORMot2\src\', zipFileName, 'mORMot_2023-05-03', '*.pas;*.inc', {pmNew7Zip=}False, ProgressCallback);
SaveFolderTo7Zip('c:\mORMot2\src\', zipFileName, 'mORMot_2023-05-04', '*.pas;*.inc', {pmNew7Zip=}False, ProgressCallback);
SaveFolderTo7Zip('c:\mORMot2\src\', zipFileName, 'mORMot_2023-05-05', '*.pas;*.inc', {pmNew7Zip=}False, ProgressCallback);
end;The result in TestMultiData.7z file looks as expected:
mORMot_2023-05-01\app
mORMot_2023-05-01\core
...
mORMot_2023-05-02\...
mORMot_2023-05-03\...
mORMot_2023-05-04\...
mORMot_2023-05-05\...I have checked with a tool that compares directories, everything is OK for me.
With best regards
Thomas
The following source code for testing. If 7z file exists, open it, otherwise create it:
var zipFileName: TFileName := MakePath([Executable.ProgramFilePath, 'TestDaten.7z']);
if FileExists(zipFileName) then
libWriter := New7zWriter(zipFileName, fh7z)
else
libWriter := New7zWriter(fh7z);For it to work, function SaveToFile must be changed as follows:
begin
fFileName := DestName;
// f := TFileStreamEx.Create(DestName, fmCreate);
if fUpdateReader <> Nil then
f := TFileStreamEx.Create(DestName, fmOpenWrite or fmShareDenyNone)
else
f := TFileStreamEx.Create(DestName, fmCreate);
try
SaveToStream(f);Without this change it makes no sense to open a file for editing because it cannot be saved with SaveToFile.
With best regards
Thomas
Now these tests have been passed:
libWriter.AddFiles('c:\mORMot2\src\', '', '*', True);
libWriter.AddFiles('c:\mORMot2\src\', 'mORMot_2023-05-05', '*', True);
libWriter.AddFiles('c:\mORMot2\src\', 'mORMot_2023-05-05', '*.pas', True);
libWriter.AddFiles('c:\mORMot2\src\', 'mORMot_2023-05-05', '*.pas;*.inc', True);With this it works as expected. Thanks for this.
With best regards
Thomas
Delphi 11.3, mORMot2 GitHub commit 5420 (e4ccc8e)
Function I7zWriter.AddFiles:
/// add (or replace) some files from a folder within the archive
procedure AddFiles(const Dir, Path, Wildcard: TFileName; recurse: boolean);I can't find a reasonable use for the "Path" and "Wildcard" parameters. If I write the following:
libWriter.AddFiles('c:\mORMot2\src\', 'mormot_2023-05-04', '*', True);The result is a 7z file without files with a directory structure that looks like this:
mormot_2023-05-04\c_\mORMot2\src\app
mormot_2023-05-04\c_\mORMot2\src\core
...It would be interesting if "Path" was the starting path in the 7z file. Then you could create something like this, for example:
mormot_2023-04-28\app
mormot_2023-04-28\core
...
mormot_2023-05-04\app
mormot_2023-05-04\coreThis would be a very useful functionality.
I can't find a useful use for the "Wildcard" either. Only for "*" I get a result. Which values are intended here? The only solution that works for me is:
libWriter.AddFiles('c:\mORMot2\src\', '', '*', True);I would have liked it better to use FindFiles from unit mormot.core.search here.
With best regards
Thomas
With Commit 5420 (e4ccc8e), it behaves as expected in my tests. Thanks a lot for that.
With best regards
Thomas
There was no leak I am afraid. The TRawByteStream was properly released.
With your soReference modification, I have an access violation now during normal process.
You're right. Sorry, didn't test properly there. That works:
libWriter.AddStream(TRawByteStringStream.Create(content), soReference, faArchive, 0, 0, 'TestFolder', false, false);A stream with no content for any combination results in an memory leak:
libWriter.AddStream(TRawByteStringStream.Create(''), soReference {soOwned}, faDirectory, 0, 0, 'TestFolder', True, False);This error message is displayed:
An unexpected memory leak has occurred. The unexpected small block leaks are:
21 - 28 bytes: TRawByteStringStream x 1Without content it only works properly with soOwned. That was the thread that triggered this post.
With best regards
Thomas
Could a function like this be introduced:
function MakeFileName(const pmcPart: array of const; pmLastIsExt: Boolean): TFileName;
var
ext: RawUtf8;
begin
if pmLastIsExt
and (Length(pmcPart) > 1)
and VarRecToUtf8IsString(pmcPart[High(pmcPart)], ext) then
begin
Result := MakePath(Slice(pmcPart, Length(pmcPart) - 1));
if (Result <> '')
and (ext <> '') then
begin
if ext[1] = '.' then
Result := Result + Utf8ToString(ext)
else
Result := Result + '.' + Utf8ToString(ext);
end;
end
else
Result := MakePath(pmcPart);
end;Then you could write the following without looking at anything:
const
PNG_EXT = 'png';
begin
ShowMessage(MakeFileName([Executable.ProgramFilePath, 'One', 'TestFile', PNG_EXT], True));
ShowMessage(MakeFileName(['C:', 'One', 'TestFile', ExtractFileExt(Executable.ProgramFileName)], True));With best regards
Thomas
Delphi 11.3, mORMot2 GitHub commit 5395 (bda0951)
1) Memory leak in function T7zWriter.AddBuffer. Must soOwned be used and not soReference.
procedure T7zWriter.AddBuffer(const ZipName: RawUtf8;
const Data: RawByteString);
begin
AddStream(TRawByteStringStream.Create(Data), soReference, // <-- soOwned
faArchive, 0, 0, ZipName, false, false);
end;2) In function T7zWriter.AddStream should be written:
...
item.Stream := Stream;
// item.Size := Stream.Size;
if Stream <> Nil then
item.Size := Stream.Size;3) Can an T7zWriter.AddDirectory function be added?
With best regards
Thomas
It may help some people, ...
That was the reason I asked. I prefer to answer questions in a forum about programming problems with concrete source code. Most with reference to mORMot. The new functions, which are unknown, often discourage, although the solution, thanks to mORMot, is better. If it was more similar to actual Delphi source code, it could be easier to teach. Rarely you get such a concrete feedback how good the result has become by using mORMot.
With best regards
Thomas
Could a record helper for RawUtf8 be useful for current Delphi versions?
type
TRawUtf8Helper = record helper for RawUtf8
public
procedure From(const pmcValue: String); inline;
function ToString: String; inline;
function ToInteger: Integer; overload; inline;
function ToInteger(pmDefault: Integer): Integer; overload; inline;
function ToInteger(pmMin, pmMax: Integer; pmDefault: Integer = 0): Integer; overload; inline;
procedure TRawUtf8Helper.From(const pmcValue: String);
begin
Self := StringToUtf8(pmcValue);
end;
function TRawUtf8Helper.ToInteger: Integer;
begin
Result := Utf8ToInteger(Self);
end;
function TRawUtf8Helper.ToInteger(pmDefault: Integer): Integer;
begin
Result := Utf8ToInteger(Self, pmDefault);
end;
function TRawUtf8Helper.ToInteger(pmMin, pmMax, pmDefault: Integer): Integer;
begin
Result := Utf8ToInteger(Self, pmMin, pmMax, pmDefault);
end;
function TRawUtf8Helper.ToString: String;
begin
Result := Utf8ToString(Self);
end;Then you could write like this:
var
s: String;
u: RawUtf8;
begin
s := '12';
u.From(s);
ShowMessage(u.ToString);
var i: Integer := u.ToInteger(8, 12);With best regards
Thomas
I have published my seventh article with the topic mORMot here (forum Delphi-PRAXIS). The title is: Realize a JSON-Viewer with DocVariant and Virtual TreeView. The text is written in German, the sourcecode uses english names. Sourcecode and program can be found in the appendix.
The program is a simple JSON-Viewer. With the example code you get:
A document can be loaded from a file or imported via the clipboard. It can be saved again in packed format as a file.
Individual branches of a document can be saved as a file or exported to the clipboard as an object with extension root node as an attribute.
Each branch can be displayed in a separate view window and closed by double-clicking on the tab. In multiple view the delete function is locked.
A simple editor to edit values of attributes or to delete entries or whole branches.
Here is the translation into English with Google Translator. The result is not perfect (rather not so good), also some formatting is destroyed, but it is readable.
With best regards
Thomas
This thread continues the series "A small example in response to a question". The articles were published in German in the Delphi Praxis Forum. Included in the article announcement is a link for translation into English using Google Translator. The result is not perfect, also some formatting is destroyed, but it is readable.
If you liked an article, you can leave a comment in the Delphi-Praxis Forum. It is a friendly forum and comments in English language are possible. If you write a comment, please do not follow each other too quickly, so that the article is well placed in the activities view for a long time.
With best regards
Thomas
I don't think so.
You are right. When I look at it in Delphi 11.3, it is highlighted correctly. I had looked at it with another editor and there the display was misleading. I think the spelling {$else OSWINDOWS} confused me because I just write {$else}. Sorry for the wrong warning.
With best regards
Thomas
Delphi 11.3, mORMot2 GitHub commit 5323 (752e7f6), unit mormot.core.os
I think the second last {$else OSWINDOWS} is too much here.
{$ifdef OSWINDOWS} // line 1030
{$ifdef UNICODE} // line 1032
...
{$else} // line 1039
...
{$endif UNICODE} // line 1062
...
{$else OSWINDOWS} // line 1082
...
{$endif OSWINDOWS} // line 1101With best regards
Thomas
Thanks igor for help, but it do not work for me.
You do not register the default helper, so "IF" is not available to you. You have to write it like this:
mustache.RenderJSON(Context, Nil, TSynMustache.HelpersGetStandardList)All in all, I would not solve this during generation, but calculate it outside and then pass it. How to use Mustache is described in this article with source code.
With best regards
Thomas
Delphi 11.3, mORMot2 GitHub commit 5225 (da76642)
The word "false" is displayed incorrectly in the output. It concerns only the case "false" and not "true". All other data types are also displayed correctly. The string for "false" has the hex value "05 66 61 6C 73", but should have "66 61 6C 73 65". You can reproduce it with following test source code:
var
p: PDocVariantData;
begin
p := _Safe(_Json('{"EnableCORS":false}'));
ShowMessage(Utf8ToString(FormatUtf8('%: %', [p.Names[0], p.Value[0]])));With best regards
Thomas
I know what you're feeling, I had the same problem on this forum and couldn't find the solution that stopped my progress. Everyone suggest me the holy documentation that I don't understand and it does not show the real solutions. I hate documentation, a lot of simple samples is enough in many other projects such as DevExpress, DMVC and many many more. Does someone read DevExpress documentation to start making cool apps ?? No visual components, no real support, very slow responses, hard developing for every stupid projects, no simple swagger integration.
When I look at your profile, you've only asked questions here, never really helped others, and yet you've always been answered. When I read your comment, I think: Find the mistake!
I have a Tdocvariant in a variant variable and I want to assign a part of it ("var1") to an other pascal variable (var1) which is a record with the same structure.
Do you mean this?
type
TArrItem = packed record
a, b: RawUtf8;
end;
TDataItem = packed record
v1: array of TArrItem;
v2: RawUtf8;
end;
TDataItemList = array of TDataItem;
const
JSON_DATA: RawJson = '{"var1":[{"v1":[{"a":"1","b":"2"},{"a":"3","b":"4"}],"v2":"something"}]}';
var
doc: TDocVariantData;
list: TDataItemList;
begin
doc.InitJson(JSON_DATA, mFastFloat);
if DynArrayLoadJson(list, Pointer(doc.A['var1'].ToJson), TypeInfo(TDataItemList)) <> Nil then
ShowMessage(Utf8ToString(FormatUtf8('% - %', [list[0].v1[0].a, list[0].v2])));Arnaud does it in a similar way in a few places.
With best regards
Thomas
A little empathy is lacking in wanting to help others.
If you want help, you should allow it. A good start would be to answer my first question. The following start, for example, would be helpful:
Server: Delphi 11.3, mORMot2 GitHub commit 5186
My web application is a PWA. I would like to implement the authentication with JavaScript.
If someone has something suitable, they will help you. It has worked for thousands of others.
If no one has an answer, then follow my second recommendation. Look at the respective implementation in the units mormot.rest.server and mormot.rest.client. In the unit mormot.rest.client you will find the class TRestClientAuthenticationDefault. Set a breakpoint in the relevant functions (ClientComputeSessionKey etc.) and follow the further steps.
With best regards
Thomas
Delphi 11.3, mORMot2 GitHub commit 5186
Unit mormot.net.sock.pas, line 1891
[dcc32 Fehler] mormot.net.sock.pas(1891): E2004 Bezeichner redeklariert: "ip4"
[dcc32 Fehler] mormot.net.sock.pas(1891): E2004 Bezeichner redeklariert: "port"
The following prevents it:
function TNetAddr.SetIP4Port(pmIP4: cardinal; pmPort: TNetPort): TNetResult;With best regards
Thomas
Until some time ago, the following could be written:
CreateWithOwnModel([TAuthGroup, TFileAuthUser], {HandleUserAuthentication=} False, pmcRootName);
// Register authentication methods selectively
AuthenticationRegister(TRestServerAuthenticationDefault);Now the following must not be forgotten to be added:
fPublishedMethodAuthIndex := ServiceMethodRegister('auth', Auth, {bypassauth=}true, [mGET]);Couldn't the AuthenticationRegister function be extended and do it at the same time?
AuthenticationRegister(aMethod: TRestServerAuthenticationClass; pmRegisterAuthMethod: Boolean = True): TRestServerAuthentication;With best regards
Thomas
I'm asking for help because I've tried everything exactly as stated in the manual, in the doc on the forums... And I can't find the problem
First of all, you didn't write which programming language you are working with.
It could be that the source code for JS authentication you find in the forum is no longer up to date. In any case, you have to read the documentation. If you don't understand the process from the description, write a simple Delphi example for server and client, or take an existing example and follow the process in the debugger. It is helpful at the beginning if you first switch off the timestamp check on the server. You do this like this:
(AuthenticationRegister(TRestServerAuthenticationDefault) as TRestServerAuthenticationSignedUri).NoTimestampCoherencyCheck := True;With best regards
Thomas
2) This model create 2 tables Painel and Projeto but fill just Projeto and create a string field into Projeto table to store all TPanel as json. How I can store projeto and painel in diferent tables, but yet so make the same RETRIEVE above?
Look closely at my source code from Post #4 and extend your service function with the one shown there for loading TPaineis here. You will get the desired result only with a DTO. A direct generation from the ORM is not possible with your specifications.
With best regards
Thomas
What type of TOrmPhoneObjArray? and How I can get a person id=? and the orm bring the person with all phones?
type TOrmPhoneObjArray = array of TOrmPhone; Class TObjectWithID has the property IDValue. Just try my source code.
With best regards
Thomas
1) Will be dificult mount reports. because by default FastReport and others reports generators work with Datasets, then we need get the records master detail in tables.
FastReport can work with many sources, DataSets are just one of them. Read this article. This type of connection is much more flexible.
3) How to find something into objects that are store in this field blob?
It does not have to be stored in a blob field. It can also be stored as JSON. SQLite, for example, can execute queries directly on these fields. Read this article for more information.
How u treat this? Store all objects as documents (nosql style)?
We don't know your requirements. mORMot can be operated in many ways and is not limited to one. The help is extensive, many questions have already been answered in the forum and there are articles to be found on the internet. The best is to create a small example for each possibility and see if it fits for your own application.
With best regards
Thomas
But I can't use a json string , such as :"{"name":"mark","data":[1,2,3,4,5],"some":{"11.1":"Hello","22.2":",","44.4":"World"}}".
const
_JSON = '{"name":"mark","data":[1,2,3,4,5],"some":{"11.1":"Hello","22.2":",","44.4":"World"}}';
var
doc: TDocVariantData;
begin
if doc.InitJson(_JSON) then
begin
ShowMessage(doc.S['name']);
ShowMessage(doc.O['some'].S['11.1']);
ShowMessage(VarToStr(doc.A['data'].Value[1]));
end;With best regards
Thomas
I don't really understand your description. The problem is that there are several ways to solve it. If it is necessary for you to have the phone numbers in a separate table, you can use one of the following options (there are even more possibilities):
type
TOrmPersonID = type TID;
TOrmPhone = class(TOrm)
private
FPersonID: TOrmPersonID;
published
property PersonID: TOrmPersonID
read FPersonID write FPersonID;
TOrmPerson = class(TOrm)
public
property Phones: TOrmPhoneObjArray
procedure TOrmPerson.GetAllPhones(out pmoList: TOrmPhoneObjArray);
const
_SQL_WHERE = '(PersonID=?)';
begin
RestServer.Server.RetrieveListObjArray(pmoList, TOrmPhone, _SQL_WHERE, [IDValue]);
end;Or another option:
type
TOrmPerson = class(TOrm)
private
FPhones: TIDDynArray;
public
function AddPhone(const pmPhoneID: TID): Integer;
procedure DeletePhone(const pmPhoneID: TID);
published
property Phones: TIDDynArray
read FPhones;
function TOrmPerson.AddPhone(const pmPhoneID: TID): Integer;
begin
Result := AddInt64Once(TInt64DynArray(FPhones), pmPhoneID);
end;
procedure TOrmPerson.DeletePhone(const pmPhoneID: TID);
var
idx: Integer;
begin
idx := Int64ScanIndex(Pointer(FPhones), Length(FPhones), pmPhoneID);
if idx >= 0 then
DeleteInt64(TInt64DynArray(FPhones), idx);
end;It is easier if you save the phone numbers in the people table as a list (TObjectList<TPhone>), without creating a Phone table. If you have been working with databases for a while, you have to get used to this technique.
With best regards
Thomas
If you want to look at the JSON functionality mentioned by ttomas, you can find the example ORM-DocVariant here.
With best regards
Thomas