PHP beginners tutorial

In this tutorial we will show how simple is to write DSL and use code generated from it. We'll show some basic operations like creating objects, updating them and writing search requests using specifications.

Let's say that we want to build football team management utility. Let's start by defining our first simple model.

module Football
{
    root Team
    {
        string name;
    }
    root Player
    {
        string name;
        Team *team;
    }
}

This would give us two classes in PHP: Team and Player. Both of those classes would have string properties defining its name. Additionally, Player class would have Team property stating which team are they playing for. Primary key is specified in brackets after aggregate root name in model. When primary key is left out, like in this scenario, surrogate ID is created. Above DSL is identical to:

module Football
{
    root Team(ID)
    {
        int ID { sequence; }
        string name;
    }
    root Player(ID)
    {
        int ID { sequence; }
        string name;
        Team *team;
    }
}

So every object will get its surrogate primary key. Keyword 'sequence' means that server will assign its value (usually next value). Let's create two football teams using those classes.

use Football\Team;

require_once 'platform/Bootstrap.php'; // recompiles DSL

// creating team objects
$team1 = new Team();
$team1->name = 'Old School';
$team2 = new Team();
$team2->name = 'New School';

// adding teams to cloud
$team1->persist();
$team2->persist();

As we see, adding teams is done by method persist. When it is called, persist method will make a http request to main server (we could save both teams in a single request, but this is beyond this tutorial). If we want, now we can load saved teams from cloud using findAll method and print their names.

$teams = Team::findAll();
foreach($teams as $team)
    echo $team->name . "<br>";

Output should be something like this:

Old School
New School

But wait, who will play in our teams? Let's import Player class.

use Football\Player;

We will add two players in each team and persist them.

// creating player objects
$player1 = new Player(array('name' => 'Oliver Kahn', 'team' => $teams[0])); // setting properties trough constructor
$player2 = new Player(array('name' => 'Davor Suker', 'team' => $teams[0]));
$player3 = new Player(array('name' => 'Victor Valdes', 'team' => $teams[1]));
$player4 = new Player(array('name' => 'Cristiano Ronaldo', 'team' => $teams[1]));

// adding players to cloud
$player1->persist();
$player2->persist();
$player3->persist();
$player4->persist();

This way we would create Old School and New School teams. Davor Suker and Oliver Kahn would be Old School players, Victor Valdes and Cristiano Ronaldo would be New School players. Sooner or later Victor Valdes will retire and transfer to Old School team. In PHP this is done by:

$players = Player::findAll();
foreach($players as $player)
    if($player->name === 'Victor Valdes'){
        $player->team = $teams[0];
        $player->persist();
    }

Its worth mentioning that reference to another aggregate root will be loaded lazily. Here we find all players and then iterate through them changing team of Victor Valdes to Old School. There is a better way of doing the same thing. After persistence, object will get its ID and an URI from the server, so every other persist on a same object will update that object. URI represents a unique identification for this object and is equal to the primary key when there is only single column in the primary key. If primary key is multi column then URI is combination of primary key columns.

$player5 = new Player(array('name' => 'Luka Modric', 'team' => $teams[1]));
$player5->persist(); // Luka Modric playing for New School

$player5->team = $teams[0];
$player5->persist(); // Luka Modric playing for Old School

Method count on class Player would print out number of players currently registered, while invoking method delete on object deletes that objects.

echo Player::count(); // total number of players
$player5->delete(); // removes Luka Modric

Let's go back to our model. Currently Team and Player have surrogate field ID as primary key. We could change team object, so that its primary key is name, since there can't be two teams with the same name. But a team could change its name. Would it maintain its identity if it does so? In this case we see a benefit for having surrogate key, so that the team can maintain its identity even if it changes its name. Also, let's add nickname to players. It is not necessary that every player has nickname so we'll set this field to be optional.

Now our DSL would look like

module Football
{
    root Team
    {
        string name { unique; } // two teams can't have same name
    }
    root Player
    {
        string name;
        string? nickname; // question mark means this field is optional (nullable)
        Team *team;
    }
}

Now since we have unique constraint on Team name, we can only persist object with an unique name.

$team1 = new Team(array('name' => 'Old School'));
$team2 = new Team(array('name' => 'New School'));
$team3 = new Team(array('name' => 'New School'));

$team1->persist();
$team2->persist();
// $team3->persist(); // this line would result in error because team with name New School already exists

Instead of getting all players with method findAll, we can get only one object using his primary key. This is done with method find:

echo Player::find('1'); // prints player with URI = 1 if exists

This guessing of URI is impractical. Team name is set to unique, so we want to be able to get team by its name. Specification is the right thing to use in this case.

module Football
{
    root Team
    {
        string name { unique; }
        specification searchByName 't => t.name == name'
        {
            string name;
        }
    }
    root Player
    {
        string name;
        string? nickname;
        Team *team;
    }
}

Using C# lambda expressions syntax we create a predicate which evaluates to true if team has a name that we are searching for. Now we can implement change request in PHP code by using generated classes and writing something like:

$teams = Team::searchByName('Old School');

or something like:

$specification = new Team\searchByName(array('name' => 'Old School'));
$teams = $specification->search(); // gives same result as code above

which is much nicer. Using specification we could write complex search requests like: "get all players whose name starts with 'A' and that has no nickname". This could be accomplished by

specification searchComplex 'p => p.name.StartsWith("A") && p.nickname == null';

We want to have Players property on Team, but its ok that this field is read only since we want to modify player when he switches teams. This way, every team will have automatically generated array of players playing for that team. Also, let's add some other info like Team foundation date and Player number in the Team.

module Football
{
    root Team
    {
        string name { unique; }
        detail players Player.team;
        date founded;
        specification searchByName 't => t.name == name'
        {
            string name;
        }
    }
    root Player
    {
        string name;
        string? nickname;
        Team *team;
        int number;
    }
}

After inserting few teams and players we could print all players playing for Team Old School:

foreach($teams[0]->players as $player)
    echo "$player<br>";

With this we conclude first part of tutorial. You should be able to create simple objects and use them now.