Page 1 of 2

My take at an ORM for PHP

Posted: Sun May 04, 2008 12:08 am
by andre_c
I am currently working on an object-to-relational mapping tool for php and was hoping I could get some feedback on the approach and the API. I know I have to clean up the code a little but I'm hoping I'm in the right direction when it comes to the *aproach*. Please take a look at some of the documentation and tell me what you think. Even better if you can take the time to download it and try it.

http://outlet.knowledgehead.com

If you're wondering: Why another ORM tool, when we have Propel, Doctrine, EZPDO, etc? Here are some of my thoughts:
http://outlet.knowledgehead.com/wiki/in ... Motivation

Thanks,
Alvaro

Re: My take at an ORM for PHP

Posted: Sun May 04, 2008 12:24 pm
by Christopher
I did not have time to check out the code and you have no source viewer, so I can't really comment on the code. I like the idea.

Re: My take at an ORM for PHP

Posted: Tue Jun 17, 2008 7:14 pm
by volomike
andre_c wrote:I am currently working on an object-to-relational mapping tool for php and was hoping I could get some feedback on the approach and the API. I know I have to clean up the code a little but I'm hoping I'm in the right direction when it comes to the *aproach*. Please take a look at some of the documentation and tell me what you think. Even better if you can take the time to download it and try it.

http://outlet.knowledgehead.com

If you're wondering: Why another ORM tool, when we have Propel, Doctrine, EZPDO, etc? Here are some of my thoughts:
http://outlet.knowledgehead.com/wiki/in ... Motivation

Thanks,
Alvaro
I know this is a bump, Alvaro, because I'm bringing this back up again, but I was doing a query in Search here at devnetwork.net and I stumbled upon your little gem called Outlet. Man this thing is sweet. Can you share with me a todo list so that I can see what's still not implemented yet?

Why do I like it? Well, Propel is a lot of code and you have to run a generator with it. But with your tool, you just build your simple object classes painlessly, your get/set methods only for foreign keys, and then it's the way you call Outlet that does the rest of the magic on those simple classes. Simplicity is beautiful, Alvaro.

Excellent work.

Re: My take at an ORM for PHP

Posted: Wed Jun 18, 2008 1:40 am
by matthijs
On first impression, looks interesting. Have downloaded the code and checked out the project site, which looks good (clean and informative).

I did wonder, do you have your own site or weblog were I can read more about the person behind the project (you)? I always like to know who's behind a project and read a bit about the behind the scenes of a project etc.
(hope it's not alvarocarrasco.com, powered by microsoft live office asp, if so please replace with some open source php cms/blogging tool ;) That's a joke of course, but with a serious undertone..)

Re: My take at an ORM for PHP

Posted: Wed Jun 18, 2008 2:45 am
by volomike
Here's my review of Outlet ORM. I have it working and I _love_ it. It's the simplest ORM tool out there that I've seen, is lean, and runs fast. How could we not notice this?

1. I couldn't get your Outlet::init() function to work unless I fed it:

include('outlet-config.php')

..as in:

Outlet::init(include('outlet-config.php'));

2. The 'docroot' thing from the Quick Start Guide was confusing to me. My directory structure was:

/var/www/myapp/lib/outlet <-- your outlet class files, stuck under lib so that I can stick other libraries there too, such as Smarty and whatever

/var/www/myapp/orm <-- my Member and Address classes (my custom ORM)

/var/www/myapp <-- my index.php page and 'outlet-config.php' page is here

So, I reach it with http://127.0.0.1/myapp/

However, I had to change your init code like so:

define('APPROOT', dirname(__FILE__).'/');
set_include_path(APPROOT.'/lib/outlet:' . get_include_path());
require_once('Outlet.php');
require_once('orm/Member.php');
require_once('orm/Address.php');
Outlet::init(include('outlet-config.php'));
$outlet = Outlet::getInstance();
$outlet->createProxies();

3. I had to install docbook-utils and then run docbook2pdf on your outlet.docbook file to generate a PDF to read the additional documentation.

4. I had to add a 'dialect' of 'mysql' to my outlet-config.php file beneath the dsn parameter:

'dialect' => 'mysql',

5. Do you have a 'todo' list for this project so that I can learn what doesn't work yet, or that you might be changing/improving soon?

6. If you have an entity class that ends in -s already, it creates an odd situation where it doesn't use -es instead as the suffix, requiring me to have a couple odd class methods in my Address class that looked like:

public function getAddresss() { //misspelled due to odd thing about Outlet
return $this->_Addresses;
}

public function setAddresss(array $Addresses) { // misspelled due to odd thing about Outlet
$this->_Addresses = $Addresses;
}

7. Perhaps you could consider an alternate way of doing the outlet-config.php file, such as allowing me to do it .conf style like so:

connection.dsn = blah blah
connection.user = blah blah
connection.password = blah blah

classes.Member.table = members
classes.Member.props.ID = id, int, pk:true, autoIncrement:true

...and so on

8. In my index.php test, I still had to require my Member and Address entities that I needed:

require_once('orm/Member.php');
require_once('orm/Address.php');

...but the docs didn't mention that.

9. It might help to create a Bash script that one runs at command line. It could ask for the database name, username, and password. It would build an outlet-config.php file automatically. It could infer foreign key relationships, but on all associations it would clearly state that this was a TODO area for the programmer and that it made a best guess based on field names.

For instance, if I have 'members' and 'addresses', and if 'addresses' contains 'member_id' or 'memberid', then you can be pretty certain that I have a foreign key association there in addresses, referencing back to members. The script could automatically detect this and try to build that in the outlet-config.php file automatically.

10. You state that when building outlet-config.php, you have varchars and ints. Although I completely understand what you're doing here and don't have a problem with it -- you may have newbie programmers who want to use 'date' or 'timestamp' rather than 'varchar', so your documentation may need to reflect why it's only 'varchar' and 'int', and which one they should use for certain datatypes.

Re: My take at an ORM for PHP

Posted: Wed Jun 18, 2008 10:04 am
by andre_c
Hi,
I'm glad you like it and thank you very much for your feedback.
I will be posting a roadmap page either today or tomorrow that will have a todo list. It seems that you checked out from the trunk and I've made a couple of changes to the way you configure outlet since I wrote the documentation. I will update the docs once I cut the 0.2 release.

Some answers:
- I am adding a way to specify the name of the method to fix the 'Addresss' issue either today or tomorrow as part of the 0.2 release.
- I would also like alternate configuration formats and possibly a gui. I'll talk about those features on the roadmap page.
- I am thinking about adding date and datetime possibly soon after the 0.2 release. It may be valuable for some simple validation.
- I'll write some stuff about myself in the about section when I get a chance. Sorry, no site or blog yet.

Thanks for the interest and feedback,
Alvaro

Re: My take at an ORM for PHP

Posted: Wed Jun 18, 2008 12:44 pm
by volomike
andre_c wrote: Thanks for the interest and feedback,
Alvaro
Hey, cool. You responded!

Good to see there's a real person behind this still. I'm on a project right now where we abandoned Zend Framework and I wasn't too pleased with the complexity of Propel. This Outlet ORM really fits the bill at least as a replacement for me for Propel.

I was slow to ORM at first. Before ORM, previously I did parameterized SQL statements with ^ for the parameter slots. I stuck it in a class file that served up all the SQL as public vars. I wrote a Pack() statement to stuff the variables in when I loaded the SQL statement into a var. However, the problems with this are that when you have a team of developers working on a project, a few guys start doing things their own way, or rewrite SQL statements their own way when an existing one already exists. Also, it takes time to write out all those SQL statements and think those things through, and ORM speeds up rapid application development. The drawback to ORM is speed, but on my current contract, they are opting for "improvability" and RAD over speed, and they say they've got very fast hardware, so they're scaling okay with that.

If I end up on a project where speed is very important, I may go native mysql API and avoid an ORM, and stick with parameterized SQL from a class file.

I was also slow to PDO at first. I had read that mysql API runs faster than mysqli and PDO, but now I hear that the only active development still going on with MySQL drivers in PHP is on PDO. Plus, many of the ORM tools are sticking with the PDO route as their primary focus. So, PDO is what it shall be for our future. I hear on #php IRC chat that PDO is about to get super fast with something new called mysqlnd.

Some more questions/comments for you:

1. What does outletgen.php do? I tried to call it at command line since it took an arg for the config file, but I couldn't get it to work. Is that beta?

2. I think it would be easy for you to do a Bash or PHP script that follows the 80/20 rule. It could interrogate a database, then build the outlet-config.php file automatically, including 80/20 rule assumptions on associations when it sees something like MemberID, memberid, member_id, Member_ID, member_ID, and so on, meaning a foreign key in some table that refers back to a members table. (Please also remember that if you have an addresses table, then a foreign key reference might be address_id, but you need to automatically tack on the -es on the end instead of -s in order to get the table name because the word already ends in -s. So, although you and I have discussed this before -- this script, too, will need to account for this.) One could then take the output from this and customize.

3. Another useful script could generate the default ORM classes like Member, Address, etc. We could then take those and customize after that script is run, or subclass and customize them as they recommend with Propel.

4. I just installed phpBB the other day. Man that was easy. I'd like to suggest that you install this for your Outlet ORM project so that we could interact with you there. You could even use it as a way to collect bugs if you create a Bugs & Problems forum in it. And if this thing takes off like I think it will, then you might even find that a #outletorm IRC chat room would do people a lot of good, although I don't know how to get an IRC chat room started. (Another route is to download a free jQuery + AJAX chatter and stick it on the site for live chat.)

5. Take a look at this directory structure, and how I made it easier to get started with your tool:

/var/www/myproject/lib/outlet <-- where I stuck your classes
/var/www/myproject/orm <-- where I stuck my ORM classes that I built like Member, Address, etc.
/var/www/myproject <-- where I stuck outlet-config.php, index.php, and something new I wrote called OutletMap.php.

In my OutletMap.php file, it looks like so:

Code: Select all

require_once('lib/outlet/Outlet.php');
Outlet::init(require_once('outlet-config.php'));
$outlet = Outlet::getInstance();
$outlet->createProxies();
In my sample test index.php, I start the whole thing like so:

Code: Select all

require_once('orm/Member.php');
require_once('orm/Address.php');
require_once('OutletMap.php');
 
And then I can get straight to work with:

Code: Select all

$Member = new Member();
$Address = new Address();
//do the sets here like $Member->FirstName = 'blah' and $Address->Street = 'blah'
$Member->addAddress($Address);
$outlet->save($Member);
See how that's a piece of cake compared to the way you had in your Quick Start?

6. The way I hear it, use of 'require' is not preferred anymore. Use 'require_once'. If someone tells me I'm wrong on this, I'll listen, but that's just what I read.

Re: My take at an ORM for PHP

Posted: Thu Jun 19, 2008 5:06 am
by andre_c
I just refactored some of the config code and I added the 'plural' option:

Code: Select all

 
...
        // you can specify the plural at the entity mapping
        'Address' => array(
            'table' => 'addresses',
            'plural' => 'Addresses',
            'props' => array(
                'ID'        => array('id', 'int', array('pk'=>true, 'autoIncrement'=>true)),
                'UserID'    => array('user_id', 'int'),
                'Street'    => array('street', 'varchar'),
                ...
            )
        ),
...
        // or if you have a relationship with a name other than the foreign entity name, you can specify the plural there:
        array('one-to-many', 'Address', array('key'=>'UserID', 'name'=>'WorkAddress', 'plural'=>'WorkAddresses'))
...
 
This is now available in the trunk and it will released as part of the 0.2 release. You can see an example of this in the unit tests.

Some answers:

1. What does outletgen.php do? Currently, the $outlet->createProxies() call uses eval (one call) to create the necessary proxies. I actually haven't noticed any performance issues with using eval in this way, but if you really need to squeeze every last drop, you can use outletgen output all of the proxies into a file and then include the file. That should allow the byte-code cache (if you're using one) to improve performance. Again, I haven't needed to do this, but it is available just in case.

2. About config generation... I already have some code written for a web-based config generator that lets you reverse a database and customize from there. It will be a few weeks before I can finish it though. It will read the foreign key constraints in the database and it will make a best guess for the associations.

3. About a tool to generate entities... The same tool from last question will be able to generate the basic entities and relationship methods.

4. I agree that it would be nice to have a discussion place. I still haven't decided between a mailing list and a forum... I'll put something up when I get a chance.

5. I set up the directory as it appears on the quick start guide because I prefer to not have my library code inside of the document root. I can see how your set up might be easier to understand though... so maybe I'll change it.

6. I prefer to use "require"s if I know for sure that I won't be trying to include that file again. I've heard that op-code caches don't like "require_once", I don't know if that's still the case.

I'll try to get the roadmap page up soon.

Alvaro

Re: My take at an ORM for PHP

Posted: Thu Jun 19, 2008 1:03 pm
by volomike
So far so good. I didn't use the download link, but went to the svn checkout technique, and I managed to get the Addresses plurality to work. I had to change outlet-config.php with the plurality line like you mentioned.

Oh, and another thing for your docs -- if you have a 1:1 association, then just consider it a 1:many in your framework because it will work just fine. And in reverse, a many:1.

Re: My take at an ORM for PHP

Posted: Thu Jun 19, 2008 1:27 pm
by volomike
I think I'd like to see a generator script that creates, by an 80/20 rule, my outlet-config.php and my ORM classes. When it does this, it needs to spit out a message at the end (as well as leave comments inside) where it made 80/20 rule assumptions on the column types and foreign key associations it perceived from the table, and warn the user that they should look over the output to ensure it is correct.

So, if I have a Members table and an Addresses table, and my Address table has a column inside called member_id, memberid, Member_ID, MemberID, or even MeMbEr_Id (you get the point), then it assumes -- aha! -- a foreign key connection between Addresses and Members. And it should automatically handle plurality by guesses. So, for instance, if the script sees address_id, then it should see that this already ends in -s ,so it should first attempt to find a table of addresses (with -es), and, if not finding that, then look for a table of addresss (with -s). I know that looks irrational, but that's the best approach in my opinion. Still another improvement would be that it looks for a member table, and, not finding that, looks for members, and, not finding that, looks for memberes (again, a little odd, but works). (Someone reading this without actually knowing the logic in Outlet ORM might think I'm a complete nut for this logic, but once you see Outlet ORM, and use a table with -s already on the end, you'll know what I mean.)

Re: My take at an ORM for PHP

Posted: Wed Jun 25, 2008 11:01 pm
by andre_c
I just released version 0.2 with the new code from the trunk. Whoo hoo!! :D
The documentation has also been updated to reflect the new changes.
I also removed the warning from the main page. I've been using it on all of my new projects and it seems to be pretty stable by now.

Thanks for your suggestions. Keep them coming. I'll be working on the config generator during this week I'll keep you posted on my progress.

Re: My take at an ORM for PHP

Posted: Thu Jun 26, 2008 10:40 am
by volomike
Just downloaded it. Thanks for the update.

Re: My take at an ORM for PHP

Posted: Thu Jun 26, 2008 11:11 am
by volomike
My opinion? The docs could stand some tweaks. Here's my latest review:

http://www.outlet-orm.org/wiki/index.ph ... tart_Guide

* I don't understand why I need a subdirectory called docroot containing css, images, index.php. Are you trying to say that I would have my virtual directory, and then a parent directory to that where the libraries are stored?

* Relationships: what about explaining what to do in the case of the one to one relationship?

* outlet-config.php. If there was a way to have an alternative version of this in INI or XML format, and then it gets parsed and cached in shared memory unless the file's mod date changes, that might be easier on its usage? Right now it has a kind of JSON-like format and it's easy to make a typo, frustrating a newbie user, limiting its acceptance.

* Explain to newbies why init, getInstance, and createProxies are important.

http://www.outlet-orm.org/manual/

* You'll want to include an explanation about the plurality fix one needs to use in their outlet-config.php.

* If table or column names are odd, like they include a dash inside or a reserved word, then you'll want to ensure these get the backtick operator used in your libraries for this, such as `account-records` instead of account-records. You might already be doing something like that -- but want to double check.


Anyway, what's interesting here, and that most people may not realize, is that your 0.2 release is very stable, extremely well-written, and is NOT feature-rich for a reason. At 44K, you can't go wrong to use Outlet ORM instead of any other ORM. And if any programmer wants something more that's not delivered in the ORM, then fine -- go straight PDO SQL for just that need.

However, seriously, please consider the 1:1 relationship because that sometimes occurs in projects. For now I hacked it with 1:many.

Perhaps an advanced topic for your manual could be the idea of subclassing your Outlet object to give it extra functionality?

Re: My take at an ORM for PHP

Posted: Thu Jun 26, 2008 11:29 am
by Kieran Huggins
This looks very interesting... something I've been wanting to see for PHP for a while now. Keep it up!

Re: My take at an ORM for PHP

Posted: Mon Jun 30, 2008 3:27 pm
by volomike
How are dates stored in this ORM? You'll probably want to use the new PHP5 way to work with dates -- using DateTime() object. Also, on everything I do, I store things in GMT time and then I translate to the end user's timezone based on their browser's GMT +/- as it is announced to me when they hit my sites. In some cases I may permit the user to change their GMT setting -- depending on the type of website.