marcojetson/freckle

A minimalistic ORM built on top of Doctrine DBAL and heavily inspired by Spot2

v1.0 2016-06-22 20:31 UTC

README

Freckle is an Object-Relational-Mapper built on top of Doctrine DBAL.

Freckle is inspired by Spot2.

Build Status Code Climate

Table of contents

Installation

Install with Composer

composer require marcojetson/freckle

Configuration

You can get a connection through the Freckle\Manager class.

$connection = Freckle\Manager::getConnection([
  'driver' => 'pdo_sqlite',
]);

Entities

Entities must extend Freckle\Entity and implement the definition method.

/**
 * @method int getId()
 *
 * @method string getTitle()
 * @method setTitle(string $title)
 *
 * @method string getBody()
 * @method setBody(string $body)
 */
class Post extends Freckle\Entity
{
  public static function definition()
  {
  	return [
      'table' => 'post',
      'fields' => [
        'id' => ['integer', 'sequence' => true, 'primary' => true],
        'title' => 'string',
        'body' => 'string',
      ],
	];
  }
}

Definition

Defining an entity requires a table name and its fields. Fields are defined by an array with mandatory positional parameters and optional named parameters.

[
    string $type,
    mixed default=null, // default value, callables supported!
    bool primary=false,
    bool require=false,
    bool|string sequence=false, // specify sequence name if required by database
]

Generation

Freckle is able to generate entities for you. Use Freckle\Connection::import() to automatically generate mappings for your tables.

foreach ($connection->generate() as $mapping) {
    file_put_contents($mapping->entityClass() . '.php', (string)$mapping);
}

Data manipulation

Interact with your entities using a mapper. You can get a mapper using the previously created connection.

$postMapper = $connection->mapper(Post::class);

Insert

// create entity and insert
$post1 = $postMapper->entity([
  'title' => 'Hello World',
  'body' => 'My very first post',
]);

$postMapper->insert($entity);

// ...or do it in a single step
$post2 = $postMapper->create([
  'title' => 'Lorem Ipsum',
  'body' => 'My second post',
]);

Update

$post2->setTitle('Lorem ipsum dolor');
$postMapper->update($post2);

Not sure if new entity or not? Then use Freckle\Mapper::save().

Delete

$postMapper->delete($post2);

Retrieval

Use Freckle\Mapper::find() to initialize a query

$query = $postMapper->find(['title like' => '% post']);

// queries are lazy, keep attaching parts till ready
$query->not('id', 1)->gte('score', 10);

foreach ($query as $post) {
  echo $post->getName(), PHP_EOL;
}

// or retrieve a single result
$postMapper->find(['id' => 1])->first();

Where operators

Where operators can be appended to field when using Freckle\Query::where() or being executed as query methods.

  • eq, equals, =
  • not, !=
  • gt, greaterThan, >
  • gte, greaterThanOrEquals, >=
  • lt, lessThan, <
  • lte, lessThanOrEquals, <=
  • like
Custom operators

Add your own operators extending Freckle\Operator.

class JsonExists extends Operator
{
  public function __invoke(Query $query, $column, $value = null)
  {
    return 'jsonb_exists(' . $column . ', ' . $query->parameter($value) . ')';
  }
}

Freckle\Operator::add('json_exists', JsonExists::class);

$postMapper->find([
  'properties json_exists' => 'author',
]);

// or use it as a method
$postMapper->find()->json_exists('properties', 'author');

Relations

Related entity retrieval is supported.

/**
 * @method int getId()
 *
 * @method string getBody()
 * @method setBody(string $body)
 *
 * @method int getPostId()
 * @method setPostId(int $postId)
 *
 * @method Post getPost()
 */
class Comment extends Freckle\Entity
{
  public static function definition()
  {
  	return [
      'table' => 'comment',
      'fields' => [
        'id' => ['integer', 'sequence' => true, 'primary' => true],
        'body' => 'string',
        'post_id' => 'integer',
      ],
      'relations' => [
        'post' => ['one', Post::class, ['id' => 'this.id']],
        },
      ],
	];
  }
}

Definition

In the same fashion of fields, defining a relation consist in an array with mandatory positional parameters and optional named parameters.

[
    string $type,
    string $entityClass,
    array $conditions,
    string through=null, // "table.column" for many-to-many relations
    string field='id', // related entity primary column
]