Setters & Getters in Ruby vs PHP
Alright, now we’re getting into some real differences between Ruby and PHP.
PHP’s Take on Setters & Getters (the Classical OOP Method)
If you’ve ever the pleasure of using Symfony’s Doctrine as an ORM, you’ll be super familiar with the concept of setters & getters. The origin of this pattern goes way back to the Java days.
The idea is that the underlying property of an object is accessible, but the method to set a new value is controlled.
Let’s start with a contrived example:
Now we can instantiate a new User
and get their first name:
Now let’s add a Setter so that we can actually change the user’s name at runtime:
Woo! Now we can set the User’s first name during registration:
Now you’re probably wondering, why don’t we just make the User->first_name
property public? That way we can set it with $user->first_name = 'Amy'
and skip all of that Setter/Getter nonsense.
Well you could do it that way, but let’s say we need to perform a transformation on that data. For example, what happens if our user makes a mistake and forgets to capatilize their own name during registration?
Uh oh. Now throughout our application we’ll call our awesome user amy
instead of Amy
.
Here’s where a setter can save us:
We’ve added the ucfirst($string)
function to our setFirstName
method. That way all input is transformed to have the first word to be capatialized. In this case from amy
to Amy
automagically:
Those familiar with Laravel’s Eloquent ORM will be scratching their heads at this example. Under the hood Eloquent is doing something similar by storing the model’s data as an array under $_attributes
and using some clever PHP magic methods to check for specially named setter methods.
It’s a little complex to show here. The classic Getter/Setter method is much easier to understand.
Ruby’s Setters/Getters
You may have noticed to get that functionality we had to do quite a bit of typing. That’s normal for statically defined languages like PHP and Java. It’s verbose and kind of ceremonious.
Ruby offers a special shortcut to create setters and getters for your classes. Let’s start with the way we’d normally create a new class based on what we know about static languages:
# A contrived User example
#
# @author Dylan Pierce <me@dylanjpierce.com>
class User
@first_name = 'Dylan'
def first_name
return @first_name
end
end
A couple of questions you might have right off the bat:
What the heck is this @
character doing in the variable @first_name
?
In Ruby, we have different levels of variables. The @
denotes that the variable is a class variable. A class variable is just a variable that is available anywhere within the class.
You can think of $this->first_name
and @first_name
as being logically equivalent. Both are variables that have special context in that they’re “shared” across all methods in the class.
Next question you might have is where’s the keyword get
in the getter method? Shouldn’t it be something like get_first_name
?
You could name your getter method that way, but you’ll come to see soon that there’s a reason for this.
Regardless if you have a solid grasp on these differences, let’s continue by actually using this new User
class:
user = User.new
puts user.first_name
=> 'Dylan'
Now, let’s craft a Setter method by hand in our User
Ruby Class:
# A contrived User example
#
# @author Dylan Pierce <me@dylanjpierce.com>
class User
@first_name = 'Dylan'
def first_name=(first_name)
@first_name = first_name
end
def first_name
return @first_name
end
end
Yup, you’re seeing that right. There’s a big ole =
right in the method name. Alongside of the fact it accepts an argument, it’s exactly the same name as our getter.
It’s threw me for a tiff for a long time. Don’t expect it to be or feel natural at first. But here’s the class in use just like we did in the PHP implementation:
user = User.new
user.first_name('Amy')
puts user.first_name
=> 'Amy'
Using Ruby Shorthand to Generate Setters and Getters
Now manually typing getters and setters for Ruby’s classes are lot less legwork (or should I say fingerwork) typing than it’s PHP cousin. But we have another special treat that’s built into the Ruby language.
It’s natural for Ruby to have shortcuts for building out OOP functionality since everything is an object.
Ruby offers 3 more specialized keywords in classes to generate getters, setters or both at the same time.
Let’s generate the setters and getters seperately. Now this will be logically equivalent to our User
class in former examples:
class User
attr_reader :first_name
attr_writer :first_name
end
So as you can probably guess, attr_reader
are our reading methods (a.k.a. getter methods). attr_writer
are our writing methods (a.k.a. setter).
user = User.new
user.first_name('Amy')
puts user.first_name
=> 'Amy'
But wait there’s more.
Not only do you have the shortcut methods of attr_reader
and attr_writer
but you have a third method that covers both: attr_accessor
.
This means we can reduce our User
class to 3 lines of code:
class User
attr_accessor :first_name
end
This will give us the same exactly functionality, but with a huge cost savings in terms of typing. Reducing this type of boilerplate is huge for your productivity and ultimately let’s you solve problems rather that typing or relying on a heavy IDE to constantly create boilerplate code.