- Schema export, loader, generator
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( .... )
)
);
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
{
}
$authors = new AuthorCollection;
$authors->find();
$authors->where(); ... etc (can use sqlbuilder for query)
- 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.
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
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 );
}
}
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:
- books accessor is created from AuthorBook accessor.
- get AuthorBook relation from self object
- find self reference (author.id) and foreign reference (
author_books.author_id
) - left join
author_books
onauthor.id = author_books.author_id
- get Book relation from AuthorBook model.
- find book reference (book.id) and foreign reference (
author_books.book_id
) - left join
books
onbook.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 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
- column name validation.