Page 1 of 1

ORMizer :)

Posted: Wed Jan 06, 2010 9:43 am
by VladSun
Well, it happened to me - I need an XML data storage %$#
So ... I've decided to write my own ORM like library (using my style weirdnesses :twisted: )
It's still an alpha version, so I'll not provide the code (which will happen after I have the first RC ready in "Coding critique"). Instead I'll provide you its usage.

First my example domain object:

Code: Select all

<?php
 
/**
 * @orm-table   user
 * @orm-storage XML
 * 
 * @orm_storage-file        ./data/db.xml
 * @orm_storage-encoding    UTF-8
 */
class User
{
    /**
     * @orm-column  id
     * @orm-type    int
     * @orm-length  11
     * @orm-primary-key
     * @orm-autoincrement
     */
    public $user_id;
 
    /**
     * @orm-column  fk_id
     * @orm-type    int
     * @orm-length  11
     * @orm-primary-key
     * @orm-autoincrement
     */
    public $friend_id;
 
    /**
     * @orm-column  username
     * @orm-type    char
     * @orm-length  32
     */
    public $user_username;
 
    /**
     * @orm-column  userpass
     * @orm-type    char
     * @orm-length  32
     */
    public $user_password;
 
    /**
     * @orm-column  salt
     * @orm-type    char
     * @orm-length  2
     */
    public $salt = null;
 
    /**
     * @orm-column  secret
     * @orm-type    char
     * @orm-length  20
     */
    public $secret = '';
 
    /**
     * @orm-column  usermap
     * @orm-type    char
     * @orm-length  32
     * @orm-nullable
     */
    public function map()
    {
        return 'map';
    }
 
    /**
     * @return              string Salt'n'pepper password
     * @orm-mutator         user_password
     * @orm-mutator-mode    write
     */
    public function saltAndPepper()
    {
        return $this->user_password.$this->user_username.$this->salt;
    }
 
    /**
     * @return              string Encrypted secret
     * @orm-mutator         secret
     * @orm-mutator-mode    write
     */
    public function encrypt()
    {
        return $this->secret.'encrypted';
    }
 
    /**
     * @return              string Decrypted secret
     * @orm-mutator         secret
     * @orm-mutator-mode    read
     */
    public function decrypt()
    {
        return substr($this->secret, 0, -strlen('encrypted'));
    }
 
    public function __construct()
    {
        $this->salt = rand(10,99);
    }
 
}
You must already noticed the big number of strange PHPDoc comments

Next my index.php:

Code: Select all

 
require_once 'Doconfigurable.php';
require_once('ORM.php');
 
require_once 'User.php';
 
$user1 = new User();
$user1->user_username = 'username1';
$user1->user_password = 'password1';
$user1->secret = 'secret1!';
 
$user2 = new User();
$user2->user_username = 'username2';
$user2->user_password = 'password2';
$user2->secret = 'secret2!';
 
// Insert
ORM::save($user1);
ORM::save($user2);
 
// If the object has a complete PK field set
//ORM::delete($user1);
//
//$user1->user_username = 'username3';
////Update
//ORM::save($user1);
//
//$user3 = new User();
//$user3->id = 2;
//$user3->friend_id = 2;
//$user3 = ORM::load($user3);
 
$rows = ORM::collection('User')->get();
 
print_r($rows);
Its output:

Code: Select all

Array
(
    [0] => User Object
        (
            [user_id] => 1
            [friend_id] => 1
            [user_username] => username1
            [user_password] => password1username149
            [salt] => 49
            [secret] => secret1!
            [map] => map
        )
 
    [1] => User Object
        (
            [user_id] => 2
            [friend_id] => 2
            [user_username] => username2
            [user_password] => password2username214
            [salt] => 14
            [secret] => secret2!
            [map] => map
        )
 
)
and the generated XML "database" file:

Code: Select all

<?xml version="1.0" encoding="UTF-8"?><db>  <users id="3" fk_id="3">    <user id="1" fk_id="1">      <username>username1</username>      <userpass>password1username149</userpass>      <salt>49</salt>      <secret>secret1!encrypted</secret>      <usermap>map</usermap>    </user>    <user id="2" fk_id="2">      <username>username2</username>      <userpass>password2username214</userpass>      <salt>14</salt>      <secret>secret2!encrypted</secret>      <usermap>map</usermap>    </user>  </users></db>
I think, it's clear enough what I am doing :)

Thoughts?

PS:
Some of the features:
- composite primary keys allowed;
- autoincrement columns allowed (should be a PK);
- not nullable fields restrictions;
- type/length validation;
- possible to have an SQL "storage";
TODO:
- foreign keys and constraints;
- collection filters;

Re: ORMizer :)

Posted: Wed Jan 06, 2010 12:35 pm
by Weirdan
And how do you plan to solve concurrency issues? By locking entire file?

Re: ORMizer :)

Posted: Wed Jan 06, 2010 12:37 pm
by VladSun
Well, I plan to use this in a software for "embedded" devices - i.e. most probably only one administrator registered/logged. So, file locking is OK :)
In my particular case, it's a software for a network administration.

Also, it may be considered that putting all of the data into the memory is not a problem (because it's a small piece of data and concurrency is not a problem).

Re: ORMizer :)

Posted: Wed Jan 06, 2010 3:47 pm
by VladSun
In fact, I can't see any other solution for concurrency issues, other than file locking in this case - maybe some kind of CVS :) ... Putting data in memory involves even more concurrency issues ...

Re: ORMizer :)

Posted: Wed Jan 06, 2010 4:23 pm
by Christopher
If you "ORM" implement a Lock then that would deal with the concurrency issues.

Re: ORMizer :)

Posted: Thu Jan 07, 2010 6:24 am
by VladSun
arborint wrote:If you "ORM" implement a Lock then that would deal with the concurrency issues.
Yes, for the XML "backend storage" it's the obvious solution.

It won't be an issue if an SQL "backend storage" is used.

Changing to SQL backend should be as easy as changing these lines in User.php:

Code: Select all

<?php
 
/**
 * @orm-table   user
 * @orm-storage XML
 * 
 * @orm_storage-file        ./data/db.xml
 * @orm_storage-encoding    UTF-8
 */
class User
{
....
to

Code: Select all

<?php
 
/**
 * @orm-table   user
 * @orm-storage SQL
 * 
 * @orm_storage-db          orm_example
 * @orm_storage-encoding    UTF-8
 */
class User
{
....
Probably, it will be useful if there is a way to set global settings for the ORM class instead of fetching them from PHPDoc comments for every class.

Re: ORMizer :)

Posted: Thu Jan 07, 2010 9:54 am
by VladSun
TODO: ORM configurations (per domain object definition) caching

Re: ORMizer :)

Posted: Thu Jan 07, 2010 1:08 pm
by Christopher
VladSun wrote:It won't be an issue if an SQL "backend storage" is used.
It still is if you need to lock records why the script is doing some processing, and another request could ask to read the record.

Re: ORMizer :)

Posted: Thu Jan 07, 2010 5:49 pm
by VladSun
arborint wrote:
VladSun wrote:It won't be an issue if an SQL "backend storage" is used.
It still is if you need to lock records why the script is doing some processing, and another request could ask to read the record.
So, probably it's better to use transparent timestamp checking in order to detect concurrency changes, instead of locking ...

PS: In fact, I can't see any major difference between my "ORM" and advanced ORM systems like Doctrine - even Doctrine uses the "timestamp" optimistic approach and locking is the pessimistic approach.

Re: ORMizer :)

Posted: Thu Jan 07, 2010 6:47 pm
by Christopher
Those are two good ways to lock records...

Re: ORMizer :)

Posted: Thu Jan 07, 2010 7:32 pm
by VladSun
:) OK, besides concurrency issues and schema caching issues, what other problems do you see in my approach?