Paradox Community
Search:

 Welcome |  What is Paradox |  Paradox Folk |  Paradox Solutions |
 Interactive Paradox |  Paradox Programming |  Internet/Intranet Development |
 Support Options |  Classified Ads |  Wish List |  Submissions 


Paradox OCX Internet/Intranet Articles  |  Non-OCX Internet/Intranet Articles  


Paradox Web Server
Lesson 4 - More Detail
© 2001 Tony McGuire

Lesson 4 - More Detail

A breakout of the various elements of a Web site run by Paradox, using the Examples that came with Paradox as a starting point:

1. You need a Paradox form with the OCX "installed".

2. You need a Paradox table that matches the incoming request's "action" statement (function) with a library, and the method that will be executed by Paradox to fill the request.

This table's 4 fields are
REQTYPE (P or G, for POST or GET)
URI (Action element from the Internet from, preceeded by a slash "/")
LIBRARY (The library Paradox should go to and pass the HANDLER to that library's handlerequest() method)
HANDLER (The actual method you are using to process this URI. This can match the URI (without slash), or be anything else you wish; it just has to match up to one of the case... statements with the library)

3. You need a Paradox library that contains a handlerequest() method. In the handlerequest(), you will have a switch...endswitch construct that matches the name passed by the OCX to a method in the library. The name passed by the OCX will be the entry in the table's "HANDLER" field (#2, above). You can match the case statement to the internal library method or change it; personal choice. Only real reason I found not to match these was if you wanted to add a little confusion to anyone trying to figure out your coding practice and program flow.

4. If you want to use "templates", as in the example, you will need a table of templates. These templates are HTML code you will use multiple times throughout your website(s). If you use the fetchtemplate() method included in the examples, this template file will contain 2 fields

TEMPLATE. A primary key field whose value you will pass to fetchtemplate() (as in fetchtemplate("GETHEADER")). fetchtemplate() will return the code in the second field....

HTML. This field is a memo field that contains actual HTML that you want to make "reusable".

Because you are using Paradox as your Web Server, references to tables within your library can use aliases. This is very handy, since a web visitor can only "see" files in the "root" directory and below. Using aliases, you can place the data you are searching (including the above tables/libraries), anywhere, as long as Paradox has access to it. Thus, you could actually search against data on a completely different machine than the one Paradox and the Websrv.ocx are executed on (An alias would take care of it, just as on a LAN).

I have found it very helpful to declare database variables at the library level, then "open" these databases in the libraries "open" method. Speeds things up considerably when coding; tcursor.open("tblname.db",databasevariable). If you need to change which alias a database variable is opening, you only need to change it in the library's "open" method (of course, you can also change where the alias is pointing, but I am assuming the original alias needs to be maintained - I ran into this scenario, and it caused me no end of tedious recoding). This may be standard coding practice, but I just recently learned to use this (the hard way) so thought I would share.

Opening the tcursor, you can include the database name as well as a secondary index. Since just about everything I do on the Internet makes use of secondary indexes, I incorporate this into the coding of almost every method.

The next statement after opening the tcursor is the tcursor.setrange(). In two steps, I have most of the answer I need, if not all of it.

If the setrange() incorporates all of my search parameters (let's say someone searching for a particular model of car (Corvette) offered within a certain price range ($15,000 - $18,000), the setrange() results in a tcursor containing the complete set of records that need to be returned to the user.

Assume a method for searching for a car. Define the variables to be used:
var
  tc tcursor
  cr string ; car model
  lp string ; low price
  hp string ; high price
  ly string ; earliest year built
  hy string ; latest year built
  da dynarray[] string ; dynarray for additional search criteria
  retvar string ; string variable to store line of return HTML code
endvar

cr=request.getfield("model")
lp=request.getfield("lowprice")
hp=request.getfield("highprice")
ly=request.getfield("lowyear")
hy=request.getfield("highyear")
Thus
tc.open("autosforsale.db",databasevar,"carprice")
tc.setrange(cr,lp,hp)
is the entire code that narrows down a table to those records that match the "search" criteria of car model and price range. In less than 1 second (much less!). (At least on a table containing 40,000 records this is the performance.)

Of course, this results in _all_ Corvettes in this price range being included in the tcursor.

What if the Internet visitor only wanted those Corvettes made between 1968 and 1973?

That's where setgenfilter() comes in. Setgenfilter() respects the limited view of a setrange(), using a dynamic array to pass the additional criteria to setgenfilter().

What you can do is define a dynarray[] in the method's var...endvar statement (just as you declared the tcursor, mode, and lowprice/highprice variables)

Set up the criteria for the setgenfilter() using the dynarray:
switch
  case (ly.size()>=4 and hy.size()>=4) : da["yb"]=">="+ly+", <="+hy
  case ly.size()>=4 : da["yb"]=">="+ly
  case hy.size()>=4 : da["yb"]="<="+hy
endswitch
("yb" is the field name in the table that contains the year that the car was built. This causes the da["yb"] criteria to automatically be applied against that field in the table. Yes, I realize that this is standard OPAL, but that's what I have been saying all along. I just don't want someone to get lost in all of this because I didn't include an explanation. I didn't understand setrange()/setgenfilter() until I started programming for the Internet in Sept '99, and I don't want to take for granted that I am the only person who didn't understand this concept. [Then I'd really feal dumb.])

Now, you have a criteria for the setgenfilter() built using the dynamic array. To apply it
if da.size()>=1 then
  tc.setgenfilter(da)
endif
(you can use multiple dynarray criteria elements within 1 setgenfilter command; just add additional da[""] elements before executing the setgenfilter(da).

I use the if..then..endif construct in the event year didn't matter to the person doing this search - while model and price range are required, year built isn't. (for this example)

Since setrange() narrowed things down to only Corvettes in a certain price range (instantaneously), now the setgenfilter() only needs to be applied against this much smaller data set. This results in the setgenfilter() being nearly instantaneous as well (in this example, anyway. Your data/secondary indexes will dictate how small of a data set setrange() will result in, which will dictate how many records setgenfilter() will be applied against.)

As you might imagine, table structure is absolutely critical to this model. There are no joins or calculations (calculations are possible, more on this later). And everything hinges on the secondary indexes. I restructured just about every table I use for the Internet, although there are ways around this in some cases. I started out with a separate file for "searches", with the complete records maintained in their original tables. My situation is a little unique, however, in that every record has a unique reference ID; I can do a qlocate() of the reference ID on the tables containing the full data. Not all of your situations make this possible.


Lesson 5

Discussion of this article


 Feedback |  Paradox Day |  Who Uses Paradox |  I Use Paradox |  Downloads 


 The information provided on this Web site is not in any way sponsored or endorsed by Corel Corporation.
 Paradox is a registered trademark of Corel Corporation.


 Modified: 15 May 2003
 Terms of Use / Legal Disclaimer


 Copyright © 2001- 2003 Paradox Community. All rights reserved. 
 Company and product names are trademarks or registered trademarks of their respective companies. 
 Authors hold the copyrights to their own works. Please contact the author of any article for details.