What 6 months of PHP/Laravel taught me as a Ruby/Rails developer

“Good programmers use their brains, but good guidelines save us having to think out every case.”
— Francis Glassborow

After many years working with Ruby on Rails, I’ve spent the last six months working full time with PHP and Laravel. What surprised me the most was not learning a new syntax, but how little friction I felt switching stacks.

Not because the languages are the same (they aren’t) but because Rails and Laravel share a very similar philosophy: convention over configuration, developer happiness, and code that reads like intention.

Below are the main similarities and differences I noticed from a very practical, day-to-day perspective.

Eloquent Models feel like ActiveRecord (and that’s a good thing)

If you’ve used Rails’ ActiveRecord for years, Eloquent will feel instantly familiar.

Model definition

Rails (ActiveRecord):

class Post < ApplicationRecord
  belongs_to :user
  has_many :comments

  scope :published, -> { where(published: true) }
end

Laravel (Eloquent):

class Post extends Model
{
    protected $fillable = ['title', 'body', 'published'];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }

    public function scopePublished($query)
    {
        return $query->where('published', true);
    }
}

Querying feels almost identical

This is where Rails muscle memory really kicks in.

Rails:

Post.published
    .where(user_id: 1)
    .order(created_at: :desc)
    .limit(10)

Laravel:

Post::published()
    ->where('user_id', 1)
    ->orderBy('created_at', 'desc')
    ->limit(10)
    ->get();

Chaining, scopes, lazy evaluation - the flow and intent are basically the same.

Collections API: Ruby vibes everywhere

Laravel’s Collection API is one of its strongest features — and clearly inspired by Ruby’s Enumerable.

Ruby:

users
  .select { |u| u.active? }
  .map(&:email)
  .sort

Laravel:

$users
    ->filter(fn ($u) => $u->active)
    ->map(fn ($u) => $u->email)
    ->sort()
    ->values();

Once you stop thinking in raw arrays and start thinking in collections, PHP becomes much more expressive.

Hash vs Array: different names, same idea

This is one of the first mental adjustments when moving from Ruby to PHP.

In Ruby, Hash is the natural structure for key-value data. In PHP, arrays do everything - lists, maps, hashes - which feels odd at first but works well in practice.

Defining key-value data

Ruby (Hash):

user = {
  name: "User",
  email: "[email protected]",
  active: true
}

PHP (array as hash):

$user = [
    'name' => 'User',
    'email' => '[email protected]',
    'active' => true,
];

Accessing elements

Ruby:

user[:email]
user.fetch(:email)

PHP:

$user['email'];
$user['email'] ?? null;

The null-coalescing operator makes safe access very straightforward.

Adding or updating values

Ruby:

user[:role] = "admin"
user.merge!(last_login: Time.now)

PHP:

$user['role'] = 'admin';

$user = array_merge($user, [
    'last_login' => now(),
]);

With Laravel Collections:

$user = collect($user)
    ->merge(['last_login' => now()])
    ->toArray();

Transforming data

Ruby:

prices = { apple: 10, banana: 5 }
prices.transform_values { |v| v * 2 }

PHP (Collection):

$prices = collect(['apple' => 10, 'banana' => 5])
    ->map(fn ($v) => $v * 2)
    ->toArray();

Nested access

Ruby:

user.dig(:profile, :address, :city)

PHP (Laravel helper):

data_get($user, 'profile.address.city');

Different syntax, same intent.

Request validation is as simple as Rails strong params

Validation in Laravel feels very close to Rails - simple, readable, and explicit.

Rails:

params.require(:user).permit(:name, :email)
validates :email, presence: true, uniqueness: true

Laravel:

$request->validate([
    'name'  => 'required|string',
    'email' => 'required|email|unique:users',
]);

In both frameworks, validation lives close to where data enters the system.

Routes: familiar DSL, different syntax

Routing is another area where the philosophies strongly align.

Rails:

Rails.application.routes.draw do
  resources :posts
  get "/health", to: "health#check"
end

Laravel:

Route::resource('posts', PostController::class);
Route::get('/health', [HealthController::class, 'check']);

RESTful by default, expressive, and easy to reason about.

Artisan feels like Rails generators on steroids

If you like rails generate, you’ll feel at home with Artisan.

Rails:

rails generate model Post title:string body:text
rails routes

Laravel:

php artisan make:model Post -m
php artisan make:controller PostController --resource
php artisan route:list

Artisan goes beyond generators:

  • queue workers
  • cache management
  • app optimization
  • custom CLI commands

It’s a very strong part of the Laravel ecosystem.

PHP is simple, explicit, and predictable

Modern PHP is very different from what many of us remember.

Less magic, more clarity

Ruby:

user.find_by_email("[email protected]")

PHP:

User::where('email', '[email protected]')->first();

No hidden meta-programming - what you see is what runs.

Interfaces and typing help a lot

interface PaymentGateway
{
    public function charge(int $amount): bool;
}

Clear contracts reduce ambiguity, especially in larger teams.

The not-so-great parts

No stack is perfect.

  • Laravel caching can be annoying and inconsistent across environments
  • Finding libraries for newer or niche services can be harder than in Ruby
  • PHP still requires more boilerplate in some areas
  • The ecosystem is more fragmented (versions, extensions, hosting)

None of these are deal breakers - just trade-offs.

Final thoughts

After six months working daily with Laravel, one thing is very clear to me:

PHP is not dead. Not even close.

Laravel delivers:

  • a modern developer experience
  • a familiar MVC philosophy
  • excellent productivity
  • and a surprisingly smooth transition for Rails developers

Rails still shines in elegance and ecosystem maturity, but Laravel proves that PHP has evolved - a lot - and in the right direction.

At the end of the day, good software is less about the language and more about:

  • clear code
  • good tests
  • solid architecture
  • and teams that know what they’re doing

And in that sense, both Rails and Laravel get the job done - really well.

Lastest Posts