Skip to content

Latest commit

 

History

History
404 lines (267 loc) · 9.82 KB

Plan.md

File metadata and controls

404 lines (267 loc) · 9.82 KB

LazyRecord ORM

Requirement

  • Schema export, loader, generator

Configuration File

LazyORM configuration file:

configuration file content:

---
bootstrap:
  - tests/bootstrap.php
schema:
  paths:
    - tests/schema
data_sources:
  default:
    dsn: 'sqlite:tests.db'
    # dsn: 'sqlite::memory:'
  slave:
    dsn: 'mysql:host=localhost;dbname=test'
    user: root
    pass: 123123

translate this into php config file build/datasource_config.php.

<?php
return array(
    'source' => array(
        'master' => array( .... )
    )
);

API

Create connection from data source:

$conn = LazyRecord\ORM::getConnection('master');

LazyRecord\ORM::setConnection( $conn );
LazyRecord\ORM::setSchemaLoaderPath( array( 'build/schema' , 'path/to/other/schema' ) );

$gen = new LazyRecord\SchemaBuilder;
$gen->addPath( 'schema/' );
$gen->setTargetPath( 'build/schema' );
$gen->build();

$loader = new SchemaClassLoader;
$loader->load( 'build/schema/classmap.php' );
$loader->register();

$record = new Book;
$collection = new BookCollection;

Generated Schema class:

<?php

class BookSchema extends LazyRecord\BaseSchema
{
    function __construct()
    {
        $this->columns = array( 
            ....
        );
        $this->relations = array(
        
        );
    }
}

Generated Model class:

<?php

class Book extends LazyRecord\Model
{
    // load schema data in __construct


}

Generated Collection class:

<?php
class BookCollection extends LazyRecord\Collection
{

}

Model

$authors = new AuthorCollection;
$authors->find();
$authors->where(); ... etc  (can use sqlbuilder for query)

Schema Columns

  • basic column attributes:
    • type: date, varchar with length, text, integer, float ... etc
    • primary key
  • a schema writer: write pure PHP schema into JSON or YAML
  • type cast.

Schema Loader Process

When Model object created, try to load the ModelSchema file, which is an array reference that handles.

Should have a global cache for schema array, like LazyRecord::schemas[ $class ]

$schema = \LazyRecord\SchemaLoader::load( $class );

PHP Schema

<?php
class AuthorSchema extends LazyORM\SchemaBuilder
{

    function schema()
    {
        $this->column('name')
                ->varchar(30)
                ->isa('string') // default, apply String validator
                ->isa('DateTime')  // DateTime object.
                ->isa('integer') // Integer object.

                ->validator('ValidatorClass')
                ->validator( array($validator,'method') )
                ->validator('function_name')
                ->validator(function($val) { .... })

                ->maxLength(30)
                ->minLength(12)

                ->canonicalizer('CanonicalClass')
                ->default('Default')
                ->validValues( 1,2,3,4,5 )
                ->validValues( array( 'label' => 'value'  ) );

        // type is inherited from author.id column (bigint or string)
        $this->column('publisher_id')
                ->reference('Publisher','id', schema::has_many ); 

    }
}

Relationship

Has one Relation:

just do left join on main table with b.

$author->profile = array( ... ); // create with current author_id

Has many Relation:

just do left join on main table with b.

create:

    $father->children[] = array( ... );   // append one book with current author_id

    /*

    find "children" relationship

    found hasMany relationship

    hasMany "child".

    find hasMany foreight key (fathor_id)

    create nwe child with current fathor_id

    */

search

    $father->children->where()
        ->like( 'name' , '%Bill%' );

    /*

    find "children" relationship

    found hasMany relationship

    hasMany "child".

    find hasMany foreight key (fathor_id)

    select children with where child.fathor_id = self.id

    */

Many to many relation:

create:

$author->books[] = array( 'title' => 'New book' );   


/*
    find "books" relation 

    found many to many relationship:(author => author_books => books)

    find target model (book)

    create new book 

    the "books" is related to "author_books" (book.id belongs to author_books.book_id)

    author_books.author_id belongs to author.id

    create author_book link with (author_id, book_id)
*/


list:

foreach( $author->books as $book ) {

}

find:

    $author->books->find( 123 );   // find 123 in a subset of author books

ManyToMany relation implementation:

tell schema, AuthorBook.author_id is linking to self.id

$authorSchema->addRelation('author_books', 'AuthorBook','author_id',self,'id', SCHEMA::HasMany );

( relation key, 'foreign schema', 'foreign key', 'self key', relation type )

tell schema, create a books accessor for getting from AuthorBook->books:

authors <=> authors_books <=> books

$authorSchema->manyToMany('books', 'author_books', 'book_id', SCHEMA::many_to_many );

( relation key, relation key, relation foreign key )

To handle many to many relationship, here is the flow:

  1. books accessor is created from AuthorBook accessor.
  2. get AuthorBook relation from self object
  3. find self reference (author.id) and foreign reference (author_books.author_id)
  4. left join author_books on author.id = author_books.author_id
  5. get Book relation from AuthorBook model.
  6. find book reference (book.id) and foreign reference (author_books.book_id)
  7. left join books on book.id = author_books.book_id

Here is the code:

To retrieve books from author

foreach( $author->books as $book ) {
    $title = $book->title;     
}

should create a query:

SELECT books.* from books b
    LEFT JOIN author_books ab ON (b.id = ab.book_id)
    LEFT JOIN authors a ON (a.id = ab.author_id)
    where a.id = :author_id

(is ->title through __get faster than native property? or better than getTitle() ? )

Implementation:

class Relation {
    public $key; // relation key
    public $type; // relation type: many to many, has many

    public $selfSchema;
    public $foreignSchema;

    public $selfKey;
    public $foreignKey;
}

class Accessor
{

}

class Model 
{

    /**
     *    relation key => relation object
     */
    public $relations = array();


    function __get($key)
    {
        if( isset($this->relations[ $key ] ) && $r = $this->relations[$key] ) {
            $r->type // many to many or (has many)

        }


    }

    public function author_books()
    {
        $accessor = $thisSchema->getAccessor('author_books'); // which relates to author_books
        $relation = $thisSchema->getRelation( $accessor->getRelationKey() );
        $schema     = $relation->getForeignSchema();
        $selfKey    = $relation->getSelfPk();
        $foreignKey = $relation->getForeignPk();

        $sql = $query->where()
            ->leftJoin( $schema->table )
            ->on()
                ->equal( $selfKey , $foreignKey )->back()->build();
    }

    public function books()
    {
        $accessor = $thisSchema->getAccessor('books'); // which relates to author_books

        $joinQueue = array();

        $relation = $thisSchema->getRelation( $accessor->getRelationKey() );
        $relationSchema = $relation->getForeignSchema();

        $ab_author_pk = $relation->getForeignPk(); // author_books.author_id
        $a_pk = $relation->getSelfPk();    // author.id

        // books relation in author_books, defines: self.book_id => books.id
        $subRelation = $relationSchema->getRelation('books');   
        $subRelationSchema = $subRelation->getForeignSchema();

        $b_pk = $subRelation->getForeignPk();
        $ab_book_pk = $subRelation->getSelfPk();

        $this->select('*')->table( $subRelationSchema->getTable() )
            ->leftJoin( $relationSchema->getTable() )
                    ->on()->equal( $ab_book_pk , $b_pk )
                    ->back()
            ->leftJoin( $this->getTable() )
                    ->on()->equal( $ab_author_pk , $a_pk )
                    ->back();
    }

    function books()
    {
        $accessor = $thisSchema->getAccessor('books'); // which relates to author_books

        $joinQueue = array();

        $relation = $thisSchema->getRelation( $accessor->getRelationKey() );
        $relationSchema = $relation->getForeignSchema();
        $joinQueue[] = array( $relationSchema->getTable(), 
            $relation->getSelfPk(), 
            $relation->getForeignPk() );


        // books relation in author_books, defines: self.book_id => books.id
        $subRelation = $relationSchema->getRelation('books');   
        $subRelationSchema = $subRelation->getForeignSchema();
        $joinQueue[] = array( $relationSchema->getTable(), 
                $subRelation->getSelfPk(), 
                $subRelation->getForeignPk() );

        foreach( $joinQueue as $join ) {
            list( $table, $sKey, $fKey) = $join;
            $this->leftJoin( $table )->on()->equal( $sKey, $fKey );
        }
    }

}

Validation

  • Validation for database
  • Validation for Software (Application data), eg: email, string length, password
    • column name validation.
      • validate insert column names ,type, values
      • validate update column names ,type, values
    • parameter validation.
    • chained validators
    • canonicalizer