Rails Principles in PHP

Posted 27 September 2006 under , ,

I was recently contracted to develop a CRUD web application for a local non-profit. After spending the last few months developing with Ruby on Rails at work, imagine my dismay when the project specifications called for the site to be written in PHP. I looked at a few PHP-based Rails knockoffs — CakePHP and PHP on Trax (/gag) — but I couldn’t see myself using either of them to create a production web application. Both contain a lot of good ideas, but for all of the similarities between these frameworks and Rails, what really stood out for me were the differences. To paraphrase one Digg member, “One of the best features of Ruby on Rails is Ruby.”

However, as development on the project has proceeded, I’ve found myself pulling various Rails principles into the PHP implementation, with (imo) positive results. Even the file structure of the application resembles that of a Rails app, with directories for models, views, and presentation resources (stylesheets, scripts, images). What follows are some of these concepts. As a note: “Rails principles” is something of a misnomer; it’s probably more accurate to call most of them simply good principles.

MVC (Model-View-Controller)

From Wikipedia:

Model-view-controller (MVC) is a software architecture that separates an application’s data model, user interface, and control logic into three distinct components so that modifications to one component can be made with minimal impact to the others.

A typical PHP application is made up of a series of pages, each of which performs multiple tasks. A page might receive input from a form, validate it, insert it into a database, and then display the results, pulling in a header and footer from external files. As you can imagine, this leads to a lot of inflexibility and redundant code. What if the user submits bad data? What if the database schema changes?

Following the MVC pattern, data and logic are isolated from the user interface. All requests go through a central controller, in this case, index.php. The controller receives requests and their associated parameters, determines what actions to perform, and then works with the models to execute them. Each model, representing an object stored in the database, has methods for creating, updating, and deleting records, as well as formatting and validating data and reporting errors. Model classes also have static methods for finding an object by its ID number and getting a list of all instances of that class.

Depending on the success or failure of the intended action, the controller then determines what view to render, and with what information. If the user intended to create a new record, but failed to fill in a required field, the controller sends the user’s input, along with a friendly reminder, to the record creation page. Once the user makes the correction, and the record is stored in the database, he will be sent to the new record’s page with a message indicating his success. Such flexibility would be difficult to accomplish in a more page-oriented architecture.

Code reuse

Like Ruby on Rails, each model class extends a generic Model class that contains functions for the storage and retrieval of database records. This way, when I want to create a new stored object, I only have to create the new class, point it to the corresponding database table, and create custom methods for data formatting and validation. Additionally, a lot of views can be reused — if the data is the same, there’s no sense in storing two forms for creating and updating records when one will suffice.

Flash notices

A great feature of Rails is the flash[:notice] variable, which can be set to display a message the next time a view is rendered. I’ve found such functionality invaluable for describing the results of an action to the user in meaningful way, and thus I’ve duplicated it for my current project. Here are two such messages, for when a user attempts to make an invalid update …

And a valid one …

Here’s the rub

One major stumbling block I’ve encountered has to do with the way PHP handles inheritance of static class methods. Consider the following Ruby class definitions:

class Car
  def Car.showClass
    puts name
  end
end

class Hotrod < Car
end

And the equivalent in PHP:

class Car {
  static function showClass() {
    echo get_class();
  }
}

class Hotrod extends Car { }

Calling the Ruby functions gives the expected results:

> Car.showClass
Car
> Hotrod.showClass
Hotrod

But in PHP

> Car::showClass()
Car
> Hotrod::showClass()
Car

¡Qué mala suerte! RoR’s find() function, pretty much called whenever you want to do anything, depends on classes having some sense of self-awareness. This doesn’t translate so well into PHP, where it seems that classes, like half of my private school classmates, can only tell you who their parents are. I’ve developed a workaround, but it’s not as pretty as I’d hoped.

Further reading

In case I haven’t convinced you I’m the biggest geek in the world:

There’s a lot to love about PHP; it has transparency, stability, and hosting support that Ruby on Rails, in its current iteration, can’t match. Add in some of the standout features of the Rails framework, though, and the result is a much stronger (cleaner, more organized) final product.

Comments
  1. Can you show me the workaround for php?

    Thanks!

    — Daan · May 7, 05:26 AM · #
  2. Daan: Basically, the Model superclass has a find() function which takes three arguments: id, model name, and database table. The child classes each have their own find() functions, which take only one argument (id), but then call Model.find with static values filled in for their model names and database tables. In other words:

    abstract class Model {
        static function find($id, $model, $db_table) {
            // find the object
            return new $model(...);
        }
    }
    
    class Person extends Model {
        static function find($id) {
            return parent::find($id, “Person”, “people”);
        }
    }
    

    It’s hardly ideal to have to write one of these functions for every model, but at least we keep all of the heavy lifting in the Model superclass. An interesting discussion on this subject can be found at the PHP: get_class manual page.

    David · May 9, 05:34 AM · #
  3. Hi,

    can you explain too how the self.methods in RoR gets applied in PHP? ex:

    class Product < ActiveRecord::Base

    def self.find_products_for_sale find(:all, :order => “title”) end end

    How would this translate to PHP?

    — Franz · Nov 16, 09:03 PM · #
  4. Franz: If I’m understanding your question correctly, “self” is the Ruby syntax for declaring static methods, meaning methods that can be called without instantiating the class. PHP uses the “static” keyword, like in the car example above. Hope that helps.

    David · Nov 17, 05:39 PM · #
Leave a Comment (please be nice/human)
Name
Email
http://
Message
Textile Help


About David

pic of david David Eisinger is a Web Programmer in lovely Durham, North Carolina, specializing in Ruby on Rails and Javascript. Take a look at (most of) his online activities, or feel free to contact him.

Status (via Twitter)

Recent Photos (via Flickr)