The previous chapter introduced you to modeling. With your knowledge you can now setup a fully functional online database. Its generic user interface is driven by the schema. If you want to tailor the user interface and work flows to your requirements, you need to start coding. These two approaches are most powerful when combined. It is easy to integrate the base components with simple links during programming.
Topincs application development uses PHP. A basic understanding of PHP and in particular object-oriented programming will be helpful from now on. In many cases the coding artifacts are no longer than 10 lines. You will look for complicated class hierarchies in vain. We aim to keep the code simple and independent, yet we value eternal programmer wisdom highly: don't repeat yourself!
The fundamental programming concepts in Topincs are:
API reference
Hint: Both works: get_child_age
or getChildAge
!
Hint: Use the strict equals comparison operator === or equals
if you want to know if two tobjects represent the same topic!
You gain access to data in the store by retrieving a tobject. There is a 1-1 correspondence between a tobject and a topic. A tobject is a PHP object with an interface determined by modeled constraints of the type of the topic. By calling methods on the tobject you retrieve or manipulate the names, occurrences, and associations of the topic. The interface also performs automatic conversion, e.g. from native PHP objects like DateTime
to the correct value representation and back. The following example illustrates the basic usage of a tobject.
// Maria's wish has the system id 1351.
$wish = Tobject::get("id:1351");
echo $wish->id;
// 1351
echo $wish;
// 2021, Switzerland, Maria, Ball
// If not in string context, use the label method.
$label = $wish->label();
// The default link uses the label as the link text.
echo $wish->a();
echo $wish->get_child_name();
// Maria
echo $wish->get_child_age();
// 8
echo $wish->get_gift();
// Ball
echo $wish->get_year();
// 2021
echo $wish->get_country();
// Switzerland
During one PHP runtime – instantiated either by a service request or a job execution – a topic that is retrieved multiple times will always result in the same tobject. This behavior can be used to aggregate additional information on the tobject by storing computational results or frequently used data. This simple behavior of tobjects becomes very useful in any sort of complex computation since it reduces the need to revert to secondary data structures thus reducing the overall complexity of source code.
Hint: If you need to ensure a certain property structure on a tobject in more than one service, you can declare an init
method in a domain class.
// Compute the average age of the children for a given year.
$year = Tobject::get($some_year_id);
$year->age_sum = 0;
$year->wish_count = 0;
foreach ($year->get_all_wishs() as $wish) {
$wish->age_sum += $wish->get_child_age();
$wish->wish_count + 1;
}
echo $wish->age_sum / $wish->wish_count;
// By id:
$year = Tobject::get(1234);
// By a topic reference
$year = Tobject::get("id:1234");
$year = Tobject::get("si:year/2020");
$year = Tobject::get("sl:someurl");
// Use fetch to create if does not exist
$year = Tobject::fetch("si:year/2020");
Hint: Keeping serialization names constant is a good idea, but a broken interface can be repaired by using a domain class!
The interface of a topic depends on its topic type and is driven by constraints and serialization names. This sounds more complicated than it is. Basically it is just a formality, since Topincs already suggests serialization names. We need to confirm them on the Programming interface page which you can access through the admin menu. If the suggested name is not to your liking, simply choose a different one. In the following video you will see how the interface of the Santa Claus system was setup.
Check it out
The following table illustrates the relationship between serialization names and method names.
Serialization name | Item type | Method name examples |
---|---|---|
child-name | Name type | has_child_name get_child_name |
child-age | Occurrence type | get_child_age set_child_age |
country | Role type | get_all_countrys delete_all_countrys |
wish | Topic type | make_Wish isa_Wish |
The interface allows access and manipulation of statements about the topic that a tobject represents. There is these method families: has
, count
, get
, set
, add
, delete
, and txt
.
Hint: The individual statements are independent from each other.
// has
if ($year->has_wish()) {
...
}
// count
echo $year->count_wishs();
// get
$age = $wish->get_child_age();
foreach ($year->get_all_wishs() as $wish) {
...
}
// add
$year->add_wish($wish);
// Christmas is postponed #1:
$next_year->add_all_wishs($this_year->get_all_wishs());
// delete
$wish->delete_child_age(); // delete one
$year->delete_all_wishs();
// set (delete all and then add)
$wish->set_child_name("Maria");
// Christmans is postponed #2:
// Existing wishes on next year are first deleted.
$next_year->set_all_wishs($this_year->get_all_wishs());
// txt (similar to get, but respects language preference in a multilingual store)
echo $gift->txt_description();
// Get the topic type of the tobject
if ($something->type() === "wish") {
...
}
// Creating a wish
$wish = Tobject::make_Wish()
->set_child_name("Henry")
->set_child_age(9)
->set_gift($bike_id)
->set_year("si:year/2021")
->set_country($france_id);
// Deleting a single topic
$wish->delete();
// Deleting many topics
$year->get_all_wishs()->delete();
// This deletes only the associations
$year->delete_all_wishs();
// Iterate over all years
foreach (Tobject::all_years() as $year) {
...
}
// If you need them in an array
$years = Tobject::all_years()->to_array();
Hint: Think of services as a high level functions! Think of topic types as high level types!
A Topincs service satisfies a domain specific need. They are little programs with a name, input and output. They are the smallest unit of computation in Topincs and similar to a function. When called the passed arguments are processed and in most cases output is produced, which can be as simple as yes/no or as complex PDF document of 100 pages. Services are called by HTTP requests, from other services in PHP on the server or from JavaScript in the browser.
GET
or POST
.When you provide a read-only view of data in the store, you should use the GET
verb. When the service modifies data in the store or sends out an email, use POST
.
A service is created through the admin menu. Visit the index of Services and click Define. You need to specify a formal name, a label, a service type (GET or POST), and a format (HTML, JSON, PDF, etc.). Like everything else in Topincs, services are just topics. After creating the service the source code can be edited by clicking on Source code in the menu on the topic page. Initially there is a number of files initially present, but you can easily create a new one to outsource common code by simply declaring it.
One mandatory PHP file comes with every service, it is called GET.php
or POST.php
depending on the service type. This is the computational component of the service. In GET services it usually queries and aggregates information from the database. In POST services it manipulates data or sends out an email. A second PHP file is present, if you specified a format for the service. In most cases it will be HTML. We recommend that this stays largely free from sophisticated computation and simply performs the output. It is called the presentational component. It is optional, e.g. if the POST service simply sends out a HTTP location header after it is done. There can be more than one presentational component, e.g. when you output a table in CSV or XLSX format.
Hint: By default, arguments are accessed parsed. In rare case you might need them unparsed. Use get_unparsed
.
API reference
Service parameters should be added during the service creation, but may also be added later. There is two kinds of parameters: topic parameters and value parameters. For the first ones you need to specify one or more topic types of which the instances can be used as arguments. They will be rendered as a select box in the argument query form. For the latter you need to specify a datatype.
On a service call the paramters are bound to argument values. Topic parameters will be tobjects. Value parameters will be converted to the corresponding PHP type. The service can access parameters via their formal name through the variable $p
which is an instance of ServiceParameters
. If you add a parameter later to a service, you need to manually adjust the service code to actually assign it to a variable in your computational component.
Santa Claus wants to be on top of things, so he needs an overview over all wishes of the current year. We will implement a service with one optional value argument. A positive integer holding a year. The computational component will try to fetch the year topic and then iterate over all wishes, group them by country and display selected properties in one table per country.
// Receiving the a single argument for a parameter:
$year = $p->get("year");
// Specifying a default value:
$year = $p->get("year", (new DateTime())->format("Y"));
// Receiving many in an array:
$wishes = $p->get_all("wish");
// Store URL:
https://www.topincs.com/santaclaus/
// URL for the service with the formal name 'overview':
https://www.topincs.com/santaclaus/overview
// Service URL with the argument 'year' bound:
https://www.topincs.com/santaclaus/overview?year=2021
// If mandatory arguments are bound, the service is called.
// Otherwise the service form shows.
// To force the form append an ampersand:
https://www.topincs.com/santaclaus/overview?year=2021&
// *= makes a parameter immutable in the service form:
https://www.topincs.com/santaclaus/overview?year*=2021&
Check it out
Hint: Is it easy to call a service from JavaScript or PHP.
Services can be accessed unparameterized from the default main menu. An auto-generated form will be presented to gather the service parameters, e.g. which year to display. This service form is very similar to the base component form, but the data it collects are just passed to the service and not persisted unless the service code does so. Services complement the base components for application development.
There is numerous things you can do with a service. Many items on the following list will become clearer, once you have read through the next chapter User interface.
Hint: It is good programming practice to have only one class per file!
A domain class extends a tobject with arbitrary methods. It can be registered for one or more topic types. The tobject will have methods of all domain classes that are registered (loaded) at call time. It is convenient to think of a domain class as being assigned to a topic type, but this correspondence is not essential to a domain class. A domain class is rather a mixin which has certain implicit expectations towards the tobject. It may hold instance methods or static methods.
A domain class is created on the page of the topic type. Click Create domain class, provide a name, click Ok and you can edit the source code of the class. You can use it in service by simply requiring it.
Santa Claus wants the children to receive an email confirmation, so that they can be sure their wish was heard. Since we want to do that in various places in the application we create a domain class.
A trigger is a piece of PHP code that is executed when certain events in the web database happen. Examples for events are:
A trigger is created in the programming section on the schema page of the store. First specify on which events the trigger fires. There is three classes of events:
After the trigger is created, click on Source code to program the trigger. The parameters passed to the trigger code are explained in the source code file. It is mainly the affected topic. Below you find a detailed table for item events.
Item type | Parameter | Bound to |
---|---|---|
Name | $topic_id | the id of the parent topic |
Occurrence | $topic_id | the id of the parent topic |
Role | $topic_id | the id of the player |
$cp_id | the id of the counter player | |
Topic | $topic_id | the id of the topic itself |
When modifying a topic in the form or with the API, names, occurrences, and associations are changed in multiple ways. We illustrate which combination of changes causes which type of event with a simple example using an occurrence type sending date. Given a topic type Invoice with multiple optional sending dates:
Check it out
Warning: In the meantime there is a general fetch method on Tobject for the task in this video. See below.
API reference
<?php
require_once("domain/Year.php");
$wish = Tobject::get($topic_id);
$today = new DateTime();
$year = $today->format("Y");
$wish->set_year(Year::fetch($year));
Hint: The main purpose of a template is output and not computation. Thus keep it simple!
Any Topincs service consists of a computational and a representational component which creates in many cases HTML that is sent to the browser to be rendered. The markup is held in GET.html or POST.html. But HTML plays also an important role in Topincs for PDF and email generation. In these cases templates come in handy. Unlike a PHP source code file, which defines functions, classes, or performs an elaborate computation, a template is mainly a text with blanks in it. The blanks will be replaced with computed values upon evaluation of the template. Templates have no format restriction: you can create HTML, Text, SVG, XML, etc.
API reference
API reference
You have the choice between PHP and Twig templates. They only differ in syntax. There is two functions built into Topincs to evaluate a template: php and twig. Both share the same signature: pass it the template filename as a string and the context as an associative array, it returns a string holding the completed template. You create the template file by simply writing down the call to php or twig and reloading the code editor.
Santa wants to send an email confirmation after the wish has been received. He hopes that this will reduce the number of hotlline calls. The elves in the call center have been complaining about the unnatural work load during peak hours.
Check it out
<?php
// PHP
$html = php("mytemplate.html.php", ["name" => "Rudolph"]);
// Twig
$html = twig("mytemplate.html.twig", ["name" => "Vixen"]);
// The filename of the template must adhere to the shown format.
<h1>Hello, <?= $name ?></h1>
<p>Congratulations! You are employee of the month.</p>
External references
<h1>Hello, {{ name }}</h1>
<p>Congratulations! You are employee of the month.</p>
Programming in Topincs empowers you to achieve more with less effort. A programming interface comes alive in a matter of seconds by assigning serialization names. The smallest unit of application development is a service. Domain classes are a flexible tool to tie computational behavior to one or more topic types. They make it possible to change the underlying data in the topic map and still keep dependent code functional by using a domain class as an adapter and importing it in the scripts that rely on the old data schema. These properties make it possible to quickly react to changes in the requirements.
This page cannot be displayed in your browser. Use Firefox, Opera, Safari, or Chrome instead.
Saving …