The Model
Structuring the business logic
Table of Contents
What's the model?
The model is the piece within your application containing your business logic. Because is a good practice to separate your business logic from the rest of your application clearly, lion implements a Model-View-Controller pattern to ease the application structuration, where the Model become one of the 3 pieces within this pattern.
We can find as part of the model layer the following classes:
- Your business classes: A set of value holder classes representing your business artifacts.
- Your DAOs: Classes in charge of loading/saving your business objects from and to a storage engine (i.e. a database)
- Your model services: Classes with the services that will be public from outside the model, representing the front-end to access to your model.
- An ORM: A set of classes in charge of making transparent how business objects are persisted (i.e. making transparent the SQL usage by offering an API)
- A database abstraction layer: A set of classes in charge of making transparent the concrete storage engine (i.e. the concrete database engine)
However, the model is not limited to those classes nor responsibilities, but to all the logic with a direct relationship to your business logic
Defining model services
Lion proposes a way to separate the model from the rest of the application: by defining a set of services working as a gateway to the model.
Those services are what we have known as model services
Model services are defined within the configuration, by specifying where to route each service call to a real model class.
i.e. We could define a set of model services and redirect them to a concrete Dao:
<?xml version = "1.0" standalone="yes"?>
<configuration>
<model-services>
<!-- Invoice services -->
<class name="InvoiceDao">
<service name = "getAllInvoices" class-method = "loadAll"/>
<service name = "getInvoice" class-method = "load"/>
<service name = "saveInvoice" class-method = "save"/>
</class>
</model-services>
</configuration>
In this example, we are declaring some model services (getAllInvoices, getInvoice and saveInvoice) that will be routed to some InvoiceDao methods, to be concrete to loadAll, load and save.
Continuing with our example, the Dao that we are talking about could be more like the following one:
<?php
class InvoiceDao {
public function loadAll() {
...
}
public function load($invoice_id) {
...
}
public function save(Invoice $invoice) {
...
}
}
To call to a model service from outside the model layer, we'll use the __ModelProxy instance (singleton).
i.e.:
<?php
//retrieve all the invoices:
//retrieve a concrete invoice:
//save an invoice:
The first advantage of this approach is the fact that we are hiding how the model is structured internally by just exposing some services to be consumed from the rest of layers.
In our example, we are calling to getAllInvoices by ignoring that our call will be routed to a DAO:
Internally, the model proxy creates an InvoiceDao instance to call to his method. It also pass all the received parameters as them to return the received result.
Lion also allows to route service calls to context instance methods. To do that, we can use the instance tag instead of the class:
<?xml version = "1.0" standalone="yes"?>
<configuration>
<model-services>
<!-- Invoice services -->
<instance id="invoiceDao">
<service name = "getAllInvoices" class-method = "loadAll"/>
<service name = "getInvoice" class-method = "load"/>
<service name = "saveInvoice" class-method = "save"/>
</instance>
</model-services>
</configuration>
This is more like it could be done in a real project: Our DAOs have been initialized by dependency injection, so it's expected that they have been initialized with all their dependencies, i.e. the instance representing the database connection or the transaction manager to use:
<?xml version = "1.0" standalone="yes"?>
<configuration>
<context-instances>
<context-instance id="invoiceDao" class="InvoiceDao">
<property name="connection"><ref id="localDbConnection"/></property>
</context-instance>
<context-instance id="localDbConnection" class="DbConnection">
<property name="dbName"><value>myDb</value></property>
<property name="dbEngine"><value>mysql</value></property>
<property name="dbHost"><value>localhost</value></property>
<property name="dbUser"><value>myUser</value></property>
<property name="dbPassword"><value>secret</value></property>
</context-instance>
</context-instances>
</configuration>
Caching capabilities
Another advantage of using model services approach is the caching capability.
We can declare caching properties associated to model by using the cache and cache-ttl attributes at service level.
i.e.
<?xml version = "1.0" standalone="yes"?>
<configuration>
<model-services>
<!-- Invoice services -->
<class name="InvoiceDao">
<service name = "getAllInvoices"
class-method = "loadAll"
cache = "true"
cache-ttl = "300"/>
<service name = "getInvoice"
class-method = "load"
cache = "true"
cache-ttl = "60"/>
<service name = "saveInvoice" class-method = "save"/>
</class>
</model-services>
</configuration>
In this example, we are caching the getAllInvoices service to expire after 300 seconds (ttl), as well as the getInvoices with a ttl of 1 minute
The cache-ttlis optional, being 0 by default, forcing the cache to never expire
Protecting services
We can also protect some of our model services by assigning permissions.
i.e. we could force the security layer to check that the user in session has a concrete permission to be able to call the saveInvoice service:
<?xml version = "1.0" standalone="yes"?>
<configuration>
<model-services>
<!-- Invoice services -->
<instance id="invoiceDao">
<service name = "getAllInvoices" class-method = "loadAll"/>
<service name = "getInvoice" class-method = "load"/>
<!-- Need the SAVE_INVOICES_PERMISSION to call to this service: -->
<service name = "saveInvoice" class-method = "save">
<permission id="SAVE_INVOICES_PERMISSION"/>
</service>
</instance>
</model-services>
</configuration>
Persistence layer
There are more like hundred of elegant approaches to equip our model with persistence capabilities. However, lion does not implement any of them :)
Why? The answer is clear: Because there are more like hundred of frameworks to do that out there.
What Lion provides is an easy way to integrate them as part of the model layer.
See the Integrating an ORM section to learn how to integrate an ORM, which is a good example to know about those integration capabilities