Skip to main content

Managing Data

In n2n's object-oriented approach we manage data using business objects. In this chapter we introduce you to working with business objects!

Creating Business Objects​

Entities are objects that serve the management of data. Business objects are entities that, besides the data, can also contain logic for data processing. In our blog module we will work with three business objects: BlogArticle, BlogCategory, BlogComment. Please note that it is not about creating a great blog module, but about demonstrating as much of n2n's functionality as possible with little code.

For our business objects we create a separate directory in our module: /app/qs/bo

Business objects consist of their properties and the corresponding getter and setter methods. The only thing that is a bit more complicated to understand are the annotations of the relationships between the business objects.

business-objects.zip​

Download the business objects and copy them into the directory of the business objects

Properties and Relationships​

First take a closer look at the imported business objects.

BlogArticle​

A BlogArticle can have several comments (OneToMany relationship) and several categories (ManyToMany) assigned. These relationships are annotated in the business object:

// annotation of the relationships:
private static function _annos(AnnoInit $ai) {
$ai->p('comments', new AnnoOneToMany(BlogComment::getClass(), 'blogArticle', CascadeType::ALL));
$ai->p('comments', new AnnoOrderBy(array('id' => 'DESC')));
$ai->p('categories', new AnnoManyToMany(BlogCategory::getClass(), null,
CascadeType::PERSIST|CascadeType::MERGE));
}

The associated comments and categories are available as properties ($comments, $categories). For relationships the properties that contain the associated objects are annotated.

For the comments (BlogComments) a OneToMany relationship is annotated, because several comments can fall upon one BlogArticle. The following parameters are passed to AnnoOneToMany:

  • The class of the associated object
  • The property of the associated object where the article is stored
  • The CascadeType determines which operations (PERSIST, MERGE, REMOVE, DETACH) are passed on to the associated objects. In our case all operations (CascadeType::ALL) are passed on. This means that when the blog article is deleted (REMOVE), the operation is passed on to the associated comments and they are deleted as well!

The second annotation of comments determines that the comments, when they are called via getComments(), are always sorted by id DESC.

The third annotation determines the relationship to BlogCategory. We define a Many-To-Many relationship. Here we leave the second parameter (mappedBy) empty and only pass on PERSIST and MERGE for the CascadeType. Because REMOVE is missing, the associated categories are not deleted when an article is deleted. That makes sense, because other blog articles could still be associated with these categories!

BlogCategory​

On BlogCategory too, the annotation of the relationship to BlogArticle stands out:

private static function _annos(AnnoInit $ai) {
$ai->p('articles', new AnnoManyToMany(BlogArticle::getClass(), 'categories',
CascadeType::PERSIST|CascadeType::MERGE));
}

Here we now see the other end of the Many-To-Many relationship between article and categories. In contrast to the annotation on BlogArticle, the second parameter of AnnoManyToMany is not NULL here. On one side of a Many-To-Many relationship we have to specify a mappedBy property.

BlogComment​

On BlogComment we now see a Many-To-One relationship. It is the other end of the One-To-Many relationship on BlogArticle:

private static function _annos(AnnoInit $ai) {
$ai->p('blogArticle', new AnnoManyToOne(BlogArticle::getClass(), CascadeType::MERGE|CascadeType::PERSIST));
$ai->p('image', new AnnoManagedFile());
}

The annotation on the property $image indicates that the property is a file. AnnoManagedFile() ensures that a FileManager takes care of storing the uploaded file.

That the property $image is something special can also be seen in the setter method:

public function setImage(File $image = null) {
$this->image = $image;
}

Here it is determined that the passed value must be a File. To also allow NULL values, the = null is important.

Another peculiarity we find in the constructor:

public function __construct() {
$this->setCreated(new \DateTime());
}

With this instruction you set the default value of the property created to the current date and the corresponding time. Every comment that is created gets the current date as its created value this way.

Next we want to take a closer look at the annotations of the relationships.

Annotations of Relationships​

n2n knows the following classes to annotate relationships between business objects:

  • AnnoOneToMany (1:n relationship)
  • AnnoManyToOne (n:1 relationship)
  • AnnoManyToMany (n:n relationship)
  • AnnoOneToOne (1:1 relationship)

The first three classes are explained in more detail below.

1:n Relationship​

This annotation is located on the class BlogArticle. A BlogArticle can have several BlogComments. The class AnnoOneToMany knows the following important parameters in the constructor:

PropertyExplanationValue / example
$targetEntityThe class of the object to which a relationship exists.BlogComment::getClass()
$mappedByThe property on which the other object stores the relationship.'blogArticle'
BlogComment has the property $blogArticle, where the relationship is stored.
$cascadeTypeHere it is defined which operations are passed on.CascadeType::ALL
With all database manipulations these are transferred to the attached objects. Attention: this includes deletion!
$fetchTypeHere it is determined when the associated objects should be loaded. Immediately when loading the object or only on access.FetchType::EAGER, loads all associated objects immediately
FetchType::LAZY, loads the objects only when they are needed.
$orphanRemovalDetermines whether objects that are no longer linked should be deleted.true or false

CascadeType​

We want to take a closer look at $cascadeType. The CascadeType determines which operations are passed on to the associated objects. There are the following important types:

  • CascadeType::PERSIST (create)
  • CascadeType::MERGE
  • CascadeType::REMOVE (delete)
  • CascadeType::ALL

Here we look at PERSIST and REMOVE more closely. When an object is persisted, with CascadeType::PERSIST the associated objects are persisted as well. With REMOVE an object is deleted. CascadeType::REMOVE accordingly means that all associated objects are deleted as well! With CascadeType::ALL all operations are passed on.

Attention

If a business object is mistakenly given CascadeType::REMOVE or CascadeType::ALL, associated objects are deleted as well. This can lead to data loss!

n:1 Relationship​

This annotation is located on BlogComment. A BlogComment always belongs to one BlogArticle. The class AnnoManyToOne has the following important constructor parameters:

PropertyExplanationValue / example
$targetEntityThe class of the object to which a relationship exists.BlogArticle::getClass()
$cascadeTypeDetermines which operations are passed on to the associated objects.`CascadeType::MERGE
$fetchTypeHere it is determined when the associated objects should be loaded.

In contrast to the 1:n relationship there is no $orphanRemoval here.

n:n Relationship​

The last relationship we look at more closely is the n:n relationship. We find it on BlogArticle and BlogCategory. A BlogArticle can be assigned to several BlogCategory and vice versa. The annotation AnnoManyToMany has the following important constructor parameters:

PropertyExplanationValue / example
$targetEntityThe class of the object to which a relationship exists.BlogArticle::getClass()
$mappedByThe mappedBy value only has to be specified on one side of the relationship. This way the system decides which class is master and which is slave.null or the property on the other object
$cascadeTypeDetermines which operations are passed on to the associated objects.`CascadeType::MERGE
$fetchTypeHere it is determined when the associated objects should be loaded.
$orphanRemovalDetermines whether objects that are no longer linked should be deleted.

SQL Dump​

With this the business objects we need in the course of this Quickstart are defined and their relationships explained. For storing the business objects the corresponding tables in the database are still needed. n2n uses a predefined naming strategy. It derives the corresponding table and column names from the names of classes and properties. Example: BlogArticle objects are stored in the table blog_article.

Attention

Via annotations table (AnnoTable) and column names (AnnoColumn) can be chosen individually. n2n even allows the definition of your own naming strategy.

Database tables can also be created via the Hangar. In the Quickstart we create the tables with the following SQL file.

quickstart.sql​

This SQL file contains the required CREATE TABLE and INSERT statements for the business objects shown above.

Registering Entities​

All entities must be registered in n2n. To do so, add the following lines to var/etc/qs/app.ini:

[orm]
entities[] = "qs\bo\BlogArticle"
entities[] = "qs\bo\BlogCategory"
entities[] = "qs\bo\BlogComment"

From now on n2n knows our business objects. Now we are ready to access our data!