Quick Start

These code and config samples should help you to get up and running quickly. Further explanation and samples are provided in the pages that follow.

Installation

Objectiphy requires PHP 7.4 or above (PHP 8.1+ is recommended), is available on GitHub and can be installed using Composer:

composer require objectiphy/objectiphy

Before you can start creating repositories to read and write data, you will need a repository factory. The factory requires a database connection, and ideally should also be told whether it is in development mode or not, and be given a directory in which to store cache files (if not specified, it will be assumed that you are in development mode, and proxy classes will be stored in the PHP temporary directory - usually /tmp).

The development mode flag is used to decide whether to re-generate proxy classes on each request or not, and whether to clear the cache between requests, so setting the flag to false on your production site will drastically improve performance. The cache directory should be set to a directory that is writeable by the user PHP runs under (typically, wherever you normally store cache files, such as var/cache if using Symfony). If proxies are stored in the PHP temp directory, there is a risk that the PHP garbage collector will delete them before they are finished with, so please ensure you specify a different directory at least in production.

The following sections give examples on how to configure Objectiphy in Symfony, Pimple, and using plain PHP code. This should give you enough information to adapt to whatever dependency injection container you are using.

Quick Symfony Configuration

A typical Symfony configuration for the Objectiphy repository factory looks like this:

PDO:
    class: \PDO
    arguments:
      - 'mysql:host=%env(DB_HOST)%;dbname=%env(DB_NAME)%'
      - '%env(DB_USER)%'
      - '%env(DB_PASSWORD)%'

# Or if you want to re-use an existing Doctrine connection:
# doctrine_connection:
#     class: Doctrine\DBAL\Connection
#     factory: ['@doctrine.orm.default_entity_manager', 'getConnection']
# PDO:
#     class: \PDO
#     factory: ['@doctrine_connection', 'getNativeConnection']

Objectiphy\Objectiphy\Factory\RepositoryFactory:
    arguments:
        $pdo: '@PDO'
        $cacheDirectory: '%kernel.cache_dir%'
        $devMode: '%kernel.debug%'

To create a repository for an entity (in this example, a Policy entity), you can define it like this (note: you can create a repository for a specific entity without needing to write your own custom repository class):

# Standard Objectiphy repository for a known entity
MyPolicyRepository:
    class: Objectiphy\Objectiphy\Orm\ObjectRepository
    factory: ['@Objectiphy\Objectiphy\Factory\RepositoryFactory', 'createRepository']
    arguments:
        $entityClassName: 'MyNamespace\Policy'

The object returned by the above definition will be a standard Objectiphy object repository which is able to load and save Policy instances. If you don't know ahead of time which entity you are going to need from the repository, you can defer specifying the entity class until later:

# Standard Objectiphy repository for any entity
MyRepository:
    class: Objectiphy\Objectiphy\Orm\ObjectRepository
    factory: ['@Objectiphy\Objectiphy\Factory\RepositoryFactory', 'createRepository']

If you want to override the default repository to add your own methods, just extend the Objectiphy\Objectiphy\ObjectRepository class, and you can tell Objectiphy to use your custom class either by specifying your custom repository class name in the Table mapping definition, or in your yaml definition file you can tell it to use your replacement class like this:

# Custom Objectiphy repository for a known entity
MyPolicyRepository:
    class: Objectiphy\Objectiphy\Orm\ObjectRepository
    factory: ['@Objectiphy\Objectiphy\Factory\RepositoryFactory', 'createRepository']
    arguments:
        $entityClassName: 'MyNamespace\Policy'
        $repositoryClassName: 'MyNamespace\MyCustomPolicyRepository'

To use a repository, you should explicitly tell Symfony which repository instance to use, and use the repository factory to create it, rather than relying entirely on auto-wiring - otherwise you risk breaking forward compatibility. Doctrine is similar - you can't just type-hint on a Doctrine repository and auto-wire it, as you need to use the entity manager to generate a specific type of repository. Instead of the entity manager, Objectiphy uses the repository factory.

So, for example, if you have a controller which takes in a generic Objectiphy ObjectRepository (in which case, as shown here, you should type hint on the interface, not the concrete repository class):

namespace MyNamespace\Controller;

use Objectiphy\Objectiphy\Contracts\ObjectRepositoryInterface;

class MyPolicyController
{
    protected ObjectRepositoryInterface $myPolicyRepository;

    public function __construct(ObjectRepositoryInterface $myPolicyRepository)
    {
        $this->myPolicyRepository = $myPolicyRepository;
    }
}

...you would need to explicitly define which repository to pass in using yaml like this:

MyNamespace\Controller\MyPolicyController:
    arguments:
        $myRepository: '@MyPolicyRepository'

Of course, if you are using a custom repository class, you can use auto-wiring to inject it into your controller (or service), but you should still use the repository factory to create your repository - this ensures it has everything needed to do its job, and if changes are made to Objectiphy in future (for example a new dependency is introduced), your code will not break, as the factory will take care of it for you. Some people like to have an empty custom repository class for every entity (typically auto-generated by the maker bundle) - this means less dependency wiring, but more code bloat.

Quick Pimple Configuration

A Pimple implementation might look more like this:

use Objectiphy\Objectiphy\Factory\RepositoryFactory;
use MyNamespace\Entity\MyPolicy;
use MyNamespace\Repository\MyPolicyRepository;

// Create the PDO instance
$container['pdo'] = function() {
    return new \PDO(
        "mysql:host=localhost;dbname=dbname", 
        getenv('DB_USER'), 
        getenv('DP_PASS')
    );
}

// Create the factory
$container['repositoryFactory'] = function($c) {
    //Set the cache directory and dev mode flag according to your environment:
    $repositoryFactory = new RepositoryFactory($c['pdo'], '/var/cache', false);
    return $repositoryFactory;
});

// Create a standard Objectiphy repository 
$container['myPolicyRepository'] = function($c) {
    return $c['repositoryFactory']->createRepository(MyPolicy::class);
});

// Create a custom repository that extends the Objectiphy one
$container['myCustomPolicyRepository'] = function($c) {
    return $c['repositoryFactory']->createRepository(
        MyPolicy::class, 
        MyPolicyRepository::class
    );
});

Quick Plain Old PHP Configuration

If you are not using a dependency injection container, the equivalent PHP code to create a repository is as follows:

use Objectiphy\Objectiphy\Factory\RepositoryFactory;
use MyNamespace\Entity\MyEntity;

// Standard Objectiphy repository for a known entity
$pdo = new \PDO(
    "mysql:host=localhost;dbname=dbname", 
    getenv('DB_USER'), 
    getenv('DB_PASS')
);
//Set the cache directory and dev mode flag according to your environment:
$repositoryFactory = new RepositoryFactory($pdo, '/var/cache', false);
$repository = $repositoryFactory->createRepository(Policy::class);
use Objectiphy\Objectiphy\Factory\RepositoryFactory;

// Standard Objectiphy repository for an entity whose type 
// will be determined at runtime
$pdo = new \PDO(
    "mysql:host=localhost;dbname=dbname", 
    getenv('DB_USER'), 
    getenv('DB_PASS')
);
$repositoryFactory = new RepositoryFactory($pdo);
$repository = $repositoryFactory->createRepository();
use Objectiphy\Objectiphy\Factory\RepositoryFactory;
use MyNamespace\Entity\MyEntity;
use MyNamespace\Repository\MyPolicyRepository;

// Custom Objectiphy repository for a known entity
$pdo = new \PDO(
    "mysql:host=localhost;dbname=dbname", 
    getenv('DB_USER'), 
    getenv('DB_PASS')
);
$repositoryFactory = new RepositoryFactory($pdo);
$repository = $repositoryFactory->createRepository(
    Policy::class, 
    MyPolicyRepository::class
);

Quick Introduction to Mapping

There are detailed pages about all of the mapping options available, but to get you started, in Objectiphy, like Doctrine, you can use either PHP attributes, or annotations (ie. PHP docblock comments) to define how to map data from database tables and columns into entities and their properties (and vice-versa). If you use attributes (only available in PHP 8.0 and above), annotations will be ignored - no need to use both (annotations are only necessary for PHP 7.4 or if you have not updated your entities to use attributes yet).

Objectiphy can understand the most commonly used Doctrine attributes and annotations, but it also has its own, which will take precedence over Doctrine. This means you can use existing Doctrine entities with their existing annotations, and if you need to override something for Objectiphy (for example to use a feature of Objectiphy that is not available in Doctrine), you can simply add a new Objectiphy attribute or annotation without breaking any code that might still be using Doctrine.

Here are some basic examples of how an entity looks with attributes and annotations firstly for Objectiphy, and then for Doctrine:

Example 1 - Objectiphy Mapping Definition


namespace Example\Entity;

use Objectiphy\Objectiphy\Mapping\Table;
use Objectiphy\Objectiphy\Mapping\Column
use Objectiphy\Objectiphy\Mapping\Relationship;

 #[Table(name:'insurance.policy')]
class Policy
{
    #[Column(isPrimaryKey: true)]
    public int $id;
    
    #[Column]
    public string $policyNo;
    
    #[Relationship(
        childClassName: Customer::class,
        sourceJoinColumn: 'policyholder_id',
        relationshipType: 'one_to_one',
        joinType: 'INNER'
    )]
    public Customer customer;
    
     #[Relationship(
         childClassName: Claim::class,
         relationshipType: 'one_to_many',
         mappedBy: 'policy'
     )]
    protected array $claims;
}

Example 2 - Doctrine Mapping Definition


namespace Example\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

#[ORM\Entity(
    repositoryClass: PolicyRepository::class,
)]
#[ORM\Table(name: 'newdriver.policy')]
class Policy
{
    #[ORM\Id]
    #[ORM\Column(type: 'integer')]
    #[ORM\GeneratedValue(strategy: 'AUTO')]
    protected int $id;
    
    #[ORM\Column(type: 'string', name: 'policy_no')]
    protected string $policyNo;
    
    #[ORM\OneToOne(targetEntity: Customer::class)]
    #[ORM\JoinColumn(name: 'policyholder_id')]
    protected Customer $customer;
    
    #[ORM\OneToMany(
        targetEntity: Claim::class, 
        mappedBy: 'policy'
    )]
    protected ArrayCollection $claims;
}

Objectiphy would be able to load and hydrate the properties for both of the above entities in exactly the same way (note: for to-many relationships, you can use a Doctrine ArrayCollection if you wish - or your own custom collection class, but you can also use a plain array as shown above). Also note that in Objectiphy, properties can be public, protected, or private (whereas Doctrine prefers that you only use protected or private properties). For more detailed information on the mapping options available, please refer to the section on Mapping Providers.

Last updated