File: README.md

Recommend this page to a friend!
  Classes of Emmanuel Antico  >  eMapper  >  README.md  >  Download  
File: README.md
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: eMapper
Database abstraction layer that maps data types
Author: By
Last change: Updated: Docs and changelog.
Updated: Docs.
Date: 6 years ago
Size: 40,368 bytes
 

Contents

Class file image Download
eMapper
==============

**The Extensible Data Mapper library for PHP**

<br/>
**Author**: Emmanuel Antico
<br/>
**Version**: 3.2.0

<br/>
Changelog
------------------
<br/>
2014-08-28 - Version 3.2.0 

  * Added: Associations.

<br/>
Dependencies
--------------
<br/>
- PHP >= 5.4
- [eMapper-annotations](https://github.com/emaphp/eMapper-annotations "") package
- [eMacros](https://github.com/emaphp/eMacros "") package
 
<br/>
Installation
--------------
<br/>
**Installation through Composer**
<br/>
```javascript
{
    "require": {
        "emapper/emapper" : "3.2"
    }
}
```

<br/>
About
------------

<br/>
*eMapper* is a PHP library aimed to provide a simple, powerful and highly customizable data mapping tool. It comes with some interesting features like:

- **Customized mapping**: Results can be mapped to a desired type through mapping expressions.
- **Indexation and Grouping**: Lists can be indexed or grouped together by a column value.
- **Custom types**: Developers can design their own types and custom type handlers.
- **Cache providers**: Obtained data can be stored in cache using APC or Memcache.
- **Dynamic SQL**: Queries can contain Dynamic SQL clauses writted in *eMacros*.
- **Entity Managers**: Managers provide a set of small ORM features which are common in similar frameworks.

<br/>
Introduction
-----------

<br/>
>Step 1: Pick an engine

eMapper supports SQLite, PostgreSQL and MySQL (or MariaDB if you prefer). Creating a connection requires creating an instance of the corresponding driver.

```php
//MYSQL
use eMapper\Engine\MySQL\MySQLDriver;

$driver = new MySQLDriver('database', 'localhost', 'user', 'passw');
//...

//SQLite
use eMapper\Engine\SQLite\SQLiteDriver;

$driver = new SQLiteDriver('database.sqlite');
//...

//PostgreSQL
use eMapper\Engine\PostgreSQL\PostgreSQLDriver;

$driver = new PostgreSQLDriver('host=localhost port=5432 dbname=database user=postgres password=123456');
```

<br/>
>Step 2: The Data Mapper

Now that the connection driver is ready we create an intance of the *Mapper* class.
```php
use eMapper\Mapper;

$mapper = new Mapper($driver);
```

<br/>
>Step 3: Have fun

eMapper is not ORM-oriented but type-oriented. Everything revolves around queries and *mapping expressions*. The following examples try to give you an idea of how the data mapping engine works.

```php
//by default, results are mapped to an array of arrays
$users = $mapper->query("SELECT * FROM users");

foreach ($users as $user) {
    echo $user['name'];
    //...
}

//to indicate the desired type we overload the mapper instance by calling the type method
$username = $mapper->type('string')->query("SELECT name FROM users WHERE id = 100");

//queries can receive a variable number of arguments
$users = $mapper->query("SELECT * FROM users WHERE surname = %{s} AND id > %{i}", "Doe", 1000);

//'arr' (or 'array') returns a row as an associative array
$user = $mapper->type('arr')->query("SELECT * FROM users WHERE name = %{s}", 'emaphp');

//using 'obj' as mapping expression will convert a row to an instance of stdClass
$user = $mapper->type('obj')->query("SELECT * FROM users WHERE id = %{i}", 42);

//close connection
$mapper->close();
```

<br/>
Mapping 101
-----------

<br/>
#####Simple types

```php
//mapping to an integer ('i', 'int', 'integer')
$total = $mapper->type('i')->query("SELECT COUNT(*) FROM users WHERE sex = %{s}", 'F');

//mapping to a boolean ('b', 'bool', 'boolean')
$suscribed = $mapper->type('b')->query("SELECT is_suscribed FROM users WHERE id = %{i}", 99);

//mapping to a float ('f', 'float', 'real', 'double')
$price = $mapper->type('f')->query("SELECT MAX(price) FROM products WHERE refurbished = {b}", false);

//mapping to a string ('s', 'str', 'string')
$body = $mapper->type('s')->query("SELECT message FROM posts WHERE slug = %{s}", 'emapper_rocks');

//dates ('DateTime', 'dt', 'timestamp', 'datetime')
$lastLogin = $mapper->type('dt')->query("SELECT last_login FROM users WHERE id = %{i}", 1984);

//finally, you can tell the exact column to fetch by providing a second argument
$code = $mapper->type('s', 'serial')->query("SELECT * FROM products WHERE id = %{i}", 101);
```

<br/>
#####Arrays

```php
//by default, an array contains both numeric and associative indexes
$movie = $mapper->type('array')->query("SELECT * FROM movies WHERE id = %{i}", 55);

//but you can change this by adding a second argument
use eMapper\Result\ArrayType;

//numeric only
$movie = $mapper->type('array', ArrayType::NUM)->query("SELECT * FROM movies WHERE id = %{i}", 56);

//associative only
$movie = $mapper->type('array', ArrayType::ASSOC)->query("SELECT * FROM movies WHERE id = %{i}", 57);
```

<br/>
#####Objects

```php
//mapping to a stdClass instance ('obj', 'object')
$book = $mapper->type('obj')->query("SELECT * FROM books WHERE isbn = %{s}", "9789507315428");

//using a custom class
$book = $mapper->type('obj:Acme\Library\Book')->query("SELECT * FROM books WHERE isbn = %{s}", "9788437604183");
```

*Note*: One important thing to remember when mapping to a structure is that values contained in columns declared using the DATE or DATETIME types are converted to instances of [DateTime](http://ar2.php.net/manual/en/class.datetime.php "").

<br/>
Lists
-----------

<br/>
#####Simple types

```php
//just add brackets at the end of the expression and you're done
$id_list = $mapper->type('int[]')->query("SELECT id FROM users");

$prices = $mapper->type('float[]')->query("SELECT price FROM products");

$isbn_list = $mapper->type('string[]')->query("SELECT isbn FROM books");

$creation_dates = $mapper->type('dt[]')->query("SELECT created_at FROM posts");

//again, a second argument could be provided to define which column to fetch
$refurbished = $mapper->type('bool[]', 'refurbished')->query("SELECT * FROM products");
```

<br/>
#####Arrays and Objects

```php
//same rule applies to arrays and objects
$users = $mapper->type('obj[]')->query("SELECT * FROM users");

//an array type could also be provided when mapping to lists
use eMapper\Result\ArrayType;

$users = $mapper->type('arr[]', ArrayType::ASSOC)->query("SELECT * FROM users");
```

<br/>
Indexes and Groups
-----------------------

<br/>
#####Indexes
```php
//the index column goes between brackets
$books = $mapper->type('array[id]')->query("SELECT * FROM books");

//a default type could be added right next to it
$products = $mapper->type('arr[id:string]')->query("SELECT * FROM products");

//make sure the column is present in the result (this won't work)
$books = $mapper->type('obj[isbn]')->query("SELECT id, name, price FROM books");

//when mapping to arrays, the index should be represented appropriately
use eMapper\Result\ArrayType;

//use first column as index
$products = $mapper->type('arr[0]', ArrayType::NUM)->query("SELECT * FROM products");
```

<br/>
#####Groups
```php
//group by a known column
$products = $mapper->type('arr<category>')->query("SELECT * FROM products");

//a type could also be specified
$books = $mapper->type('obj<publisher:string>')->query("SELECT * FROM books");

//both expressions can be mixed to obtain interesting results
$products = $mapper->type('obj<category>[id:int]')->query("SELECT * FROM products");
```

<br/>
#####Callbacks
```php
//lists could also be indexed using a closure
$products = $mapper->type('array[]')
->index_callback(function ($product) {
    //return a custom made index
    return $product['code'] . '_' . $product['id'];
})
->query("SELECT * FROM products");

// a group callback does what you expect
//it can also be combined with indexes if needed
$products = $mapper->type('obj[id]')
->group_callback(function ($product) {
    return substr($product->category, 0, 3);
})
->query("SELECT * FROM products");
```

<br/>
Queries
-------


<br/>
#####Query arguments

```php
//arguments the easy way (type identifier between braces)
$products = $mapper->type('obj[]')->query("SELECT * FROM products WHERE price < %{f} AND category = %{s}", 699.99, 'Laptops');

//argument by position (and optional type)
$products = $mapper->type('obj[]')->query("SELECT * FROM products WHERE category = %{1} AND price < %{0:f}", 699.99, 'Laptops');
```

<br/>
#####Arrays/Objects as argument

```php
//array as parameter
$parameter = ['password' => sha1('qwerty'), 'modified_at' => new \DateTime];
$mapper->query("UPDATE users SET password = #{password}, modified_at = #{modified_at:dt} WHERE name = %{1:string}", $parameter, 'emaphp');

//syntax works with objects as well
use Acme\CMS\Comment;

$comment = new Comment();
$comment->setUserId(100);
$comment->setBody("Hello World");

$mapper->query("INSERT INTO comments (user_id, body) VALUES (#{userId}, #{body});", $comment);
```
*Note*: The syntax for array/object attributes work as long as you provide the array/object as the first argument.

<br/>
Named Queries
-------------

<br/>
#####Statements

```php
//declaring a statement
$mapper->stmt("findUserByPk", "SELECT * FROM users WHERE user_id = %{i}");

//statements are invoked with the execute method
$mapper->type('obj[]')->execute('findUserByPk', 100);
```

<br/>
#####Configuration

```php
//the stmt method supports a third argument with the predefined configuration for that query
use eMapper\SQL\Statement;

//declare statement
$mapper->stmt('findProductsByCategory',
              "SELECT * FROM products WHERE category = %{s}",
              Statement::type('obj[]'));

//get products as an object list
$products = $mapper->execute('findProductsByCategory', 'Audio');

//override default configuration and return result as an indexed list of arrays
$products = $mapper->type('array[id]')->execute('findProductsByCategory', 'Smartphones');
```

<br/>
#####Namespaces

```php
//namespaces provide a more organized way of declaring statements
use eMapper\SQL\SQLNamespace;
use eMapper\SQL\Statement;

class UsersNamespace extends SQLNamespace {
    public function __construct() {
        //set namespace id through parent constructor
        parent::__construct('users');
        
        $this->stmt('findByPk',
                    "SELECT * FROM users WHERE id = %{i}",
                    Statement::type('obj'));
                    
        $this->stmt('findByName',
                    "SELECT * FROM users WHERE name = %{s}",
                    Statement::type('obj'));
                    
        $this->stmt('findRecent'
                    "SELECT * FROM users WHERE created_at >= subdate(NOW(), INTERVAL 3 DAY)",
                    Statement::type('obj[id:int]'));
    }
}

//add namespace
$mapper->addNamespace(new UsersNamespace());

//namespace id must be specified as a prefix
$user = $mapper->execute('users.findByPK', 4);
```

<br/>
Stored procedures
-----------------

<br/>
#####Invoking stored procedures

The *Mapper* class uses [method overloading](http://php.net//manual/en/language.oop5.overloading.php "") to translate an invokation to an non-existant method into a stored procedure call.

```php
//MySQL: CALL Users_Clean()
//PostgreSQL: SELECT Users_Clean()
$mapper->Users_Clean();

//if database prefix is set it is used as a prefix
$mapper->setPrefix('COM_');

//MySQL: CALL COM_Users_Clean()
//PostgreSQL: SELECT COM_Users_Clean()
$mapper->Users_Clean();
```

<br/>
#####Mapping values
```php
//simple values con be obtained like usual
$total = $mapper->type('i')->Users_CountActive();

//engines may differ in how to treat a structured value
$user = $mapper->type('obj')->Users_FindByPK(3); //works in MySQL only

//PostgreSQL needs an additional configuration value
//SQL: SELECT * FROM Users_FindByPK(3)
$user = $mapper->type('obj')->option('proc.as_table', true)->Users_FindByPK(3);
```

<br/>
Entity Managers
---------------

<br/>
#####Entities and Annotations
Writing all your queries can turn very frustating real soon. Luckily, eMapper provides a small set of ORM features that can save you a lot of time. Entity managers are objects that behave like DAOs ([Data Access Object](http://en.wikipedia.org/wiki/Data_access_object "")) for a specified class. The first step to create an entity manager is designing an entity class. The following example shows an entity class named *Product*. The *Product* class obtains its values from the *pŕoducts* table, indicated by the *@Entity* annotation. This class defines 5 attributes, and each one defines its type through the *@Type* annotation. If the attribute name differs from the column name we can specify a *@Column* annotation indicating the real one. As a general rule, all entities must define a primary key attribute. The *Product* class sets its **id** attribute as primary key using the *@Id* annotation.

```php
namespace Acme\Factory;

/**
 * @Entity products
 */
class Product {
    /**
     * @Id
     * @Type integer
     */
    private $id;
    
    /**
     * @Type string
     * @Column pcod
     */
    private $code;

    /**
     * @Type string
     */
    private $description;

    /**
     * @Type string
     */
    private $category;
    
    /**
     * @Type float
     */
    private $price;
    
    public function getId() {
        return $this->id;
    }
    
    public function setCode($code) {
        $this->code = $code;
    }
    
    public function getCode()  {
        return $this->code;
    }
    
    public function setDescription($description) {
        $this->description = $description;
    }

    public function getDescription() {
        return $this->description;
    }
    
    public function setCategory($category) {
        $this->category = $category;
    }

    public function getCategory(){
        return $this->category;
    }
    
    public function setPrice($price) {
        $this->price = $price;
    }

    public function getPrice() {
        return $this->price;
    }
}
```
Managers are created through the *buildManager* method in the *Mapper* class. This method expects the entity class full name. Managers are capable of getting results using filters without having to write SQL manually.

```php
//create a products manager
$products = $mapper->buildManager('Acme\\Factory\\Product');

//get by id
$product = $products->findByPk(4);

//obtain a product by code
use eMapper\Query\Attr;

$product = $products->get(Attr::code()->eq('XXX098'));

//get a list of products by a given category
$list = $products->find(Attr::category()->eq('Laptops'));

//define a filter (category = 'E-Books' AND price < 799.99)
$list = $products
->filter(Attr::category()->eq('E-Books'), Attr::price()->lt(799.99))
->find();

//OR condition (category = 'E-Books' OR price < 799.99)
use eMapper\Query\Q;

$list = $products
->filter(Q::where(Attr::category()->eq('E-Books'), Attr::price()->lt(799.99)))
->find();

//exclude (category <> 'Fitness')
$list = $products
->exclude(Attr::category()->eq('Fitness'))
->find();

//reverse condition (description NOT LIKE '%Apple%')
$list = $products
->filter(Attr::description()->contains('Apple', false))
->find();
```

<br/>
#####Manager utilities

```php
use eMapper\Query\Attr;

//getting all products indexed by id
$list = $products->index(Attr::id())->find();

//or grouped by category
$list = $products->group(Attr::category())->find();

//callbacks work as well
$list = $products
->index_callback(function ($product) {
    return $product->getId() . substr($product->getCode(), 0, 5);
})
->find();

//order and limit clauses
$list = $products
->order_by(Attr::id())
->limit(15)
->find();

//setting the order type
$list = $products
->order_by(Attr::id('ASC'), Attr::category())
->limit(10, 20)
->find();

//count products of a given category
$total = $products
->filter(Attr::category()->eq('Audio'))
->count();

//average price
$avg = $products
->exclude(Attr::category()->eq('Laptops'))
->avg(Attr::price());

//max price (returns as integer)
$max = $products
->filter(Attr::code()->startswith('SONY'))
->max(Attr::price(), 'int');
```

<br/>
#####Storing objects

```php
use Acme\Factory\Product;

//create manager
$products = $mapper->buildManager('Acme\Factory\Product');

$product = new Product;
$product->setCode('ACM001');
$product->setDescription('Test product');
$product->setCategory('Explosives');
$product->setPrice(149.90);

//save object
$products->save($product);

//if the entity already has an id 'save' produces an UPDATE query
$product = $products->get(Attr::code()->eq('ACM001'));
$product->setPrice(129.90);
$products->save($product);
```


<br/>
#####Deleting objects

```php
//create manager
$products = $mapper->buildManager('Acme\Factory\Product');

//delete expects an entity as parameter
$product = $products->findByPk(20);
$products->delete($product);

//reduced version
$products->deleteWhere(Attr::id()->eq(20));

//for security reasions, truncating a table requires a special method
$products->truncate();
```

<br/>
Entity Namespaces
-----------------

<br/>
#####Introduction

Entity namespaces are classes that generate statements from an entity class automatically. These classes receive their respective ids from the *@DefaultNamespace* annotation in the entity class declaration. When not specified, the namespace takes the value indicated by the *@Entity* annotation.

```php
namespace Acme\Factory;

/**
 * @Entity products
 * @DefaultNamespace products
 */
class Product {
    /**
     * @Id
     * @Type integer
     */
    private $id;
    
    /**
     * @Unique
     * @Type string
     */
    private $code;
    
    /**
     * @Type string
     */
    private $description;
    
    /**
     * @Type string
     */
    private $category;
    
    /**
     * @Type float
     */
    private $price;
    
    //...
}

```

<br/>
#####Adding a entity namespace

```php
//add an entity namespace
use eMapper\SQL\EntityNamespace;
$mapper->addEntityNamespace(new EntityNamespace('Acme\Factory\Product'));

//find by primary key
$product = $mapper->execute('products.findByPk', 2);

//find all
$products = $mapper->execute('products.findAll');

//find by
$products = $mapper->execute('products.findByCategory', 'Laptops');

//override mapping expression
$products = $mapper->type('obj:Acme\Factory\Product[id]')->execute('descriptionIsNull');
```

<br/>
#####Statement list


<table>
    <thead>
        <tr>
            <th>Statement ID</th>
            <th>Example</th>
            <th>Returned Value</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>findByPk</td>
            <td><em>products.findByPk</em></td>
            <td>Instance (NULL if not found)</td>
        </tr>
        <tr>
            <td>findAll</td>
            <td><em>products.findAll</em></td>
            <td>List</td>
        </tr>
        <tr>
            <td>findBy{PROPERTY}</td>
            <td><em>products.findByCode</em></td>
            <td>Instance if property is @Id or @Unique, a list otherwise</td>
        </tr>
        <tr>
            <td>{PROPERTY}[Not]Equals</td>
            <td><em>products.codeEquals</em><br/><em>products.priceNotEquals</em></td>
            <td>Instance if property is @Id or @Unique, a list otherwise</td>
        </tr>
        <tr>
            <td>{PROPERTY}[Not][I]Contains</td>
            <td><em>products.categoryContains</em><br/><em>products.descriptionNotContains</em><br/><em>products.categoryIContains</em></td>
            <td>List</td>
        </tr>
        <tr>
            <td>{PROPERTY}[Not][I]StartsWith</td>
            <td><em>products.categoryStartsWith</em><br/><em>products.descriptionNotStartsWith</em><br/><em>products.categoryIStartsWith</em></td>
            <td>List</td>
        </tr>
        <tr>
            <td>{PROPERTY}[Not][I]EndsWith</td>
            <td><em>products.categoryEndsWith</em><br/><em>products.descriptionNotEndsWith</em><br/><em>products.categoryIEndsWith</em></td>
            <td>List</td>
        </tr>
        <tr>
            <td>{PROPERTY}[Not]In</td>
            <td><em>products.idIn</em><br/><em>products.idNotIn</em></td>
            <td>List</td>
        </tr>
    <tbody>
        <td>{PROPERTY}[Not]GreaterThan[Equal]</td>
        <td><em>products.idGreaterThan</em><br/><em>products.priceGreaterThanEqual</em><br/><em>products.priceNotGreaterThan</em></td>
        <td>List</td>
    </tbody>
    <tbody>
        <td>{PROPERTY}[Not]LessThan[Equal]</td>
        <td><em>products.idLessThan</em><br/><em>products.priceLessThanEqual</em><br/><em>products.priceNotLessThan</em></td>
        <td>List</td>
    </tbody>
    <tbody>
        <td>{PROPERTY}[Not]IsNull</td>
        <td><em>products.descriptionIsNull</em><br/><em>products.descriptionIsNotNull</em></td>
        <td>List</td>
    </tbody>
    <tbody>
        <td>{PROPERTY}[Not]Between</td>
        <td><em>products.priceBetween</em><br/><em>products.priceNotBetween</em></td>
        <td>List</td>
    </tbody>
    <tbody>
        <td>{PROPERTY}[Not][I]Matches</td>
        <td><em>products.categoryMatches</em><br/><em>products.codeNotMatches</em><br/><em>products.descriptionIMatches</em></td>
        <td>List</td>
    </tbody>
</table>

<br/>
Associations
-----------

<br/>
#####Introduction
Associations provide an easy way to fetch data related to an entity. As expected, they also need to be declared using a special set of annotations. Their types include *one-to-one*, *one-to-many*, *many-to-one* and *many-to-many*.

<br/>
#####One-To-One

The next example shows 2 entities (*Profile* and *User*) and how to declare a One-To-One association to obtain a *User* entity from a fetched profile.

```php
namespace Acme;

/**
 * @Entity profiles
 */
class Profile {
    /**
     * @Id
     * @Type integer
     */
    private $id;
    
    /**
     * @Column user_id
     */
    private $userId;
    
    /**
     * @Column firstname
     */
    private $firstName;
    
    /**
     * @Type string
     */
    private $surname;
    
    /**
     * @OneToOne User
     * @Attr(userId)
     */
    private $user;
    
    //...
}
```
In this example, the *Product* class declares a **user** property which defines a one-to-one association with the *User* class. The *@Attr* annotation specifies which property is used to perform the required join.

```php
namespace Acme;

/**
 * @Entity users
 */
class User {
    /**
     * @Id
     * @Type integer
     */
    private $id;
    
    /**
     * @Column username
     */
    private $name;
    
    /**
     * @OneToOne Profile
     * @Attr userId
     * @Lazy
     */
    private $profile;
    
    //...
}
```
The *User* class defines the **profile** property as a one-to-one associaton with the *Profile* class. Notice that this time the *@Attr* annotation defines the property name as a value, not as an argument. Also, this association is declared as **lazy**, which means that is not evaluated right away. The following example shows how to obtain a *Profile* instance and its associated user.

```php
//obtain the profile with ID = 100
$manager = $mapper->buildManager('Acme\Profile');
$profile = $manager->findByPk(100); // returns a Profile instance
$user = $profile->getUser(); // returns the associated User instance
```

Lazy associations returns an instance of *eMapper\AssociationManager*. This means that invoking the *getProfile* method will return a manager instance. In order to get the referred value we append a call to the *fetch* method.

```php
//obtain the user with ID = 100
$manager = $mapper->buildManager('Acme\User');
$user = $manager->findByPk(100);
$profile = $user->getProfile(); // returns an AssociationManager instance
$profile = $user->getProfile()->fetch(); // fetch() returns the desired result
```
Associations also provide a mechanism to query for related attributes. Suppose we want to obtain a profile by its user name. We can do this by using a special syntax that specifies the association property and the comparison attribute separated by a doble underscore.

```php
use eMapper\Query\Attr;

//build manager
$manager = $mapper->buildManager('Acme\Profile');

//users.name = 'jdoe'
$profile = $manager->get(Attr::user__name()->eq('jdoe'));
```

<br/>
#####One-To-Many and Many-To-One

Suppose we need to design a pet shop database to store data from a list of clients and their respective pets. The first step after creating the database will be implementing the *Client* and *Pet* entity classes. The *Client* class has a one-to-many association to the *Pet* class provided through the **pets** property. The required attribute (**clientId**) is specified as a value of the *@Attr* annotation.This annotation references to the attribute in the *Pet* class that stores the client identifier.
```php
namespace Acme;

/**
 * @Entity clients
 */
class Client {
    /**
     * @Id
     */
    private $id;
    
    /**
     * @Type string
     */
    private $name;
    
    /**
     * @Type string
     */
    private $surname;
 
    /**
     * @OneToMany Pet
     * @Attr clientId
     */   
    private $pets;
}
```
This small example obtains all clients that have dogs.
```php
use eMapper\Query\Attr;

$manager = $mapper->buildManager('Acme\Client');

//get all clients that have dogs
$clients = $manager->find(Attr::pets__type()->eq('Dog'));
```

From the point of view of the *Pet* class this is a many-to-one association. The **owner** association is resolved through the **clientId** attribute of the current class, meaning that in this case it has to be specified as an argument of the *@Attr* annotation.

```php
namespace Acme;

/**
 * @Entity pets
 */
class Pet {
    /**
     * @Id
     */
    private $id;
    
    /**
     * @Column client_id
     */
    private $clientId;
    
    /**
     * @Type string
     */
    private $name;
    
    /**
     * @Type string
     */
    private $type;
    
    /**
     * @Column birth_date
     * @Type string
     */
    private $birthDate;
    
    /**
     * @ManyToOne Client
     * @Attr(clientId)
     */
    private $owner;
    //...
}
```
This small example obtains all pets for a given client.
```php
use eMapper\Query\Attr;

$manager = $mapper->buildManager('Acme\Pet');

//get all pets of Joe Doe
$pets = $manager->find(Attr::owner__name()->eq('Joe'),
                       Attr::owner__surname()->eq('Doe'));
```


<br/>
#####Many-To-Many

Many-To-Many associations are kind of special as they need to provide the name of the join table that resolves the association. Suppose that we want to add a **favorites** association from the *User* class to the *Product* class.

```php
namespace Acme;

/**
 * @Entity users
 */
class User {
    /**
     * @Id
     */
    private $id;
    
    /**
     * @Column username
     */
    private $name;
    
    /**
     * @Type string
     */
    private $mail;
    
    /**
     * @ManyToMany Product
     * @JoinWith(favorites) prod_id
     * @Column usr_id
     * @Lazy
     */
    private $favorites;
}
```
The *@JoinWith* annotation must provide the join table name as argument and the column that references the associated entity as value. In this case, this association is resolved using the *favorites* table and the product identifier is stored using the *prod_id* column. The *@Column* annotation must then declare which column in the *favorites* table identifies the current entity. The following code shows an example on how to query for a user favorited products.

```php
use eMapper\Query\Attr;

$manager = $mapper->buildManager('Acme\User');

//get all users that like Android
$users = $manager->find(Attr::favorites__description()->contains('Android'));
```

<br/>
#####Recursive associations

There are some scenarios in which an entity is associated to itself in more than one way. In this example we'll introduce the *Category* entity class to explore in detail this type of associations.

```php
namespace Acme;

/**
 * @Entity categories
 */
class Category {
    /**
     * @Id
     */
    private $id;
    
    /**
     * @Type string
     */
    private $name;
    
    /**
     * @Type integer
     */
    private $parentId;
    
    /**
     * @ManyToOne Category
     * @Attr(parentId)
     */
    private $parent;
    
    /**
     * @OneToMany Category
     * @Attr parentId
     */
    private $subcategories;
    //...
}
```

The *Category* class its related to itself through the **parent** and **subcategories** associations. Both of them need to specify the *parentId* attribute as the join attribute. Obtaining all subcategories
of a given category can be resolved in the following way.

```php
use eMapper\Query\Attr;

$manager = $mapper->buildManager('Acme\Category');

//get all subcategories of 'Technology'
$categories = $mapper->find(Attr::parent__name()->eq('Technology'));
```



<br/>
Dynamic SQL
-----------

<br/>
#####Introduction
Queries could also contain logic expressions which are evaluated againts current arguments. These expressions (or S-expressions) are written in [eMacros](https://github.com/emaphp/eMacros ""), a language based on lisphp. Dynamic expressions are included between the delimiters [? and ?]. The next example shows a query that sets the condition dynamically according to the argument type.
```sql
SELECT * FROM users WHERE [? (if (int? (%0)) 'id = %{i}' 'name = %{s}') ?]
```

```php
//add named query
$mapper->stmt('findUser', "SELECT * FROM users WHERE [? (if (int? (%0)) 'id = %{i}' 'name = %{s}') ?]");

//find by id
$user = $mapper->type('obj')->execute('findUser', 99);

//find by name
$user = $mapper->type('obj')->execute('findUser', 'emaphp');
```

<br/>
#####eMacros 101

Just to give you a basic approach of how S-expressions work here's a list of small examples. Refer to eMacros documentation for more.
```lisp
; simple math
(+ 4 10) ; 14
(- 10 4) ; 6
(* 5 3 2) ; 30

; sum first two arguments
(+ (%0) (%1))

; concat
(. "Hello" " " "World!")

; is int?
(int? 6) ; true

; is string?
(string? true) ; false

; cast to float
(as-float "65.32")

; get property value
(#description)

; get configuration value
(@default_order)

; if then else
(if (null? (#id)) "No id found!" (#id)) ; returns id when not null
```

<br/>
#####Configuration values

This example adds an ORDER clause if the configuration key 'order' is set.
```sql
SELECT * FROM users [? (if (@order?) (. 'ORDER BY ' (@order))) ?]
```

```php
//add named query
$mapper->stmt('obtainUsers', "SELECT * FROM users [? (if (@order?) (. 'ORDER BY ' (@order))) ?]");

//get all users
$mapper->type('obj[]')->execute('obtainUsers');

//the option method creates an instance with an additional configuration value
//get ordered users
$mapper->type('obj[]')->option('order', 'last_login')->execute('obtainUsers');
```

<br/>
#####Typed expressions
A value returned by a dynamic SQL expression can be associated to a type by adding the type identifier right after the first delimiter. This example simulates a search using the LIKE operator with the value returned by a dynamic expression that returns a string.
```sql
SELECT * FROM users WHERE name LIKE [?string (. '%' (%0) '%') ?]
```

```php
//add named query
$mapper->stmt('searchUsers', "SELECT * FROM users WHERE name LIKE [?string (. '%' (%0) '%') ?]");

//search by name
$users = $mapper->map('obj[]')->execute('searchUsers', 'ema');
```

<br/>
Dynamic Attributes
------------------

<br/>
#####Introduction
While associations are pretty useful by themselves you may want to go a step further. A dynamic attibute is a property that is the result of a query or calculation. This means that attributes could either be associated to a macro or a custom user query.

<br/>
#####Querys

This example introduces a new entity class named *Sale*. A sale is obviously related to a product. Let's say that we want to obtain that product using the corresponding column. In order to do this, we add a **product** property which includes a special *@Query* annotation. This annotation expects a string containing the query that solves this association. Notice that *Sale* must define a **productId** property in order to store the value sent to the query.

```php
namespace Acme\Factory;

/**
 * @Entity sales
 */
class Sale {
    /**
     * @Id
     */
    private $id;
    
    /**
     * @Column product_id
     */
    private $productId;
    
    /**
     * @Query "SELECT * FROM products WHERE id = #{productId}"
     * @Type obj:Acme\Factory\Product
     */
    private $product;
    
    //...
}
```
When used along with *@Query*, the *@Type* annotation specifies the mapping expression to use.

<br/>
#####Adding parameters

The *@Parameter* annotation can be used to define a list of arguments for a dynamic attribute. These arguments can be either a property of the current entity or a constant value. A special annotation *@Self* indicates that the current instance is used as first argument. Knowing this we can redefine the product property in two ways.

```php
/**
 * @Query "SELECT * FROM products WHERE id = #{productId}"
 * @Self
 * @Type obj:Acme\Factory\Product
 */
private $product; //Here @Self is not required because the default
                  //behaviour already uses the entity as unique argument
                  
/**
 * @Query "SELECT * FROM products WHERE id = %{i}"
 * @Parameter(productId)
 * @Type obj:Acme\Factory\Product
 */
private $product; //Here productId is the only argument for this query
                  //This requires a modification in the query argument expression
```

In other words, *@Self* must be added if the specified query receives the current instance and an additional value as arguments. The next example adds a **relatedProducts** property in the *Product* class that includes 2 arguments: the current partial instance and a integer value that sets the amount of objects to return.

```php
namespace Acme\Factory;

class Product {
    //...
    
    /**
     * @Query "SELECT * FROM products WHERE category = #{category} LIMIT %{i}"
     * @Self
     * @Parameter 10
     * @Type obj::Acme\Factory\Product[id]
     */
    private $relatedProducts;
}
```

<br/>
#####Statements

Named queries could also be called from a dynamic attribute by adding the *@StatementId* annotation.

```php
namespace Acme\Factory;

/**
 * @Entity sales
 */
class Sale {
    /**
     * @Id
     */
    private $id;
    
    /**
     * @Column product_id
     */
    private $productId;
    
    /**
     * @StatementId products.findByPk
     * @Parameter(productId)
     * @Type obj:Acme\Factory\Product
     */
    private $product;
    
    //...
}
```

<br/>
#####Stored procedures

Procedures are defined through the *@Procedure* annotation. There's not much to say about them except that they ignore the *@Self* annotation for obvious reasons.

```php
/**
 * @Entity products
 */
class Product {
    /**
     * @Id
     */
    private $id;
    
    /**
     * @Type string
     */
    private $category;
    
    /**
     * @Procedure Products_AvgPriceByCategory
     * @Parameter(category)
     * @Type float
     */
    private $averagePrice;
    
    //...
}
```

<br/>
#####Macros
*@Eval* evaluates a macro against current entity. Examples of usage of these type of attributes are getting a person's fullname or calculating his age.
```php
/**
 * @Entity people
 */
class Person {
    /**
     * @Id
     */
    private $id;
    
    /**
     * @Type string
     */
    private $name;
    
    /**
     * @Column surname
     */
    private $lastname;
    
    /**
     * @Column birth_date
     * @Type dt
     */
    private $birthDate;
    
    /**
     * @Eval (. (#name) ' ' (#lastname))
     */
    private $fullname; //build person's full name
    
    /**
     * @Eval (as-int (diff-format (#birthDate) (now) "%y"))
     */
    private $age; //calculate person's age
    
    //...
}
```

<br/>
#####Conditional attributes

The *@If* and *@IfNot* annotations are used to define conditional attributes. These attributes are evaluated if the given expression evaluates to true with @If and false with *@IfNot*. Conditions must be expressed alaso as macros.

```php
/**
 * @Entity users
 */
class User {
    /**
     * @Id
     */
    private $id;
    
    /**
     * @Type string
     */
    private $type;

    /**
     * @If (== (#type) 'member')
     * @StatementId "users.loginHistory"
     * @Parameter(id)
     */
    private $loginHistory; //get login history if user is a member
    
    /**
     * @IfNot (or (== (#type) 'member') (== (#type) 'guest'))
     * @StatementId 'admin.findNotifications'
     * @Parameter(id)
     */
    private $abuseNotifications; //get admin notifications if not a member nor guest
}
```


<br/>
#####Options
The *@Option* annotation set a custom option value for the current attribute. An option must define its name by setting it between parentheses just before the value.
```php
/**
 * @Entity users
 */
class User {
    /**
     * @Id
     */
    private $id;
    
    /**
     * @Query "SELECT * FROM contacts WHERE user_id = %{i} [? (. 'ORDER BY ' (@order)) ?]"
     * @Parameter(id)
     * @Option(order) 'contact_type'
     */
    private $contacts;
    
    //...
}
```

<br/>
Cache
-----

<br/>
#####Introduction
Currently, eMapper supports APC, Memcache and Memcached. Before setting a cache provider make sure the required extension is correctly installed.

<br/>
#####Providers
```php
//APC
use eMapper\Cache\APCProvider;

$provider = new APCProvider;
$mapper->setCacheProvider($provider);

//Memcache
use eMapper\Cache\MemcacheProvider;

$provider = new MemcacheProvider('localhost', 11211);
$mapper->setCacheProvider($provider);

//Memcached
use eMapper\Cache\MemcachedProvider;

$provider = new MemcachedProvider('mem');
$mapper->setProvider($provider);
```

<br/>
#####Storing values
```php
//get users and store values with the key 'USERS_ALL' (time to live: 60 sec)
//if the key is found in cache no query takes place (write once, run forever)
$users = $mapper->cache('USERS_ALL', 60)->query("SELECT * FROM users");

//cache keys also receive query arguments
//store values with the key 'USER_6'
$user = $mapper->type('obj')->cache('USER_%{i}', 120)->query("SELECT * FROM users WHERE id = %{i}", 6);
```

<br/>
Custom types
------------

<br/>
#####Introduction

Type handlers are classes that manage how a value is stored and retrieved from a column. To introduce how a type handler works we'll introduce a custom type called *RGBColor*.

```php
namespace Acme;

//the RGBColor class is a three component class that
//stores the amount of red, green and blue in a color
class RGBColor {
    public $red;
    public $green;
    public $blue;
    
    public function __construct($red, $green, $blue) {
        $this->red = $red;
        $this->green = $green;
        $this->blue = $blue;
    }
}
```
<br/>
#####Type handlers

A type handler extends the *eMapper\Type\TypeHandler* class and implements the *setParameter* and *getValue* methods.

```php
namespace Acme;

use eMapper\Type\TypeHandler;

class RGBColorTypeHandler extends TypeHandler {
    /**
     * Converts a RGBColor instance to a string expression
     */
    public function setParameter($color) {
        $red = ($color->red < 16) ? '0' . dechex($color->red) : dechex($color->red % 256);
		$green = ($color->green < 16) ? '0' . dechex($color->green % 256) : dechex($color->green);
		$blue = ($color->blue < 15) ? '0' . dechex($color->blue) : dechex($color->blue % 256);
		return $red . $green . $blue;
    }
    
    /**
     * Generates a new RGBColor instance from a string value
     */
    public function getValue($value) {
        return new RGBColor(hexdec(substr($value, 0, 2)),
                            hexdec(substr($value, 2, 2)),
                            hexdec(substr($value, 4, 2)));
    }
}
```

<br/>
#####Types and aliases
```php
//add type
use Acme\RGBColorTypeHandler;
$mapper->addType('Acme\RGBColor', new RGBColorTypeHandler());

//mapping to RGBColor
$color = $mapper->type('Acme\RGBColor')->query("SELECT color FROM palette WHERE id = 1");

//using an alias
$mapper->addType('Acme\RGBColor', new RGBColorTypeHandler(), 'rgb');

//mapping to RGBColor using the previously defined alias
$color = $mapper->type('rgb')->query("SELECT color FROM palette WHERE id = 1");
```

<br/>
#####Using custom types

```php
/**
 * @Entity vehicles
 */
class Car {
    /**
     * @Id
     * @Type integer
     */
    private $id;
    
    /**
     * @Type string
     */
    private $manufacturer;
    
    /**
     * @Type rgb
     */
    private $color;
    
    //...
}

```

<br/>
License
--------------
<br/>
This code is licensed under the BSD 2-Clause license.

For more information send a message to info at phpclasses dot org.