Basic Single-record Operations

A bread and butter of ORMs is ability to load individual records, work with them and then save them back into persistence. Although a logical Record seem to map into a Record inside your database, the job of DALs is to enable various transformations.

Here is example of RedBeanPHP code:

$book = R::dispense( 'book' ); $book->rating = 10; $id = R::store( $book );

Majority of the business logic code will happen with the $book record. How far can $book structure distance itself from the database structure? If I rename field “rating” into “book_rating” in SQL will my business logic code have to change too? What if I want to move “book_rating” into a different table? Can this all be done transparently?

RedBeanPHP by design does not support any advonced mapping at all, it is just syntactic sugar around your SQL table. A more powerful DAL will allow you to define various ways to re-map things, which is very important for safe database refactoring in larger projects.

Table Name Mapping is ability to link Object/Class representing your Entity with physical table or collection inside a database. With this, you can change name of your table in the database without affecting your application code. Similar type of mapping is needed at a field level. If you rename firstname to first_name inside your database, how many lines of code changes in your PHP app? Hopefully just one.

Since we talk about mapping, sometimes a physical field of SQL can be substituted by expression or a sub-query. For example, your “BasketItem” may have “cost” changed between expression “(qty*price)” and a physical field. Will your DAL be able to facilitate such a change transparently for the rest of the application? What about sub-query? Your “Basket” will have field “total” which may either be expressed as sum(cost) or be a physical field. Will decision to change how “total” calculates in your database affect your application code in more than a single place?

Type mapping allows user of a DAL library not to be concerned about how to handle DateTime format. In PHP we use DateTime for storing Date, Time and Timestamps which must be mapped into native time storage transparently by DAL, while handling timezones, daylight saving time and more:

$basket->created = \DateTime();

Another data type that DAL must handle is “money” or “currency”. In many cases applications will want to describe their own formats with a specific routine for storing and loading values of those user-defined types. Actually there are two ways to implement this. This can be either implemented within a Entity itself (e.g. setCreated(..)) in which case the implementation is not re-usable across multiple Models. The other, more preferred approach is by using mutator or typecasting, which enable global use of our new type.

When we load and store data, sometimes we would want a certain field/property to be stored inside a different table (or collection). Mapping field to related Entity is not same as Mapping field to related table, because Entity is defined at “Domain Model” while table is a persistence detail. Next example defines field country as a mapping to “country_id->name”. The class Country() can further map the “name” field into something else. Here is how Agile Data imports fields on Domain Model level:

$user->hasOne('country_id', new Country())

->addField('country', 'name');

It’s more commonly for DALs to map fields into a physical table/field or only support “calculated” fields in PHP (which qualify only as a Partial support).

Entity’s field can also be mapped into a sub-query of related entity. Again, this is different to mapping to a physical resource, because technically aggregate functions and aggregate field can abstract complexity:

$basket->hasMany('Items', new BasketItem())

->addField('total', ['aggregate'=>'sum', 'field'=>'cost']);

A single Model/Entity can contain fields from multiple physical tables (or collections) if DAL can define entity to join multiple tables. This is also called “associations”. The important requirement with this type of mapping is to be able to support basic operations, so if you add a new record, DAL must automatically distribute it into 2, 3 or more physical tables and link them up together as per your database logic.