PHP Classes
elePHPant
Icontem

PHP ORM Pros and Cons - Tiny PHP ORM Framework package blog

Recommend this page to a friend!
  All package blogs All package blogs   Tiny PHP ORM Framework Tiny PHP ORM Framework   Blog Tiny PHP ORM Framework package blog   RSS 1.0 feed RSS 2.0 feed   Blog PHP ORM Pros and Cons  
  Post a comment Post a comment   See comments See comments (17)   Trackbacks (0)  

Author: Victor Bolshov

Posted on:

Package: Tiny PHP ORM Framework

Object-Relational Mapping (aka ORM), as you may know, is an approach to manipulate information in relational databases as if they are objects.

There are many packages that implement different approaches to ORM, each as its own advantages and disadvantages.

Read this article to learn about what things are recommended or not when implementing an efficient ORM package in PHP.




Contents

Introduction

1. Keep data operations under control

2. Care about eternal values, not temporary

3. Use your tools to the full extent

Best Practices for ORM tools

1. Use thin models or entities

2. Never change auto-generated (scaffold) classes

3. Separate validation logic from the entities

Conclusion


Introduction

Is has been a long time since I was first introduced to the concepts of Object-Relational Mapping (ORM).

First time I took a deeper look at this subject was about 2004. Since then, my mind has changed, and my perception of ORM has changed either.

There was time when I thought that SQL can be practically eliminated from the application, and design patterns like Unit-of-Work echoed with warmth and joy inside my head. Well, this is no more.

For the last several years my feelings about ORM stay mostly unchanged, and as such, I want now to summarize my opinion in this post.

The following is mostly related to a typical PHP application that stores data in RDBMS. Such apps usually have a short-living request-response cycle.

The whole process of request-response usually takes 10 - 500 ms. The developer does not deal with multi-threaded environments, as opposed to Java. It does not mean there is no concurrency, of course, but that's a different issue: at least you don't have to worry that an object you're working with is being modified at the very same time in a neighboring thread.

The awareness of the short cycle of request-response has largely formed my attitude to ORM in PHP applications. In short: I like ORM but I like it simple. Below are a few points that I have set for myself.

1. Keep data operations under control

The more you rely on ORM, the less control you have over interacting with data. There's a lot of things happening behind the scene in modern ORM libraries. And that is what I dislike about those libraries.

ORM tools tend to give you a lot of "object-oriented" capabilities to retrieve data from DB. All those query languages and query objects... Damn, I'm good to go with plain old SQL! With all these tools I never know what the resulting SQL will be.

What if I want to tune the resulting SQL? Maybe I'd like to use MySQL's STRAIGHT_JOIN? With a custom query-object or a custom SQL-like query language, I doubt this is possible.

ORM libraries often return all results in form of objects. Related entities might produce even more objects in the result set. Here is an example from Doctrine documentation:

$query = $em->createQuery('SELECT u, a FROM ForumUser u JOIN u.avatar a');
$users = $query->getResult(); // array of ForumUser objects with the avatar association loaded
echo get_class($users[0]->getAvatar());

I have to admit, this is convenient. There's a catch, however. The fetch process involves 2 objects created per line. And what I need in my app is just an array with: user data + avatar data.

In many situations, all these objects can be an overkill. And another point about this example, imagine that two users may share a single avatar. Will $users[0]->getAvatar() return the very same object for both of them? Or will it create two diferent ones? These things can make your life complicated.

See also this article for Doctrine ORM performance traps.

In the end, I do not like the idea of learning a new "SQL which is not real SQL". Propel API: BookQuery::create()->filterByPublishedAt(...) ?? Oh, no. Please count me out.

That said, I do like the idea behind a Query-object. In my opinion, however, it only should give me the means to compose SQL from parts, dynamically add WHERE conditions, GROUP BY, LIMIT etc. An SQL Lego. And it is also important to be able to see the SQL query that the query object generates. An example of interface I would like:

$statement = (new Select("forum_user"))
    ->join("JOIN avatar ON (avatar.id = forum_user.avatar_id)")
    ->where("username LIKE CONCAT(?, '%')", "vasya.p")	
    ->execute();
$firstRow = $statement->fetch(FETCH_ASSOC);

This is not as clear as plain SQL but you can add WHERE conditions etc. Important when you have to compose SQL from a bunch of filters which a user can add/remove arbitrarily. However, for most cases, and especially when performance is key, I would go for writing SQL queries by hand.

2. Care about eternal values, not temporary

Yeah, this sounds pathetic ;-) By eternal values I mean the grounds on which your application stands: the programming language and the core technologies like the chosen RDBMS.

With ORM libraries sometimes you have study tons of stuff in order to use them properly. Specific @phpdoc annotations that the ORM uses to denote relations between tables, column types etc. Special query languages. Sophisticated APIs.

Remember that you've already spent time on studying SQL, and all those tools are there merely to compose SQL for you! If you have to spend too much time in the ORM docs, maybe that's a sign of a wrong choice of ORM? 

Some ORMs also offer their own means for creating/altering database schemata. And then, again, you're screwed: you have to learn some weird XML syntax that describes your entities and relations between them, or other stuff which in fact you already have. Dude, you better study your database to properly write CREATE TABLE!

Programming language and core technologies are a lesser subject to change than a particular library. The ORM is upgraded from 1.0 to 2.0 - and you have to learn new concepts, new syntax, sometimes new everything. And what about your knowledge about version 1.0? You're right. It's time to crumple your cerficate of a "Super-duper-ORM v1.0 engineer" and throw it to the wastebin.

To be clear: I don't mind learning new libraries, I don't mind learning them in depth. The thing is that some libraries are so specific, so niche, that you might not be able to apply your knowledge elsewhere. Core knowledge is almost always reusable. With a good knowledge of SQL, of the database design principles, you will quickly learn the concepts of any ORM that you'll have to work with.

And the reverse is not quite true. Remember, your next company/project is likely to be using a different ORM tool, not the one you you're struggling to master at the moment.

3. Use your tools to the full extent

Swiss Army Knife Wenger Opened https://zh.wikipedia.org/wiki/File:Swiss_Army_Knife_Wenger_Opened_20050627.jpg

Different databases have different features. Most of RDBMS support ANSI SQL but also offer a lot of specific extensions. ORM tools either  support the bare minimum of standard SQL or tend to become monstrous creatures supporting all the SQL dialects out there.

Remember: only bugs come at no cost (though some of them cost a lot later). The more complex the system is, the more potential for bugs it has.

You already have a database. If your ORM layer is not capable of utilizing some of its cool features - its a problem of the ORM. For example, you want to use different storage engines in MySQL but you create your tables using ORM as intermediate, and it won't let you choose a storage engine.

Some may say that ORM lets you switch to a different DB easily. But switching between DB is never a snap. It involves a lot of compatibility issues, different resources will be needed, you will need a different sysadmin DBA to set it up correctly. In the end, it's rarely worth the costs, from the business perspective.

Your ORM tool is there to help, not to stand in your way when you try to use the database of your choice at its best.

Best Practices for ORM tools

I have also put down a couple of things that come from my experience of using various ORM and ORM-like tools. Those may be seen as my personal best-practices that can apply to many of the tools out there.

1. Use thin models or entities

Fat Models can be attractive, but not in the case of ORMs.

Falla pintor y gorda https://commons.wikimedia.org/wiki/File:Falla_pintor_y_gorda.jpg

In fashion world, some of the not-so-slim models look nice. Speaking of models as a programming concept, this is a reasonably arguable question, either. Some prefer to have all the logic associated with an entity, in that entity's Model/Entity class.

OK, I've seen too many of those. Once it gets fat, it almost never loses weight. So I really want the entity classes to stay as slim as possible. I'm OK with getAuthors() method in a Book entity but Book->formatTitleForCustomerNewsletter() should just never be.

2. Never change auto-generated (scaffold) classes

A lot of ORM libraries offer you a scaffolding utility that can generate an Entity/Model class for a DB table for you. Leave those classes untouched. Many developers use those classes to add methods and/or properties, to enhance them with new features.

This way you actually lose the ability to regenerate the class when your DB entity is changed (column added/removed etc). A better approach is to extend the generated class and place your own code in your own class. You may also use a Decorator pattern for this.

3. Separate validation logic from the entities

Some prefer validation to be part of the entity, so that all the entity's data is validated upon insert or update. That might be useful but what I recommend is to use a separate class for validating a model's data.

This way you gain more flexibility: you can check data submitted via form without having to create an entity, and you can use the same validator to perform checks before insert or update.

Also, this approach helps to separate responsibilities. Two small classes are generally better that one large. Validation can also be easily unit-testable separately.

Conclusion

Some people say to not reinvent the wheel and use the existing solutions of others, but if other people solutions do not suite you, you may was well create yours.

Reinventing the wheel is fun, so I created my own set of ORM database tools. I created a package named TinyORM using my own principles of what it should be.

If you liked this article, or would like to discuss the principles that I described, as well my own TinyORM package, post a comment here and I will follow up.


You need to be a registered user or login to post a comment

1,393,072 PHP developers registered to the PHP Classes site.
Be One of Us!

Login Immediately with your account on:

FacebookGmail
HotmailStackOverflow
GitHubYahoo


Comments:

6. Totally Agree With You on Thin Models - A Ade (2016-06-09 01:19)
That's why I wrote a light-weight orm... - 1 reply
Read the whole comment and replies

5. more con than pro - David Mintz (2016-05-31 14:15)
maybe it's best for write operations and less useful for read... - 6 replies
Read the whole comment and replies

4. Great advice - Anthony Amolochitis (2016-05-31 13:37)
Great advice... - 1 reply
Read the whole comment and replies

3. Thin Models - Sean (2016-05-31 12:21)
Thin Models... - 2 replies
Read the whole comment and replies

2. Very good - Kiril Savchev (2016-05-31 09:20)
Very good... - 1 reply
Read the whole comment and replies

1. Stefano Azzolini - Stefano Azzolini (2016-05-31 06:12)
Great article.... - 0 replies
Read the whole comment and replies




  Post a comment Post a comment   See comments See comments (17)   Trackbacks (0)  
  All package blogs All package blogs   Tiny PHP ORM Framework Tiny PHP ORM Framework   Blog Tiny PHP ORM Framework package blog   RSS 1.0 feed RSS 2.0 feed   Blog PHP ORM Pros and Cons