SitePoint Sponsor

User Tag List

Page 4 of 7 FirstFirst 1234567 LastLast
Results 76 to 100 of 171
  1. #76
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren
    Simplest case is the rowversion becomes a hidden field in the form (along with the PK), if editting a row, and thus beyond the scope of the ORM.
    Ah yes, but I realy wanna supply a "complete" sollution to it, for now I'll do with pessimistic locking ;p

    Edit: On the topic of pessimistic locking, i got these methods:

    aquireReadLock();
    aquireWriteLock();
    checkReadLock();
    checkWriteLock();

    should I have one method named "reaseLock()" that releases all locks on an object or realseReadLock() and releaseWriteLock() ? The thing is that the Write/Read versions are "unneeded" as the Write and Read locks are mutally exclusive.

  2. #77
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Ah yes, but I realy wanna supply a "complete" sollution to it, for now I'll do with pessimistic locking ;p

    Edit: On the topic of pessimistic locking, i got these methods:

    aquireReadLock();
    aquireWriteLock();
    checkReadLock();
    checkWriteLock();

    should I have one method named "reaseLock()" that releases all locks on an object or realseReadLock() and releaseWriteLock() ? The thing is that the Write/Read versions are "unneeded" as the Write and Read locks are mutally exclusive.
    acquire

    Yeh, don't see the point of having both.

  3. #78
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Got some more question about pessismistic locking, as it is now there's a possibility to that two rows might aquire the same lock at the same time - allow me to explain:

    * Person1 asks for Write lock on row1 in table1
    * Person2 asks for Write lock on row1 in table1
    * LockManager checks amount of writelocks on row1 for Person1, they equal to = 0
    * LockManager checks amount of writelocks on row1 for Person2, they equal to = 0
    > Now here, both Person1 and Person2 can insert a write lock because they are doing the query at nearly the same instant, about a nano second apart or so. They both choose to insert a writelock
    * LockManager inserts write lock for Person1 on row1
    * LockManager inserts write lock for Person2 on row1
    * LockManager checks amount of writelocks on row1 after insert for Person1 and it
    detects there are now TWO write locks(big no-no)
    * LockManager checks amount of writelocks on row1 after insert for Person2 and it detects there are now TWO write locks(big no-no)
    > Here I can either 1. Drop all locks, or Drop the lock that was aquired the last time by using id field for the locks and delete the one with the lowest id.

    My question are:
    1. Is there any good way around this(ie doing both the check if you can insert a lock and insert the rowin the SAME query) ?
    2. What should I do when it detects two identical locks? Is my sollution above a good one(drop the one with the lowest id).

  4. #79
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Got some more question about pessismistic locking, as it is now there's a possibility to that two rows might aquire the same lock at the same time - allow me to explain:

    * Person1 asks for Write lock on row1 in table1
    * Person2 asks for Write lock on row1 in table1
    * LockManager checks amount of writelocks on row1 for Person1, they equal to = 0
    * LockManager checks amount of writelocks on row1 for Person2, they equal to = 0
    > Now here, both Person1 and Person2 can insert a write lock because they are doing the query at nearly the same instant, about a nano second apart or so. They both choose to insert a writelock
    * LockManager inserts write lock for Person1 on row1
    * LockManager inserts write lock for Person2 on row1
    * LockManager checks amount of writelocks on row1 after insert for Person1 and it
    detects there are now TWO write locks(big no-no)
    * LockManager checks amount of writelocks on row1 after insert for Person2 and it detects there are now TWO write locks(big no-no)
    > Here I can either 1. Drop all locks, or Drop the lock that was aquired the last time by using id field for the locks and delete the one with the lowest id.

    My question are:
    1. Is there any good way around this(ie doing both the check if you can insert a lock and insert the rowin the SAME query) ?
    2. What should I do when it detects two identical locks? Is my sollution above a good one(drop the one with the lowest id).
    Just have a unique/primary key on the lock table, such that its impossible to insert another lock after something is already locked.

    So always attempt the insert when acquiring the lock, and just check for failure, you'll get some sort of unique constraint violation.

  5. #80
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ren: (yes I responded once and deleted it, realizing it was wrong) - this won't work because a row/object can be readlocked multiple times but only writelocked once. So it's not possible to put a primary key on objectype-key-locktype =/.

  6. #81
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Ren: (yes I responded once and deleted it, realizing it was wrong) - this won't work because a row/object can be readlocked multiple times but only writelocked once. So it's not possible to put a primary key on objectype-key-locktype =/.
    Hmm, have to use INSERT INTO ... SELECT syntax then, so can add WHERE condition, to return 0 rows when an write lock active or read locks when wanting a write.

    Bit of an odd one.. but


    INSERT INTO locks(owner, `table`, `key`, `type`)
    SELECT :owner, :table, :key, :type
    FROM (SELECT 1) as t
    WHERE
    NOT EXISTS(SELECT 1 FROM locks l WHERE l.table = :table AND l.key = :key AND
    (l.type = 'w' OR (:type = 'w' AND l.type = 'r')));

  7. #82
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren
    Hmm, have to use INSERT INTO ... SELECT syntax then, so can add WHERE condition, to return 0 rows when an write lock active.

    INSERT INTO Locks(table, owner, lockType)
    SELECT 'Person', 'me', 'read'
    FROM Locks
    WHERE ...
    Hmm, how does this help ? Or how would you suggest a complete query for this table would look:

    Code:
    CREATE TABLE `Locks` (
      `owner` varchar(26) NOT NULL,
      `obj_type` varchar(25) NOT NULL,
      `key` varchar(255) NOT NULL,
      `lock` tinyint(1) NOT NULL,
      PRIMARY KEY  (`owner`,`obj_type`,`key`,`lock`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    
    INSERT INTO `Locks` VALUES ('thr', 'Person', 'id:3', 2);

  8. #83
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, this is what I tried after the edit of your reply, here we go:
    Code:
    INSERT INTO `Locks`(`owner`, `type`, `key`, `lock`)
    SELECT `owner`, `type`, `key`, `lock`
    FROM `Locks` as t
    WHERE
    NOT EXISTS(SELECT 1 FROM `Locks` as l WHERE l.`type` = 'Person'  AND l.`key` = 'id:3' AND
    (l.`lock` = 2 OR (`type` = 2 AND l.`lock` = 1)));
    (obj_type (from sql dump) == type (in this query)) But this just tries to insert your own rows again.

  9. #84
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    INSERT INTO locks(`owner`, `obj_type`, `key`, `lock`)
    SELECT 'thr', 'Person', 'id:3', 2
    FROM (SELECT 1) as t
    WHERE
    NOT EXISTS(SELECT 1 FROM locks l WHERE l.obj_type = 'Person' AND l.key = 'id:3' AND
    (l.lock = 2 OR (2 = 2 AND l.lock = 1)));

    Assume 2 means write, and 1 is read

  10. #85
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    ok, that's some serious sql magic ;p going to try it out and yes u got it right. 2 = write, 1 = read.

  11. #86
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    In one case, I used "super" pessismistic locking. What I do is check the row on update. If anyone else has updated the row in the last few hours, it is still considered "locked" by them. First in wins, and they get to continue have exclusive rights to update it for a period of time after they perform their last update. Once a record becomes locked, it no longer even shows up in the "available to edit" lists for other users. This worked for me because it was the business process the users wanted, and was a fairly simple technical solution.
    Jason Sweat ZCE - jsweat_php@yahoo.com
    Book: PHP Patterns
    Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI
    Detestable (adjective): software that isn't testable.

  12. #87
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Just a quick question to all of you: Which type of inheritance mapping would you prefer?
    1. Single Table Inhertiance (PoEAA p.278) (One table for all classes)
    2. Class Table Inheritance (PoEAA p.285) (Different tables for all classes with only the "extra" fields that are added in a subclass in each sub-class table)
    3. Concrete Table Inheritance (PoEAA p.293) (Different tables for all classes with all fields for one class in that table)

    So which one is the lesser of three evils?
    I don't think you can compare them. Some situations would make one approach the best choice, while other situations would call for another strategy.
    Personally I prefer the Class Table Inheritance approach, but I aknowledge that there are situations where it would be impractical. It's not _that_ hard to implement, but I haven't seen any php orm's do it (apart from something I put together myself), which I find odd.

  13. #88
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, i made that work, the correct query for writes is this:.

    Code:
    INSERT INTO Locks(`owner`, `type`, `key`, `lock`)
    SELECT 'thr', 'Person', 'id:3', 2
    FROM (SELECT 1) as t
    WHERE
    NOT EXISTS(SELECT 1 FROM Locks l WHERE l.type = 'Person' AND l.key = 'id:3' AND ( l.lock = 2 OR l.lock = 1) );
    that is some (as I said) serious magic thank you so much - I gotta brush up my SQL skill thank you ren - you'll get a spot in credits for that one (and all other posts)

  14. #89
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sweatje
    In one case, I used "super" pessismistic locking. What I do is check the row on update. If anyone else has updated the row in the last few hours, it is still considered "locked" by them. First in wins, and they get to continue have exclusive rights to update it for a period of time after they perform their last update. Once a record becomes locked, it no longer even shows up in the "available to edit" lists for other users. This worked for me because it was the business process the users wanted, and was a fairly simple technical solution.
    Ok, strange approach (it feels like for me atleast) - good to have in mind tho.


    Quote Originally Posted by kyberfabrikken
    I don't think you can compare them. Some situations would make one approach the best choice, while other situations would call for another strategy.
    Personally I prefer the Class Table Inheritance approach, but I aknowledge that there are situations where it would be impractical. It's not _that_ hard to implement, but I haven't seen any php orm's do it (apart from something I put together myself), which I find odd.
    Your probably right that yeah they're not fair to compare to eachother - problem is tho that I don't wanna implement all of them and wanted to give a go at one of them first.


    Ren: One more q, is that SQL uniform or MySQL specific?

  15. #90
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr

    Ren: One more q, is that SQL uniform or MySQL specific?
    Standard SQL. Just older versions of MySQL4 won't handle the subqueries. Though could possibly remove them, but becomes more of a hack

  16. #91
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, thank you :] gotta admit there's no way I'd figure out that query by my own.

  17. #92
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, I got my read/write lock implementation to work - and following fowlers examples in PoEAA this is how it works:

    • Read and write are mutually excklusive, so if an object is under read lock none can write lock it and if it's under write lock none can readlock it.
    • Multiple reads are ok, but not multiple writes.


    Seems good?

  18. #93
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, here's a new set of testcases for you (if anyone care to look) - here's the .phps file(the server is insanely slow, so be patient when you look in the .phps or run the tests): http://mirkk.nu/orm/v18/UnitTests/tests.phps here you can see the tests run http://mirkk.nu/orm/v18/UnitTests/tests.php - and if u dont wanna go to an external site, here's the source:
    PHP Code:
    <?php
    require_once('../Dev/dev_funcs.php');
    require_once(
    '../Dev/PDOBench.php');
    ra('../ORM/Interfaces/');
    ra('../ORM/');
    ra('../Objects/');
    require(
    'simpletest/unit_tester.php');
    require(
    'simpletest/reporter.php');
    require(
    'simpletest/reporter_showpasses.php');


    /**
     * Host, Username, Password and Database for tests:
     */
    define('DBHOST','localhost');
    define('DBUSER','secret <_<');
    define('DBPASS','secret >_>');
    define('DBNAME','development');

    /* NOTICE: These tests will drop/create the tables named 
       "Groups_test","Persons_test","Memeberships_test", "Posts_test",
       "Log_test", "Locks_test" and "Presentations_test" on the
       database the tests are pointed at - these tests should
       NOT be run on anything else then a database used for
       testing purposes. If you don't know what something of the above
       means - don't run the tests
       
       NOTICE: These test will only work on a mysql database.

       To get the tests to run, define RUN_TESTS to true
    */

    define('RUN_TESTS',true);


    if(
    RUN_TESTS === true){

        abstract class 
    ORMBaseTest extends UnitTestCase{
        
            function 
    ORMBaseTest(){
                
    // This is used in setUp() and tearDown()
                
    $this->pdo = new PDO("mysql:host=" DBHOST ";dbname=" DBNAMEDBUSERDBPASS);
                
                
    // This is used by the session
                
    $this->pdoBench = new PDOBench("mysql:host=" DBHOST ";dbname=" DBNAMEDBUSERDBPASS);
                
    // Session::__construct(PDO $pdo, $mapperPath, $classPath, $sqlEscapeChar = '`');
                
    $this->session = new Session($this->pdoBench'../Mappers/''../Classes/''`');
            }
        
            function 
    setUp(){
                
    $table = array();
                
    $table[] = 'CREATE TABLE `Groups_test` (
                  `id` int(11) NOT NULL auto_increment,
                  `name` varchar(50) NOT NULL,
                  `comment` varchar(255) NOT NULL,
                  PRIMARY KEY  (`id`)
                ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1'
    ;
                
                
    $table[] = 'CREATE TABLE `Locks_test` (
                  `owner` varchar(32) NOT NULL,
                  `type` varchar(25) NOT NULL,
                  `key` varchar(255) NOT NULL,
                  `lock` tinyint(1) NOT NULL,
                  PRIMARY KEY  (`owner`,`type`,`key`,`lock`)
                ) ENGINE=InnoDB DEFAULT CHARSET=latin1'
    ;
                
                
    $table[] = 'CREATE TABLE `Log_test` (
                  `id` int(11) NOT NULL auto_increment,
                  `time` int(11) NOT NULL,
                  `ip` varchar(15) NOT NULL,
                  `uri` varchar(255) NOT NULL,
                  PRIMARY KEY  (`id`)
                ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1'
    ;
                
                
    $table[] = 'CREATE TABLE `Memberships_test` (
                  `person` int(11) NOT NULL,
                  `group` int(11) NOT NULL,
                  PRIMARY KEY  (`person`,`group`)
                ) ENGINE=InnoDB DEFAULT CHARSET=latin1'
    ;
                
                
    $table[] = 'CREATE TABLE `Persons_test` (
                  `id` int(11) NOT NULL auto_increment,
                  `name` varchar(50) NOT NULL,
                  `email` varchar(50) NOT NULL,
                  `parent` int(11) NOT NULL,
                  `savings` int(11) NOT NULL,
                  PRIMARY KEY  (`id`),
                  UNIQUE KEY `name` (`name`),
                  UNIQUE KEY `name_2` (`name`)
                ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1'
    ;
                
                
    $table[] = 'CREATE TABLE `Posts_test` (
                  `id` int(11) NOT NULL auto_increment,
                  `subject` varchar(50) NOT NULL,
                  `author` int(11) NOT NULL,
                  `text` text NOT NULL,
                  `date` int(11) NOT NULL,
                  PRIMARY KEY  (`id`)
                ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1'
    ;
                
                
    $table[] = 'CREATE TABLE `Presentations_test` (
                  `owner` int(11) NOT NULL,
                  `text` text NOT NULL,
                  PRIMARY KEY  (`owner`)
                ) ENGINE=InnoDB DEFAULT CHARSET=latin1'
    ;
                
                
    $data = array();
                
    $data[] = "INSERT INTO `Groups_test` VALUES (1, 'BOFH', '******* Operators From Hell');";
                
    $data[] = "INSERT INTO `Groups_test` VALUES (2, 'Users', 'BOFH victims...');";
                
    $data[] = "INSERT INTO `Log_test` VALUES (1, 21525134, '127.0.0.1', 'http://www.mirkk.nu/index.php');";
                
    $data[] = "INSERT INTO `Log_test` VALUES (2, 215125123, '192.168.0.1', 'http://www.mirkk.nu/xxx');";
                
    $data[] = "INSERT INTO `Memberships_test` VALUES (1, 1);";
                
    $data[] = "INSERT INTO `Memberships_test` VALUES (2, 2);";
                
    $data[] = "INSERT INTO `Memberships_test` VALUES (3, 2);";
                
    $data[] = "INSERT INTO `Persons_test` VALUES (1, 'Fredrik', 'thrthr@gmail.com', 2, 20000);";
                
    $data[] = "INSERT INTO `Persons_test` VALUES (2, 'Madeleine', 'censored@unkown.host', 3, 2000);";
                
    $data[] = "INSERT INTO `Persons_test` VALUES (3, 'Lillis', 'not_know@no.host', 1, 100);";
                
    $data[] = "INSERT INTO `Posts_test` VALUES (1, 'First post!!', 1, 'Yay, first post!', 1253623);";
                
    $data[] = "INSERT INTO `Posts_test` VALUES (2, 'How about this?', 3, 'This is my first post /cheer!', 0);";            
                
    $data[] = "INSERT INTO `Presentations_test` VALUES (1, 'This is fredriks presentation, have a beer and take a seat ;).');";
                
    $data[] = "INSERT INTO `Presentations_test` VALUES (2, 'And here''s madeleins, cheers ;)');";
                
                foreach(
    $table as $t$this->pdo->query($t);
                foreach(
    $data as $d$this->pdo->query($d);
                
            }
            
            public function 
    tearDown(){
                
    $this->pdo->query('DROP TABLE `Groups_test`, `Memberships_test`, `Persons_test`, `Posts_test`, `Log_test`, `Presentations_test`, `Locks_test`');
            }
            
        }

        class 
    TestObjectRelationalMapping extends ORMBaseTest{
        
            function 
    testFindBySql(){
                list(
    $fredrik) = $this->session->person->findBySql('select Person.* from Person where id = ?', array(1));
                
    $this->assertIsA($fredrik"Person");
            }
            
            function 
    testFindByFields(){
                list(
    $fredrik) = $this->session->person->findByFields(array('id' => 1));
                
    $this->assertIsA($fredrik"Person");
            }
            
            function 
    testFindByPk(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertIsA($fredrik"Person");
            }
            
            function 
    testFindByStmt(){
                
    $stmt $this->pdoBench->prepare("select Persons_test.* from Persons_test where id = ?");
                list(
    $fredrik) = $this->session->person->findByStmt($stmt, array(1));
                
    $this->assertIsA($fredrik"Person");
            }
            
            function 
    testObjectsAreUnique(){
                list(
    $fredrik1) = $this->session->person->findByPk(1);
                list(
    $fredrik2) = $this->session->person->findByPk(1);
                
    $this->assertEqual(strval($fredrik1), strval($fredrik2));
            }
            
            function 
    testFindMultipleBySql(){
                
    $persons $this->session->person->findBySql("select Person.* from Person where name like '%i%'");
                
    $this->assertEqual(count($persons), 3);
                foreach(
    $persons as $person)
                    
    $this->assertIsA($person"Person");
            }
            
            function 
    testValueObjects(){
                list(
    $madeleine) = $this->session->person->findBySql("select Person.* from Person where name = ?", array('Madeleine') );
                
    $this->assertIsA($madeleine->savings"Money");
                
    $this->assertIsA($madeleine->savings"ValueObject");
            }
            
            function 
    testHasOneRelationship(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertIsA($fredrik->pres"Presentation");
            }
            
            function 
    testBelongsToRelationship(){
                list(
    $pres) = $this->session->presentation->findBySql('select Presentation.* from Presentation where owner = ?', array(1));
                
    $this->assertIsA($pres'Presentation');
                
    $this->assertIsA($pres->owner"Person");
            }
            
            function 
    testHasManyRelationship(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertIsA($fredrik->posts'DomainCollection');
                foreach(
    $fredrik->posts as $post)
                    
    $this->assertIsA($post'Post');
            }
            
            function 
    testManyToManyNamedWithIntermediateRelationship(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertIsA($fredrik->memberships'DomainCollection');
                foreach(
    $fredrik->memberships as $membership){
                    
    $this->assertIsA($membership'Membership');
                    
    $this->assertIsA($membership->group"Group");
                }
            }
            
            function 
    testCreateInCollection(){
                list(
    $fredrik1) = $this->session->person->findByPk(1);
                for(
    $i 0;$i 30$i++){
                    
    $post $fredrik1->posts->create();
                    
    $post->text "text...";
                    
    $post->subject "subject...";
                    
    $post->date time();
                }
                
    $this->session->getActiveTransaction()->commit();
                
                
    $newSession = new Session($this->pdoBench'''');
                list(
    $fredrik2) = $newSession->person->findByPk(1);
                
    $this->assertNotEqual(strval($fredrik1), strval($fredrik2));
                
    $this->assertEqual($fredrik2->posts->count(),31);
            }
            
            function 
    testUpdatesAreSaved(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $newSession = new Session($this->pdoBench'''');
                
    $fredrik->email "mynew@email.com";
                
    $this->session->getActiveTransaction()->registerDirty($fredrik);
                
    $this->session->getActiveTransaction()->commit();
                
                list(
    $fredrik2) = $newSession->person->findByPk(1);
                
    $this->assertNotEqual(strval($fredrik), strval($fredrik2));
                
    $this->assertEqual($fredrik2->email'mynew@email.com');
                
    $this->assertEqual($fredrik2->email$fredrik->email);
            }
            
            function 
    testMultipleTransactionsAreSeparate(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $fredrik->name "thr";
                
    $this->session->getActiveTransaction()->registerDirty($fredrik);
                
                list(
    $madeleine) = $this->session->person->findByPk(2);
                
    $madeleine->name "maddis";
                
    $this->session->setActiveTransaction($this->session->createTransaction('second'));
                
    $this->session->getActiveTransaction()->registerDirty($madeleine);
                
                
    // Commit first transaction
                
    $this->session->getTransactionByName('default')->commit();
                
                
    // Get a new session
                
    $newSession = new Session($this->pdoBench'''');
                list(
    $fredrik2) = $newSession->person->findByPk(1);
                list(
    $madeleine2) = $newSession->person->findByPk(2);
                
                
    $this->assertEqual($fredrik2->name$fredrik->name);
                
    // Should not be equal cos 2nd transaction is not commited
                
    $this->assertNotEqual($madeleine2->name$madeleine->name);
                
            }
            
            function 
    testSimpleObjectWithoutMapperLog(){
                list(
    $log1$log2) = $this->session->log_test->findByFields();
                
    $this->assertIsA($log1"Log_test");
                
    $this->assertIsA($log2"Log_test");
            }
            
            function 
    testDisplayQueries(){
                echo 
    "<br> PDO Queries/Prepared Statements: " $this->pdoBench->queryAmount() . "<br /><br />";
            }
            
        }
        
        class 
    TestPessimisticLockingSeperateFromSession extends ORMBaseTest{

            function 
    setUp(){
                
    parent::setUp();
                if(!isset(
    $this->lm1) || !isset($this->lm2)){
                    
    // trick that gives  you two unique sess ids and two different lockers
                    
    session_id(md5((string)microtime())); session_start(); 
                     
    // PessimisticLockManager::__construct(Session $session,          PDOProxy $pdo,        $lockTable);
                    
    $this->lm1 = new PessimisticLockManager($this->session, new PDOProxy($this->pdoBench'`'), 'Locks_test');
                    
    session_destroy(); session_id(md5((string)microtime())); session_start();
                    
    $this->lm2 = new PessimisticLockManager($this->session, new PDOProxy($this->pdoBench'`'), 'Locks_test');
                    
    session_destroy();
                }
            }
            
            function 
    testReadLock(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertTrue($this->lm1->readLock($fredrik));
            }
            
            function 
    testWriteLock(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertTrue($this->lm1->writeLock($fredrik));
            }
            
            function 
    testCheckReadLock(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->lm1->readLock($fredrik);
                
    $this->assertTrue($this->lm1->checkReadLock($fredrik));
            }
            
            function 
    testCheckWriteLock(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->lm1->writeLock($fredrik);
                
    $this->assertTrue($this->lm1->checkWriteLock($fredrik));
            }
            
            function 
    testMultipleReadsAreOk(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertTrue($this->lm1->readLock($fredrik));
                
    $this->assertTrue($this->lm2->readLock($fredrik));
            }
            
            function 
    testMultipleWritesAreNotOk(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertTrue($this->lm1->writeLock($fredrik));
                
    $this->assertFalse($this->lm2->writeLock($fredrik));
            }
            
            function 
    testCantLockWriteWhenReadIsActive(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertTrue($this->lm1->readLock($fredrik));
                
    $this->assertFalse($this->lm2->writeLock($fredrik));
            }
            
            function 
    testCantLockReadWhenWriteIsActive(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertTrue($this->lm1->writeLock($fredrik));
                
    $this->assertTrue($this->lm1->checkWriteLock($fredrik));
                
    $this->assertFalse($this->lm2->readLock($fredrik));
                
    $this->assertFalse($this->lm2->checkReadLock($fredrik));
            }
            
            function 
    testWriteLockAlsoMakesReadLockReturnTrue(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertTrue($this->lm1->writeLock($fredrik));
                
    $this->assertTrue($this->lm1->checkWriteLock($fredrik));
                
    $this->assertTrue($this->lm1->checkReadLock($fredrik));
            }
            
            function 
    testReadLockMakesWriteLockReturnFalse(){
                list(
    $fredrik) = $this->session->person->findByPk(1);
                
    $this->assertTrue($this->lm1->readLock($fredrik));
                
    $this->assertTrue($this->lm1->checkReadLock($fredrik));
                
    $this->assertFalse($this->lm1->checkWriteLock($fredrik));
            }
            
            function 
    testCanLockDifferentObjects(){
                list(
    $madeleine$fredrik$lillis) = $this->session->person->findByFields();
                
    $this->assertTrue($this->lm1->readLock($madeleine));
                
    $this->assertTrue($this->lm1->readLock($fredrik));
                
    $this->assertTrue($this->lm2->writeLock($lillis));
            }
            
            function 
    testComplexLocking(){
                list(
    $madeleine$fredrik$lillis) = $this->session->person->findByFields();
                
    $this->assertTrue($this->lm1->readLock($madeleine)); // ok non has locked madeleine
                
    $this->assertTrue($this->lm1->readLock($fredrik)); // ok none has locked fredrik
                
    $this->assertTrue($this->lm2->writeLock($lillis)); // ok, none has locked lillis
                
    $this->assertTrue($this->lm1->writeLock($madeleine)); // ok, you have readlock on madeleine
                
    $this->assertTrue($this->lm2->readLock($fredrik)); // ok, lm1 only has readlock on fredrik
                
    $this->assertFalse($this->lm1->writeLock($lillis)); // false, lm2 has writelock on lillis
                
    $this->assertFalse($this->lm2->writeLock($fredrik)); // false, lm1 has readlock on fredrik
                
    $this->assertFalse($this->lm1->writeLock($fredrik)); // false lm2 has readlock on fredrik
                
    $this->assertFalse($this->lm2->readLock($madeleine)); // false lm1 has writelock on madeleine
            
    }
            
            function 
    testDisplayQueries(){
                echo 
    "<br> PDO Queries/Prepared Statements: " $this->pdoBench->queryAmount() . "<br /><br />";
            }

        }
        
        class 
    TestLockingManagerFromSession extends ORMBaseTest{
            
    /**
              * No tests written
               */
        
    }
        
        class 
    TestImplicitPessimisticLockingMapper extends ORMBaseTest{
            
    /**
             * Tests not written yet, 
             */
        
    }
        
        
    ob_start();
        
    $tests = new GroupTest('Object Relational Mapping Test Suit');
        
    $tests->addTestCase(new TestObjectRelationalMapping());
        
    $tests->addTestCase(new TestPessimisticLockingSeperateFromSession());
        
    $tests->addTestCase(new TestLockingManagerFromSession());
        
    $tests->addTestCase(new TestImplicitPessimisticLockingMapper());
        
    $tests->run(new ShowPasses());
        
    ob_end_flush();

    }
    ?>

  19. #94
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Got some thoughts about how to handle configuration - not talking mapping here -, configuration of the Session/LockManager/etc, for stuff like paths, filenames, etc. How do you guys usually handle this? Currently I got some ideas - which are:

    1. Seperate XML file, such as:
    Code:
    <?xml version="1.0" encoding="iso-8859-1"?>
    <configuration>
    	<locking>
    		<type>Pessimistic</type> <!-- Type of locking to use -->
    		<table name="Locks">
    			<owner>owner</owner> <!-- Col. in table that holds lock owner -->
    			<type>type</type> <!-- Col. in table that holds which type the object is of -->
    			<key>key</key> <!-- Col. in table that holds the object key -->
    			<lock>lock</lock> <!-- Col. in table that holds type of lock  -->
    		</table>
    	</locking>
    	<paths>
    		<classes>Classes/</classes>
    		<mappers>Mappers/</mappers>
    	</paths>
    	<database type="mysql" escapechar="`">
    		<host>localhost</host>
    		<name>development</name>
    		<user>thr</user>
    		<password>secret</password>
    	</database>
    </configuration>
    2. Extend base class to form configuration, example:
    PHP Code:
    <?php
    class ConfigSession extends Session{

        protected 
    $locking = array(
            
    'type' => self::PESSIMISTIC_LOCKING,
            
    'tablename' => 'Locks',
            
    'tablefields' => array(
                
    'owner' => 'owner',
                
    'type' => 'objecttype',
                
    'key' => 'objectkey',
                
    'lock' => 'lock'
            
    )
        );
        
        protected 
    $paths = array(
            
    'classpath' => 'Classes/',
            
    'mapperpath' => 'Mappers/'
        
    );
        
        protected 
    $database = array(
            
    'type' => 'mysql',
            
    'escchar' => '`',
            
    'connection' => array(
                
    'host' => 'localhost',
                
    'name' => 'development',
                
    'user' => 'thr',
                
    'password' => 'secret'
            
    )
        );

    }
    ?>
    3. Insanely long contstructor arguments that needs to be enterd on every instance (have this now, don't like it).

  20. #95
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Got some thoughts about how to handle configuration - not talking mapping here -, configuration of the Session/LockManager/etc, for stuff like paths, filenames, etc. How do you guys usually handle this?
    All those ideas will work. Dont extend Session though, create a SessionFactory object for now. At least here you move construction into one spot. This will allow you to go in most directions later if you want to. Something like:
    PHP Code:
    class SessionFactory {
        private 
    $dbhost 'localhost';
        private 
    $dbname 'test';
        private 
    $dbuser 'root';
        private 
    $dbpass 'password';
        private 
    $mappersDir '../Mappers/';
        private 
    $classesDir '../Classes/';
        public function 
    createSession() {
            return new 
    Session($this->createDatasource(), $this->mappers$this->classes);
        }
        private function 
    createDatasource() {
            return new 
    PDOBench("mysql:host=$this->dbhost;dbname=$this->dbname"$this->dbuser$this->dbpass);
        }

    A couple other thoughts:

    I think you had the discussion earlier about how having the sql in the unit tests makes the tests easier to read. This is true but now you have all the sql for all the tests in one test and everything is getting a little more confusing. Only create the tables you need for each test. Eg. Only create the locking tables for the locking tests etc.

    About locking, I havent delved too much into this but I would think locking would be more implicit than explicit? A user describes which objects need locking in the metadata and the orm handles this implicitly without a call to a writeLock method. Dont know really. Just somethignt o think about.

    Looking at the test: testUpdatesAreSaved, you make an explicit call to registerDirty. In a UOW this is usually done implicitly on commit.

    Why use list($objectName) when you call findById()? This will always return only one object so list() should be refactored out.

    Anyway, hope you find these comments usefull. Good job btw.

  21. #96
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Brenden Vickery
    All those ideas will work. Dont extend Session though, create a SessionFactory object for now. At least here you move construction into one spot. This will allow you to go in most directions later if you want to. Something like:
    PHP Code:
    class SessionFactory {
        private 
    $dbhost 'localhost';
        private 
    $dbname 'test';
        private 
    $dbuser 'root';
        private 
    $dbpass 'password';
        private 
    $mappersDir '../Mappers/';
        private 
    $classesDir '../Classes/';
        public function 
    createSession() {
            return new 
    Session($this->createDatasource(), $this->mappers$this->classes);
        }
        private function 
    createDatasource() {
            return new 
    PDOBench("mysql:host=$this->dbhost;dbname=$this->dbname"$this->dbuser$this->dbpass);
        }

    Thanks - good point there, going to look more into a factory(maybee even run with a factory that you extend when I release it), something like "abstract class SessionFactory" which you extend to enter your own config variables and such.

    Quote Originally Posted by Brenden Vickery
    I think you had the discussion earlier about how having the sql in the unit tests makes the tests easier to read. This is true but now you have all the sql for all the tests in one test and everything is getting a little more confusing. Only create the tables you need for each test. Eg. Only create the locking tables for the locking tests etc.
    Ah yes, but all tables are needed for all tests.

    Quote Originally Posted by Brenden Vickery
    About locking, I havent delved too much into this but I would think locking would be more implicit than explicit? A user describes which objects need locking in the metadata and the orm handles this implicitly without a call to a writeLock method. Dont know really. Just somethignt o think about.
    Yes, you're right and if you look closely I have a test case named TestImplicitPessimisticLockingMapper at the bottom which isn't written yet, I'm still trying to come down with a good solution for implicit locking. Altho most read/write locking realy has to be explicit but the checking (ie to check for writelock before update) should be implicit(if you want it to be).

    Quote Originally Posted by Brenden Vickery
    Looking at the test: testUpdatesAreSaved, you make an explicit call to registerDirty. In a UOW this is usually done implicitly on commit.
    Ah yes, I have not implemented any type of autoregistering for dirty objects and while I will have some interface that you can implement and have the current Transaction fed to your object so you can call registerDirty() from inside the DomainObject.

    Quote Originally Posted by Brenden Vickery
    Why use list($objectName) when you call findById()? This will always return only one object so list() should be refactored out.
    Ah yes, the reason I chose to do this is that I always wanted to return an array when fetching objects, but I guess you're right - going to change it around.

    Quote Originally Posted by Brenden Vickery
    Anyway, hope you find these comments usefull. Good job btw.
    Yes I did, this is exactly the type of feedback I need, thank you .

  22. #97
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I made a quick SessionFactory implementation, here goes(only for .xml for now):
    PHP Code:
    <?php
    class SessionFactory{

        
    // Config variables
        
    protected $database = array();
        protected 
    $locking = array();
        protected 
    $paths = array();

        
    // Singleton =(
        
    protected static $instance;
        protected function 
    __construct(){}
        
        public static function 
    instance(){
            if(
    is_null(self::$instance))
                
    self::$instance = new self;
            return 
    self::$instance;
        }
        
        public function 
    create($xml null){
            if(
    is_string($xml)){
                
    $xml simplexml_load_file($xml);
                
    $pdo = new PDO((string)$xml->database['type'].':host='.(string)$xml->database->host.';dbname='.(string)$xml->database->name,
                               (string)
    $xml->database->username, (string)$xml->database->password);
                
    $pdo = new PDOProxy($pdo, (string)$xml->database['escapechar']);
                
    $session = new Session($pdo, (string)$xml->paths->mappers, (string)$xml->paths->classes);
                if(isset(
    $xml->locking)){
                    switch(
    strtolower((string)$xml->locking->type)){
                        case 
    'pessimistic':
                            
    $table = (string)$xml->locking->table['name'];
                            
    $fields = array(
                                
    'owner' => (string)$xml->locking->table->owner,
                                
    'type' => (string)$xml->locking->table->type,
                                
    'key' => (string)$xml->locking->table->key,
                                
    'lock' => (string)$xml->locking->table->lock
                            
    ); $session->setPessimisticLocking($table$fields);
                            return 
    $session;
                            break;
                        default:
                            throw new 
    RuntimeException('Invalid locking type, only valid is [Pessimistic/pessimistic]');
                            break;
                    }
                }else{
                    return 
    $session;
                }
            }else{
                
    // Use $this->database, $this->locking, $this->paths
            
    }
        }

    }
    ?>
    Looks good?

  23. #98
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    I'm still trying to come down with a good solution for implicit locking.
    Was wondering if you can use a decorator based on the Mapper interface you posted earlier. All the find*() methods lock in pessmistic mode, update, delete adds conditions in optimistic.

  24. #99
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren
    Was wondering if you can use a decorator based on the Mapper interface you posted earlier. All the find*() methods lock in pessmistic mode, update, delete adds conditions in optimistic.
    I'm not going to take the route of an decorator - mostly because it hides to much of the intial object under itself. I'm going to have an extended datamapper for locking purposes.

    And I don't think having implicit locing on find*() methods is such a good idea tbh - as most web applications won't use read locking. It's going to implement implicit checking of write lock before update/delete tho.

  25. #100
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, time to release some code - complete with tests - here goes(read license.txt before use and make sure you don't point the tests at a production database as they will drop some tables/delete rows in the database - check notice at the top of tests.php before you run them, also requires php 5.1 or better. A copy of marcus simpletest lib has to be placed in the UnitTests directory to make the tests run - check comments in UnitTests/tests.php)
    Attached Files Attached Files


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •