A Practical Example: PHP vs. RoR
I had a situation come up yesterday that, I think, illustrates what I like about Ruby on Rails particularly splendidly.
The Project: Revamp of a client's Web site. The design is actually coming from another designer, I'm just implementing and handling some server-side behaviors.
The Request: (paraphrasing) "We have this 'Contact Us' page. At the bottom, we wrote 'Be Notified' and there's a form. We want people to give us their email addresses, and we'll notify them when we update our product. Except, we don't know what we want to do with that information, once we have it. What should we do?"
So, after a bit of brainstorming, the semi-obvious answer is: Put it in a database. Most of the site is already running on a database, in theory, it's a fairly trivial option to set this form up to feed into a database.
I said in theory.
In PHP
Approximately, here's what you have to do, to make this work in PHP — No, wait. This is what I would have to do, to make this work in PHP (I said I wanted this to be a practical example, right?)
I'm building the main site in Dreamweaver, but I can build the save-to-database part in PHP — the form will call a script that packages up the POSTed data and saves it to MySQL. I already have a database-abstraction object I use for this sort of thing; so I can type $db->query('insert some SQL thing') instead of mysql_query('insert some SQL thing', $pointer). But, still, I have to write a good deal of code:
$db = new db_object('host', 'user', 'pw');
$sql = "insert into signups set first_name = '{$_POST['first_name']}', last_name = '{$_POST['last_name']}', email = '{$_POST['email']}', state = '{$_POST['state']}', date = FROM_UNIXTIME(".mktime().")";
$test = $db->query($sql);
if ($test) header("Location /redirect/to/thank-you.html");
That's not even terribly smart. They could put an injection attack into the form, and I'm not really watching for that. So, I should add something like:
foreach ($_POST as i=>$val) { $_POST[$i] = clean_up_for_sql ($val); }
And there are probably some required fields. The client wants to collect email addresses, mostly, so that's a required field. The easy way out would be:
if (!isset($_POST['email']) || empty($_POST['email'])) exit ('You left email address blank. This is a required field. Please use the Back button on your browser to go back and correct, then re-submit the form.');
Except, obviously, this is somewhat bass-ackwards: It's rude, it's bad practice, and I don't want to inflict that on my client, or their customers. So, what I really need to do is:
if (!isset($_POST['email']) || empty($_POST['email']))
{
session_start();
$_SESSION['errorfields']['email'] = TRUE;
header ("Location: /back/to/form");
}
And this is going to get pretty weak if we have more required fields in the future, so maybe this is smarter:
$required = array('email');
foreach ($required as $require_this)
{
if (!isset($_POST[$require_this] || empty($_POST[$require_this]))
{
session_start();
$_SESSION['errorfields'][$require_this] = TRUE;
header ("Location: /back/to/form");
}
}
Of course, the other half of this is that I need to go back into Dreamweaver and edit the form so that it a) preserves the state of what the Customer entered originally, but also b) presents the information about which fields are incomplete. And, since I've already elaborated on the required fields, let's set it up so that we could have an error on any field, not just email.
Here's how I would do it:
<?php
session_start();
$errorfields = $_SESSION['errorfields'];
?>
[...]
<?php if (!empty($errorfields)) {
echo '<p id="errors_box">There appear to be errors in the form. Please make sure to fill in all required fields.</p>';
} ?>
<form action="/save_to_database.php" method="POST" name="notify_form">
<div class="row<?php if (isset($errorfields['first_name'])) echo ' errorfield'; ?>"><span class="label">First name: </span><span class="input"><input type="text" name="first_name" value="<?php echo ( (isset($_POST['first_name'])) ? $_POST['first_name'] : '' ); ?>"> </input></span> </div>
<div class="row<?php if (isset($errorfields['last_name'])) echo ' errorfield'; ?>"><span class="label">First name: </span><span class="input"><input type="text" name="last_name" value="<?php echo ( (isset($_POST['last_name'])) ? $_POST['last_name'] : '' ); ?>"> </input></span> </div>
<div class="row<?php if (isset($errorfields['email'])) echo ' errorfield'; ?>"><span class="label">First name: </span><span class="input"><input type="text" name="email" value="<?php echo ( (isset($_POST['email'])) ? $_POST['email'] : '' ); ?>"> </input></span> </div>
<div class="row<?php if (isset($errorfields['state'])) echo ' errorfield'; ?>"><span class="label">First name: </span><span class="input"><input type="text" name="state" value="<?php echo ( (isset($_POST['state'])) ? $_POST['state'] : '' ); ?>"> </input></span> </div>
</form>
(If the unusual CSS is throwing you off, it comes from this excellent primer on CSS form design.)
So, Yay! Huzzah! etc. I think I've got it.
Err. Except — the client comes back and says, "Yes, this is cool. But, we decided we don't need the first name and last name separate; we're just going to collect 'name'. Oh, and we think that should be required also."
Look at how many places I need to update to make this change. Oy!
Better with Rails
So, let's see what this scenario looks like in Rails, eh?
The first thing is to generate the model, so type this at the command line:
ruby script/generate model signup
When rails is done doing it's thing, open app/models/signup.rb and add this line:
validates_presence_of :email
Back to the command line, type:
ruby script/generate controller signup
When rails is done with that, open app/controllers/signup_controller.rb and type in:
def create
@signup = Signup.new(params[:signup])
if @signup.save
flash[:notice] = 'Thank you for signing up to receive product updates.'
redirect_to :action => 'contact'
else
render :action => 'contact'
end
end
(You could also use scaffolding to create most of this, but I thought I'd be somewhat fair to PHP.)
And here's what the form will look like (though, honestly, if you're using Rails, there's a very good chance that the only new material is the very first line):
<%= error_messages_for signup %>
<%= start_form_tag :action => 'create' %>
<div class="row"><span class="label">First Name:</span> <span class="input">
<%= text_field 'signup', 'first_name' %></span> </div>
<div class="row"><span class="label">Last Name:</span> <span class="input">
<%= text_field 'signup', 'last_name' %></span> </div>
<div class="row"><span class="label">State:</span> <span class="input">
<%= text_field 'signup', 'state' %></span> </div>
<div class="row"><span class="label">Email Address :</span> <span class="input">
<%= text_field 'signup', 'email' %></span> </div>
<%= end_form_tag %>
And... that's it. Done.
When the client changes their mind, and wants those fields changed, just change the database and reload the model. That doesn't requiring much editing, just change the form a little, and your done.
Want to store the data somewhere else? Switch one line (@signups = API.new) and you're done.
Why I Like Rails
The demos of Rails tends to focus on speed: "Rails is fast... Autogenerated code... Oooh, look how easy!"
Which is fine, but...
I, at least, and I don't think I'm alone here, quickly realized that scaffolding can't do it all. That, ultimately, you'd have to rip a lot of it out and start over. And then I started thinking, "Gee, if that's all Rails has to offer... Why bother?"
But that's not all. What's more important, at least, in my experience, is the speed at which Rails can adapt to new concepts. Designers freqently find themselves in a love-hate bind with their clients (Google "client from hell"). "The client doesn't know what they want", "the client keeps changing their mind", and so on.
Rails provides a light-weight, low-overhead approach to Web development. And — my favorite part — a file organization set-up that just makes sense. All of this works to make my life easier - I can actually respond to those "crazy clients" as fast as they change their minds (well, almost as fast, anyway), and without watching my code turn into a mess.
I'm working on a reorganization of this site (my_stupid_blog, and other utilities here) in Rails, and while there's still the occasion "head banging session", overall, I'm enjoying it far more than any work I've done with PHP in recent months.
Permalink • Posted in: web, tech stuff, ruby on rails, php • Post a comment
Loading preview...